+ initial import

This commit is contained in:
Taegus
2018-12-16 13:33:09 +01:00
commit 554f96387b
47 changed files with 14962 additions and 0 deletions

183
renderer/blockchain.js Normal file
View File

@@ -0,0 +1,183 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Blockchain {
constructor() {}
getBlock(blockToGet, includeData, clbError, clbSuccess) {
web3Local.eth.getBlock(blockToGet, includeData, function(error, block) {
if (error) {
clbError(error);
} else {
clbSuccess(block);
}
});
}
getAccounts(clbError, clbSuccess) {
web3Local.eth.getAccounts(function(err, res) {
if (err) {
clbError(err);
} else {
clbSuccess(res);
}
});
}
isAddress(address) {
return web3Local.utils.isAddress(address);
}
getTranasctionFee(fromAddress, toAddress, value, clbError, clbSuccess) {
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) {
if (error) {
clbError(error);
} else {
var amountToSend = web3Local.utils.toWei(value, "ether"); //convert to wei value
var RawTransaction = {
from: fromAddress,
to: toAddress,
value: amountToSend,
nonce: result
};
web3Local.eth.estimateGas(RawTransaction, function( error, result ) {
if (error) {
clbError(error);
} else {
var usedGas = result;
web3Local.eth.getGasPrice(function( error, result ) {
if (error) {
clbError(error);
} else {
clbSuccess(result * usedGas);
}
});
}
});
}
});
}
prepareTransaction(password, fromAddress, toAddress, value, clbError, clbSuccess) {
web3Local.eth.personal.unlockAccount(fromAddress, password, function( error, result ) {
if (error) {
clbError("Wrong password for the selected address!");
} else
{
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) {
if (error) {
clbError(error);
} else {
var amountToSend = web3Local.utils.toWei(value, "ether"); //convert to wei value
var RawTransaction = {
from: fromAddress,
to: toAddress,
value: amountToSend,
nonce: result
};
web3Local.eth.estimateGas(RawTransaction, function( error, result ) {
if (error) {
clbError(error);
} else {
RawTransaction.gas = result;
web3Local.eth.getGasPrice(function( error, result ) {
if (error) {
clbError(error);
} else {
RawTransaction.gasPrice = result;
web3Local.eth.signTransaction(RawTransaction, fromAddress, function( error, result ) {
if (error) {
clbError(error);
} else {
clbSuccess(result);
}
});
}
});
}
});
}
});
}
});
}
sendTransaction(rawTransaction, clbError, clbSuccess) {
web3Local.eth.sendSignedTransaction(rawTransaction, function( error, result ) {
if (error) {
clbError(error);
} else {
clbSuccess(result);
}
});
}
getAccountsData(clbError, clbSuccess) {
var rendererData = {};
rendererData.sumBalance = 0;
rendererData.addressData = [];
var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json');
var counter = 0;
web3Local.eth.getAccounts(function(err, res) {
if (err) {
clbError(err);
} else {
for (var i = 0; i < res.length; i++) {
var walletName = vsprintf("Account %d", [i + 1]);
if (wallets) {
walletName = wallets.names[res[i]] || walletName;
}
var addressInfo = {};
addressInfo.balance = 0;
addressInfo.address = res[i];
addressInfo.name = walletName;
rendererData.addressData.push(addressInfo);
}
if (rendererData.addressData.length > 0) {
updateBalance(counter);
} else {
clbSuccess(rendererData);
}
}
});
function updateBalance(index)
{
web3Local.eth.getBalance(rendererData.addressData[index].address, function(error, balance) {
rendererData.addressData[index].balance = parseFloat(web3Local.utils.fromWei(balance, 'ether')).toFixed(2);
rendererData.sumBalance = rendererData.sumBalance + parseFloat(web3Local.utils.fromWei(balance, 'ether'));
if (counter < rendererData.addressData.length - 1) {
counter++;
updateBalance(counter);
} else {
rendererData.sumBalance = parseFloat(rendererData.sumBalance).toFixed(2);
clbSuccess(rendererData);
}
});
}
}
createNewAccount(password, clbError, clbSuccess) {
web3Local.eth.personal.newAccount(password, function(error, account) {
if (error) {
clbError(error);
} else {
clbSuccess(account);
}
});
}
closeConnection() {
web3Local.currentProvider.connection.close();
}
}
// create new blockchain variable
EthoBlockchain = new Blockchain();

