Merge pull request #633 from embark-framework/fund_accounts

Changes for 3.1 & --dev mode
This commit is contained in:
Iuri Matias 2018-07-19 11:16:20 +03:00 committed by GitHub
commit f5d82d222c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 3102 additions and 2916 deletions

View File

@ -6,6 +6,7 @@ const fs = require('../../core/fs.js');
const constants = require('../../constants.json');
const GethCommands = require('./geth_commands.js');
const DevFunds = require('./dev_funds.js');
/*eslint complexity: ["error", 36]*/
var Blockchain = function(options) {
@ -13,12 +14,17 @@ var Blockchain = function(options) {
this.env = options.env || 'development';
this.client = options.client;
this.isDev = options.isDev;
this.onReadyCallback = options.onReadyCallback;
this.onReadyCallback = options.onReadyCallback || (() => {});
if ((this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') && this.env !== 'development') {
console.log("===> " + __("warning: running default config on a non-development environment"));
}
let defaultWsApi = ['eth', 'web3', 'net', 'shh', 'debug'];
if (this.isDev) {
defaultWsApi.push('personal');
}
this.config = {
geth_bin: this.blockchainConfig.geth_bin || 'geth',
networkType: this.blockchainConfig.networkType || 'custom',
@ -41,7 +47,7 @@ var Blockchain = function(options) {
wsHost: this.blockchainConfig.wsHost || 'localhost',
wsPort: this.blockchainConfig.wsPort || 8546,
wsOrigins: this.blockchainConfig.wsOrigins || false,
wsApi: (this.blockchainConfig.wsApi || ['eth', 'web3', 'net', 'shh', 'debug']),
wsApi: (this.blockchainConfig.wsApi || defaultWsApi),
vmdebug: this.blockchainConfig.vmdebug || false,
targetGasLimit: this.blockchainConfig.targetGasLimit || false,
syncMode: this.blockchainConfig.syncMode,
@ -162,6 +168,13 @@ Blockchain.prototype.run = function() {
self.child.stderr.on('data', (data) => {
data = data.toString();
if (self.onReadyCallback && !self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) {
if (self.isDev) {
self.createFundAndUnlockAccounts((err) => {
if(err) console.error('Error creating, unlocking, and funding accounts', err);
//self.readyCalled = true;
//self.onReadyCallback();
});
}
self.readyCalled = true;
self.onReadyCallback();
}
@ -175,6 +188,13 @@ Blockchain.prototype.run = function() {
});
};
Blockchain.prototype.createFundAndUnlockAccounts = function(cb) {
let devFunds = new DevFunds(this.config);
devFunds.createFundAndUnlockAccounts((err) => {
cb(err);
});
};
Blockchain.prototype.kill = function() {
if (this.child) {
this.child.kill();

View File

@ -0,0 +1,124 @@
const async = require('async');
const Web3 = require('web3');
const {getWeiBalanceFromString, buildUrl} = require('../../utils/utils.js');
const {readFileSync, dappPath} = require('../../core/fs');
class DevFunds {
constructor(blockchainConfig) {
// TODO: temporary to avoid nasty suprises, should be removed
try {
this.web3 = null;
this.blockchainConfig = blockchainConfig;
this.accounts = [];
this.numAccounts = this.blockchainConfig.account.numAccounts || 0;
this.password = readFileSync(dappPath(blockchainConfig.account.password), 'utf8').replace('\n', '');
this.web3 = new Web3();
this.networkId = null;
this.balance = Web3.utils.toWei("1", "ether");
if (this.blockchainConfig.account.balance) {
this.balance = getWeiBalanceFromString(this.blockchainConfig.account.balance, this.web3);
}
} catch(_err) {
}
}
_sendTx() {
if (this.networkId !== 1337) {
return;
}
this.web3.eth.sendTransaction({value: "1000000000000000", to: "0xA2817254cb8E7b6269D1689c3E0eBadbB78889d1", from: this.web3.eth.defaultAccount});
}
// trigger regular txs due to a bug in geth and stuck transactions in --dev mode
regularTxs(cb) {
const self = this;
self.web3.eth.net.getId().then((networkId) => {
self.networkId = networkId;
if (self.networkId !== 1337) {
return;
}
setInterval(function() { self._sendTx(); }, 1500);
if (cb) {
cb();
}
});
}
regularUnlocks() {
const self = this;
setInterval(function() { self.unlockAccounts(self.password, () => {}); }, 20000);
}
connectToNode(cb) {
this.web3.setProvider(new Web3.providers.WebsocketProvider(buildUrl('ws', this.blockchainConfig.wsHost, this.blockchainConfig.wsPort), {headers: {Origin: "http://localhost:8000"}}));
this.web3.eth.getAccounts().then((accounts) => {
this.web3.eth.defaultAccount = accounts[0];
if (accounts.length > 1) {
this.accounts = accounts.slice(1);
}
cb();
});
}
createAccounts(numAccounts, password, cb) {
const numAccountsToCreate = numAccounts - (this.accounts.length + 1);
if (numAccountsToCreate === 0) return cb();
async.timesLimit(numAccountsToCreate, 1, (_, next) => {
this.web3.eth.personal.newAccount(password, next);
}, (err, accounts) => {
if (err) return cb(err);
this.accounts = accounts;
cb();
});
}
unlockAccounts(password, cb) {
async.each(this.accounts, (account, next) => {
this.web3.eth.personal.unlockAccount(account, password).then((_result) => {
next();
}).catch(next);
}, cb);
}
fundAccounts(balance, cb) {
async.each(this.accounts, (account, next) => {
this.web3.eth.getBalance(account).then(currBalance => {
const remainingBalance = balance - currBalance;
if (remainingBalance <= 0) return next();
this.web3.eth.sendTransaction({to: account, value: remainingBalance}).then((_result) => {
next();
}).catch(next);
}, cb);
});
}
createFundAndUnlockAccounts(cb) {
if (!this.web3) {
return cb();
}
async.waterfall([
(next) => {
this.connectToNode(next);
},
(next) => {
this.createAccounts(this.numAccounts, this.password, next);
},
(next) => {
this.unlockAccounts(this.password, next);
},
(next) => {
this.regularTxs();
this.regularUnlocks();
this.fundAccounts(this.balance, next);
}
], cb);
}
}
module.exports = DevFunds;

View File

@ -123,7 +123,13 @@ class GethCommands {
let self = this;
let config = this.config;
let rpc_api = (this.config.rpcApi || ['eth', 'web3', 'net', 'debug']);
let ws_api = (this.config.wsApi || ['eth', 'web3', 'net', 'debug']);
let defaultWsApi = ['eth', 'web3', 'net', 'shh', 'debug'];
if (this.isDev) {
defaultWsApi.push('personal');
}
let ws_api = (this.config.wsApi || defaultWsApi);
let args = [];

View File

@ -1,6 +1,7 @@
const bip39 = require("bip39");
const hdkey = require('ethereumjs-wallet/hdkey');
const fs = require('../core/fs');
const {getHexBalanceFromString} = require('../utils/utils');
class AccountParser {
static parseAccountsConfig(accountsConfig, web3, logger) {
@ -21,31 +22,14 @@ class AccountParser {
return accounts;
}
static getHexBalance(balanceString, web3) {
if (!balanceString) {
return 0xFFFFFFFFFFFFFFFFFF;
}
if (web3.utils.isHexStrict(balanceString)) {
return balanceString;
}
const match = balanceString.match(/([0-9]+) ?([a-zA-Z]*)/);
if (!match) {
throw new Error(__('Unrecognized balance string "%s"', balanceString));
}
if (!match[2]) {
return web3.utils.toHex(parseInt(match[1], 10));
}
return web3.utils.toHex(web3.utils.toWei(match[1], match[2]));
}
static getAccount(accountConfig, web3, logger) {
if (!logger) {
logger = console;
}
let hexBalance = null;
if (accountConfig.balance) {
hexBalance = AccountParser.getHexBalance(accountConfig.balance, web3);
hexBalance = getHexBalanceFromString(accountConfig.balance, web3);
//hexBalance = getHexBalanceFromString(accountConfig.balance, web3);
}
if (accountConfig.privateKey) {
if (!accountConfig.privateKey.startsWith('0x')) {

View File

@ -36,6 +36,7 @@ class Blockchain {
cb = function(){};
}
if (this.isWeb3Ready) {
this.events.emit(WEB3_READY);
return cb();
}
const self = this;
@ -50,7 +51,6 @@ class Blockchain {
const protocol = (this.contractsConfig.deployment.type === "rpc") ? this.contractsConfig.deployment.protocol : 'ws';
let provider;
this.web3Endpoint = utils.buildUrl(protocol, this.contractsConfig.deployment.host, this.contractsConfig.deployment.port);//`${protocol}://${this.contractsConfig.deployment.host}:${this.contractsConfig.deployment.port}`;
const providerOptions = {
web3: this.web3,
accountsConfig: this.contractsConfig.deployment.accounts,
@ -87,6 +87,7 @@ class Blockchain {
provider.startWeb3Provider(next);
},
function fundAccountsIfNeeded(next) {
self.isWeb3Ready = true;
provider.fundAccounts(next);
}
], (err) => {

View File

@ -199,6 +199,12 @@ class ContractDeployer {
function applyBeforeDeploy(next) {
self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, next);
},
function getGasPriceForNetwork(next) {
self.events.request("blockchain:gasPrice", (gasPrice) => {
contract.gasPrice = contract.gasPrice || gasPrice;
next();
});
},
function createDeployObject(next) {
let contractObject = self.blockchain.ContractObject({abi: contract.abiDefinition});

View File

@ -90,10 +90,7 @@ class ContractsManager {
callback();
},
function getGasPriceForNetwork(callback) {
if (self.contractsConfig.gasPrice) {
return callback(null, self.contractsConfig.gasPrice);
}
self.events.request("blockchain:gasPrice", callback);
return callback(null, self.contractsConfig.gasPrice);
},
function prepareContractsFromCompilation(gasPrice, callback) {
let className, compiledContract, contractConfig, contract;

View File

@ -83,9 +83,7 @@ class DeployManager {
// TODO: could be implemented as an event (beforeDeployAll)
function checkIsConnectedToBlockchain(callback) {
self.blockchain.onReady(() => {
self.blockchain.assertNodeConnection((err) => {
callback(err);
});
self.blockchain.assertNodeConnection(callback);
});
},

View File

@ -45,7 +45,7 @@ class Embark {
blockchain(env, client) {
this.context = [constants.contexts.blockchain];
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env, this.isDev(env)).run();
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env, this.isDev(env), () => {}).run();
}
simulator(options) {

View File

@ -1,6 +1,8 @@
let http = require('follow-redirects').http;
let https = require('follow-redirects').https;
const balanceRegex = /([0-9]+) ?([a-zA-Z]*)/;
function joinPath() {
const path = require('path');
return path.join.apply(path.join, arguments);
@ -297,6 +299,45 @@ function buildUrlFromConfig (configObj){
return this.buildUrl(configObj.protocol, configObj.host, configObj.port);
}
function getWeiBalanceFromString(balanceString, web3){
if(!web3){
throw new Error(__('[utils.getWeiBalanceFromString]: Missing parameter \'web3\''));
}
if (!balanceString) {
return 0;
}
const match = balanceString.match(balanceRegex);
if (!match) {
throw new Error(__('Unrecognized balance string "%s"', balanceString));
}
if (!match[2]) {
return web3.utils.toHex(parseInt(match[1], 10));
}
return web3.utils.toWei(match[1], match[2]);
}
function getHexBalanceFromString(balanceString, web3) {
if(!web3){
throw new Error(__('[utils.getWeiBalanceFromString]: Missing parameter \'web3\''));
}
if (!balanceString) {
return 0xFFFFFFFFFFFFFFFFFF;
}
if (web3.utils.isHexStrict(balanceString)) {
return balanceString;
}
const match = balanceString.match(balanceRegex);
if (!match) {
throw new Error(__('Unrecognized balance string "%s"', balanceString));
}
if (!match[2]) {
return web3.utils.toHex(parseInt(match[1], 10));
}
return web3.utils.toHex(web3.utils.toWei(match[1], match[2]));
}
module.exports = {
joinPath: joinPath,
filesMatchingPattern: filesMatchingPattern,
@ -323,5 +364,7 @@ module.exports = {
sha3: sha3,
normalizeInput,
buildUrl,
buildUrlFromConfig
buildUrlFromConfig,
getWeiBalanceFromString,
getHexBalanceFromString
};

5760
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -2,6 +2,7 @@
const assert = require('assert');
const sinon = require('sinon');
const AccountParser = require('../lib/contracts/accountParser');
const utils = require('../lib/utils/utils');
let TestLogger = require('../lib/tests/test_logger.js');
const Web3 = require('web3');
const i18n = require('../lib/i18n/i18n.js');
@ -75,38 +76,38 @@ describe('embark.AccountParser', function () {
describe('getHexBalance', () => {
it('should return default if no balance', () => {
const hexBalance = AccountParser.getHexBalance(null, Web3);
const hexBalance = utils.getHexBalanceFromString(null, Web3);
assert.strictEqual(hexBalance, 0xFFFFFFFFFFFFFFFFFF);
});
it('should return the balance string if already hexadecimal', () => {
const hexBalance = AccountParser.getHexBalance('0xFFF', Web3);
const hexBalance = utils.getHexBalanceFromString('0xFFF', Web3);
assert.strictEqual(hexBalance, '0xFFF');
});
it('should convert to hex when decimal', () => {
const hexBalance = AccountParser.getHexBalance('500', Web3);
const hexBalance = utils.getHexBalanceFromString('500', Web3);
assert.strictEqual(hexBalance, '0x1f4');
});
it('should convert to hex with eth string', () => {
const hexBalance = AccountParser.getHexBalance('4ether', Web3);
const hexBalance = utils.getHexBalanceFromString('4ether', Web3);
assert.strictEqual(hexBalance, '0x3782dace9d900000');
});
it('should convert to hex with eth string with space', () => {
const hexBalance = AccountParser.getHexBalance('673 shannon', Web3);
const hexBalance = utils.getHexBalanceFromString('673 shannon', Web3);
assert.strictEqual(hexBalance, '0x9cb1ed0a00');
});
it('should fail when string is not good', () => {
try {
AccountParser.getHexBalance('nogood', Web3);
utils.getHexBalanceFromString('nogood', Web3);
assert.fail('Should have failed at getHexBalance');
} catch (e) {
// Ok

View File

@ -13,6 +13,8 @@
"rpcPort": 8545,
"rpcCorsDomain": "auto",
"account": {
"numAccounts": 3,
"balance": "5 ether",
"password": "config/development/password"
},
"targetGasLimit": 8000000,