Merge pull request #633 from embark-framework/fund_accounts
Changes for 3.1 & --dev mode
This commit is contained in:
commit
f5d82d222c
|
@ -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();
|
||||
|
|
|
@ -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;
|
|
@ -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 = [];
|
||||
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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});
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
"rpcPort": 8545,
|
||||
"rpcCorsDomain": "auto",
|
||||
"account": {
|
||||
"numAccounts": 3,
|
||||
"balance": "5 ether",
|
||||
"password": "config/development/password"
|
||||
},
|
||||
"targetGasLimit": 8000000,
|
||||
|
|
Loading…
Reference in New Issue