74
renderer/maingui.js Normal file
View File

@@ -0,0 +1,74 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class MainGUI {
constructor() {
this.appState = "account";
}
changeAppState(newState) {
this.appState = newState;
$(".sidebarIconWrapper").removeClass("iconSelected");
switch(this.appState) {
case "account":
$("#mainNavBtnWalletsWrapper").addClass("iconSelected");
break;
case "send":
$("#mainNavBtnSendWrapper").addClass("iconSelected");
break;
case "transactions":
$("#mainNavBtnTransactionsWrapper").addClass("iconSelected");
break;
case "settings":
$("#mainNavBtnSettingsWrapper").addClass("iconSelected");
break;
default: // do nothing for now
}
}
getAppState() {
return this.appState;
}
showGeneralError(errorText) {
$("#txtGeneralError").html(errorText);
// create and open the dialog
$("#dlgGeneralError").iziModal();
$('#dlgGeneralError').iziModal('open');
$("#btnGeneralErrorOK").click(function() {
$('#dlgGeneralError').iziModal('close');
});
}
renderTemplate(template, data) {
var template = Handlebars.compile(ipcRenderer.sendSync('getTemplateContent', template));
$("#mainContent").empty();
$("#mainContent").html(template(data));
}
}
$("#mainNavBtnTransactions").click(function() {
EthoMainGUI.changeAppState("transactions");
EthoTransactions.renderTransactions();
});
$("#mainNavBtnSend").click(function() {
EthoMainGUI.changeAppState("send");
EthoSend.renderSendState();
});
$("#mainNavBtnWallets").click(function() {
EthoMainGUI.changeAppState("account");
EthoWallets.renderWalletsState();
});
$("#mainNavBtnSettings").click(function() {
EthoMainGUI.changeAppState("settings");
EthoSettings.renderSettingsState();
});
EthoMainGUI = new MainGUI();

174
renderer/send.js Normal file
View File

@@ -0,0 +1,174 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class SendTransaction {
constructor() {}
renderSendState() {
EthoBlockchain.getAccountsData(
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoMainGUI.renderTemplate("send.html", data);
$(document).trigger("render_send");
}
);
}
validateSendForm() {
if (EthoMainGUI.getAppState() == "send") {
if (!$("#sendFromAddress").val()) {
EthoMainGUI.showGeneralError("Sender address must be specified!");
return false;
}
if (!EthoBlockchain.isAddress($("#sendFromAddress").val())) {
EthoMainGUI.showGeneralError("Sender address must be a valid address!");
return false;
}
if (!$("#sendToAddress").val()) {
EthoMainGUI.showGeneralError("Recipient address must be specified!");
return false;
}
if (!EthoBlockchain.isAddress($("#sendToAddress").val())) {
EthoMainGUI.showGeneralError("Recipient address must be a valid address!");
return false;
}
if (Number($("#sendAmmount").val()) <= 0) {
EthoMainGUI.showGeneralError("Send ammount must be greater then zero!");
return false;
}
return true;
} else {
return false;
}
}
resetSendForm() {
if (EthoMainGUI.getAppState() == "send") {
$("#sendFromAddress").val("");
$("#sendToAddress").val("");
$("#sendAmmount").val(0);
}
}
}
$(document).on("render_send", function() {
$('select').formSelect( {classes: "fromAddressSelect"});
$("#sendFromAddress").on("change", function() {
web3Local.eth.getBalance(this.value, function(error, balance) {
$("#sendMaxAmmount").html(parseFloat(web3Local.utils.fromWei(balance, 'ether')));
});
/*
// list all transactions for this address
if (this.value) {
$("#cardTransactionsForAddress").css("display", "block");
setTimeout(() => {
// render the transactions
$('#tableTransactionsForAddress').DataTable({
"paging": false,
"scrollY": "calc(100vh - 115px)",
"responsive": true,
"processing": true,
"order": [[ 0, "desc" ]],
"data": ipcRenderer.sendSync('getTransactions', this.value),
"columnDefs": [
{
"className": "transactionsBlockNum",
"targets": 0
},
{
"targets": 1,
"render": function ( data, type, row ) {
return moment(data).format("MMM Do YYYY");
}
},
{
"targets": 4,
"render": function ( data, type, row ) {
return parseFloat(web3Local.utils.fromWei(EthoUtils.toFixed(parseFloat(data)).toString(), 'ether')).toFixed(2);
}
}
],
"drawCallback": function( settings ) {
$("#loadingTransactionsOverlay").css("display", "none");
}
});
}, 200);
} else {
$("#cardTransactionsForAddress").css("display", "none");
}
*/
});
$("#btnSendAll").off('click').on('click', function() {
$("#sendAmmount").focus();
$("#sendAmmount").val($("#sendMaxAmmount").html());
});
$("#btnSendTransaction").off('click').on('click', function() {
if (EthoSend.validateSendForm()) {
EthoBlockchain.getTranasctionFee($("#sendFromAddress").val(), $("#sendToAddress").val(), $("#sendAmmount").val(),
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
$("#dlgSendWalletPassword").iziModal();
$("#walletPassword").val("");
$("#fromAddressInfo").html($("#sendFromAddress").val());
$("#toAddressInfo").html($("#sendToAddress").val());
$("#valueToSendInfo").html($("#sendAmmount").val());
$("#feeToPayInfo").html(parseFloat(web3Local.utils.fromWei(data.toString(), 'ether')));
$('#dlgSendWalletPassword').iziModal('open');
function doSendTransaction() {
$('#dlgSendWalletPassword').iziModal('close');
EthoBlockchain.prepareTransaction(
$("#walletPassword").val(),
$("#sendFromAddress").val(),
$("#sendToAddress").val(),
$("#sendAmmount").val(),
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoBlockchain.sendTransaction(data.raw,
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoSend.resetSendForm();
// use the transaction hash
}
);
}
);
}
$("#btnSendWalletPasswordConfirm").off('click').on('click', function() {
doSendTransaction();
});
$("#dlgSendWalletPassword").off('keypress').on('keypress', function(e) {
if(e.which == 13) {
doSendTransaction();
}
});
}
);
}
});
});
// create new account variable
EthoSend = new SendTransaction();

28
renderer/settings.js Normal file
View File

@@ -0,0 +1,28 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Settings {
constructor() {}
renderSettingsState() {
EthoMainGUI.renderTemplate("settings.html", {});
$(document).trigger("render_settings");
}
}
$(document).on("render_settings", function() {
$("#btnSettingsCleanTransactions").off('click').on('click', function() {
EthoMainGUI.showGeneralError("Not implemented yet!");
});
$("#btnSettingsCleanWallets").off('click').on('click', function() {
EthoMainGUI.showGeneralError("Not implemented yet!");
});
$("#btnSettingsCleanBlockchain").off('click').on('click', function() {
EthoMainGUI.showGeneralError("Not implemented yet!");
});
});
// create new account variable
EthoSettings = new Settings();

109
renderer/syncing.js Normal file
View File

@@ -0,0 +1,109 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
// Set the provider you want from Web3.providers
SyncProgress = new ProgressBar.Line('#syncProgress',
{
strokeWidth: 6,
easing: 'easeInOut',
duration: 1400,
color: "#7A1336",
trailColor: '#eee',
trailWidth: 1,
text: {
style: {
color: '#bbb',
position: "absolute",
left: "50%",
top: "-1px",
transform: "translateX(-50%)",
fontSize: "0.9em",
LineHeight: "24px",
padding: 0
},
autoStyleContainer: false
},
from: {color: '#FFEA82'},
to: {color: '#ED6A5A'}
});
// set initial value for the progress text
SyncProgress.setText("initializing, please wait...");
var peerCountInterval = setInterval(function()
{
web3Local.eth.net.getPeerCount(function(error, count) {
$("#peerCount").html(vsprintf("Peer Count: %d", [count]));
});
}, 5000);
function StartSyncProcess() {
var alreadyCatchedUp = false;
function keepTheNodeInSync(interval) {
var nodeSyncInterval = setInterval(function()
{
web3Local.eth.isSyncing(function(error, sync)
{
if(!error) {
if(sync == true) {
console.log("start the sync");
} else if(sync) {
SyncProgress.animate(sync.currentBlock / sync.highestBlock);
SyncProgress.setText(vsprintf('%d/%d (%d%%)', [sync.currentBlock, sync.highestBlock, Math.round(sync.currentBlock / sync.highestBlock * 100)]));
} else {
web3Local.eth.getBlock("latest", function(error, localBlock) {
if (localBlock.number > 0) {
web3Remote.eth.getBlock("latest", function(error, remoteBlock) {
if (!EthoTransactions.getIsSyncing()) {
SyncProgress.animate(localBlock.number / remoteBlock.number);
SyncProgress.setText(vsprintf('%d/%d (%d%%)', [localBlock.number, remoteBlock.number, Math.round(localBlock.number / remoteBlock.number * 100)]));
}
if (remoteBlock.number == localBlock.number) {
if (alreadyCatchedUp == false)
{
// clear the repeat interval and render wallets
$(document).trigger("onNewAccountTransaction");
clearInterval(nodeSyncInterval);
alreadyCatchedUp = true;
// sync all the transactions to the current block
EthoTransactions.syncTransactionsForAllAddresses(localBlock.number);
$(document).trigger("onSyncInterval");
// restart with less intensity
keepTheNodeInSync(10000);
}
}
});
}
});
}
}
});
}, interval);
}
// initial fast syncing
keepTheNodeInSync(2000);
}
var InitWeb3 = setInterval(function()
{
try {
web3Local = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8546'));
web3Remote = new Web3(new Web3.providers.HttpProvider("https://rpc.ether1.org"));
web3Local.eth.net.isListening(function(error, success) {
if (!error) {
$(document).trigger("onGethReady");
clearInterval(InitWeb3);
StartSyncProcess();
}
});
}
catch(err) {
console.log(err);
}
}, 2000);

188
renderer/transactions.js Normal file
View File

@@ -0,0 +1,188 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Transactions {
constructor() {
this.isSyncing = false;
}
setIsSyncing(value) {
this.isSyncing = value;
}
getIsSyncing() {
return this.isSyncing;
}
syncTransactionsForSingleAddress(addressList, counters, lastBlock, counter) {
if (counter < addressList.length - 1) {
SyncProgress.setText(vsprintf("Syncing address transactions %d/%d, please wait...", [counter, addressList.length]));
var startBlock = parseInt(counters.transactions) || 0;
var params = vsprintf('?address=%s&fromBlock=%d&toBlock=%d', [addressList[counter], startBlock, lastBlock]);
$.getJSON("https://richlist.ether1.org/transactions_list.php" + params, function( result ) {
result.data.forEach(element => {
var Transaction = {
block: element.block,
fromaddr: element.fromaddr,
timestamp: element.timestamp,
toaddr: element.toaddr,
value: element.value
}
ipcRenderer.send('storeTransaction', Transaction);
});
// update the counter and store it back to file system
counters.transactions = lastBlock;
ipcRenderer.sendSync('setJSONFile',
{
file: 'counters.json',
data: counters
});
// call the transaction sync for the next address
EthoTransactions.syncTransactionsForSingleAddress(addressList, counters, lastBlock, counter + 1);
});
} else {
SyncProgress.setText("Syncing transactions is complete.");
EthoTransactions.setIsSyncing(false);
}
}
syncTransactionsForAllAddresses(lastBlock) {
var counters = ipcRenderer.sendSync('getJSONFile', 'counters.json');
var counter = 0;
if (counters == null) {
counters = {};
}
EthoBlockchain.getAccounts(
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoTransactions.setIsSyncing(true);
EthoTransactions.syncTransactionsForSingleAddress(data, counters, lastBlock, counter);
}
);
}
renderTransactions() {
EthoMainGUI.renderTemplate("transactions.html", {});
$(document).trigger("render_transactions");
// show the loading overlay for transactions
$("#loadingTransactionsOverlay").css("display", "block");
setTimeout(() => {
// render the transactions
$('#tableTransactionsForAll').DataTable({
"paging": false,
"scrollY": "calc(100vh - 115px)",
"responsive": true,
"processing": true,
"order": [[ 0, "desc" ]],
"data": ipcRenderer.sendSync('getTransactions'),
"columnDefs": [
{
"className": "transactionsBlockNum",
"targets": 0
},
{
"targets": 1,
"render": function ( data, type, row ) {
return moment(data).format("MMM Do YYYY");
}
},
{
"targets": 4,
"render": function ( data, type, row ) {
return parseFloat(web3Local.utils.fromWei(EthoUtils.toFixed(parseFloat(data)).toString(), 'ether')).toFixed(2);
}
}
],
"drawCallback": function( settings ) {
$("#loadingTransactionsOverlay").css("display", "none");
}
});
}, 200);
}
}
// event that tells us that geth is ready and up
$(document).on("onSyncInterval", function() {
var counters = ipcRenderer.sendSync('getJSONFile', 'counters.json');
if (counters == null) {
counters = {};
}
function doSyncRemainingBlocks() {
EthoBlockchain.getBlock("latest", false,
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(block) {
var lastBlock = counters.transactions || 0;
if (lastBlock < block.number) {
function getNextBlockTransactions(blockNumber, maxBlock) {
EthoBlockchain.getBlock(blockNumber, true,
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
if (blockNumber < maxBlock) {
if (data.transactions) {
data.transactions.forEach(element => {
if ((EthoWallets.getAddressExists(element.from)) || (EthoWallets.getAddressExists(element.to))) {
var Transaction = {
block: element.blockNumber.toString(),
fromaddr: element.from.toLowerCase(),
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
toaddr: element.to.toLowerCase(),
value: Number(element.value).toExponential(5).toString().replace('+','')
}
// store transaction and notify about new transactions
ipcRenderer.send('storeTransaction', Transaction);
$(document).trigger("onNewAccountTransaction");
}
});
}
// call the next iteration for the next block
getNextBlockTransactions(blockNumber + 1 , maxBlock)
}
}
);
}
// call initial call of function
getNextBlockTransactions(lastBlock, block.number);
} else {
counters.transactions = block.number;
ipcRenderer.sendSync('setJSONFile',
{
file: 'counters.json',
data: counters
});
setTimeout(function() {
doSyncRemainingBlocks();
}, 10000);
}
}
);
}
// do the initial sync
doSyncRemainingBlocks();
});
// create new transactions variable
EthoTransactions = new Transactions();

23
renderer/utils.js Normal file
View File

@@ -0,0 +1,23 @@
class Utils {
constructor() {}
toFixed(x) {
if (Math.abs(x) < 1.0) {
var e = parseInt(x.toString().split('e-')[1]);
if (e) {
x *= Math.pow(10,e-1);
x = '0.' + (new Array(e)).join('0') + x.toString().substring(2);
}
} else {
var e = parseInt(x.toString().split('+')[1]);
if (e > 20) {
e -= 20;
x /= Math.pow(10,e);
x += (new Array(e+1)).join('0');
}
}
return x;
}
}
EthoUtils = new Utils();

158
renderer/wallets.js Normal file
View File

@@ -0,0 +1,158 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Wallets {
constructor() {
this.addressList = [];
}
getAddressList() {
return this.addressList;
}
clearAddressList() {
this.addressList = [];
}
getAddressExists(address) {
return this.addressList.indexOf(address.toLowerCase()) > -1;
}
addAddressToList(address) {
this.addressList.push(address.toLowerCase());
}
validateNewAccountForm() {
if (EthoMainGUI.getAppState() == "account") {
if (!$("#walletPasswordFirst").val()) {
EthoMainGUI.showGeneralError("Password cannot be empty!");
return false;
}
if (!$("#walletPasswordSecond").val()) {
EthoMainGUI.showGeneralError("Password cannot be empty!");
return false;
}
if ($("#walletPasswordFirst").val() !== $("#walletPasswordSecond").val()) {
EthoMainGUI.showGeneralError("Passwords do not match!");
return false;
}
return true;
} else {
return false;
}
}
renderWalletsState() {
// clear the list of addresses
EthoWallets.clearAddressList();
EthoBlockchain.getAccountsData(
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
data.addressData.forEach(element => {
EthoWallets.addAddressToList(element.address);
});
// render the wallets current state
EthoMainGUI.renderTemplate("wallets.html", data);
$(document).trigger("render_wallets");
}
);
}
}
// the event to tell us that the wallets are rendered
$(document).on("render_wallets", function() {
$('#addressTable').floatThead();
$("#btnNewAddress").off('click').on('click', function() {
$("#dlgCreateWalletPassword").iziModal();
$("#walletPasswordFirst").val("");
$("#walletPasswordSecond").val("");
$('#dlgCreateWalletPassword').iziModal('open');
function doCreateNewWallet() {
$('#dlgCreateWalletPassword').iziModal('close');
if (EthoWallets.validateNewAccountForm()) {
EthoBlockchain.createNewAccount($("#walletPasswordFirst").val(),
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(account) {
EthoWallets.addAddressToList(account);
EthoWallets.renderWalletsState();
}
);
}
}
$("#btnCreateWalletConfirm").off('click').on('click', function() {
doCreateNewWallet();
});
$("dlgCreateWalletPassword").off('keypress').on('keypress', function(e) {
if(e.which == 13) {
doCreateNewWallet();
}
});
});
$(".btnChangWalletName").off('click').on('click', function() {
var walletAddress = $(this).attr('data-wallet');
var walletName = $(this).attr('data-name');
$("#dlgChangeWalletName").iziModal();
$("#inputWalletName").val(walletName);
$('#dlgChangeWalletName').iziModal('open');
function doChangeWalletName() {
var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json');
if (!wallets) {
wallets = { names: {} };
}
// set the wallet name from the dialog box
wallets.names[walletAddress] = $("#inputWalletName").val();
ipcRenderer.sendSync('setJSONFile',
{
file: 'wallets.json',
data: wallets
});
$('#dlgChangeWalletName').iziModal('close');
EthoWallets.renderWalletsState();
}
$("#btnChangeWalletNameConfirm").off('click').on('click', function() {
doChangeWalletName();
});
$("#dlgChangeWalletName").off('keypress').on('keypress', function(e) {
if(e.which == 13) {
doChangeWalletName();
}
});
});
});
// event that tells us that geth is ready and up
$(document).on("onGethReady", function() {
EthoMainGUI.changeAppState("account");
EthoWallets.renderWalletsState();
});
$(document).on("onNewAccountTransaction", function() {
if (EthoMainGUI.getAppState() == "account") {
EthoWallets.renderWalletsState();
}
});
EthoWallets = new Wallets();