Skip to content

Commit f5d82d2

Browse files
authored
Merge pull request #633 from embark-framework/fund_accounts
Changes for 3.1 & --dev mode
2 parents 4467874 + 71e9eed commit f5d82d2

File tree

13 files changed

+3103
-2917
lines changed

13 files changed

+3103
-2917
lines changed

lib/cmds/blockchain/blockchain.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,25 @@ const fs = require('../../core/fs.js');
66
const constants = require('../../constants.json');
77

88
const GethCommands = require('./geth_commands.js');
9+
const DevFunds = require('./dev_funds.js');
910

1011
/*eslint complexity: ["error", 36]*/
1112
var Blockchain = function(options) {
1213
this.blockchainConfig = options.blockchainConfig;
1314
this.env = options.env || 'development';
1415
this.client = options.client;
1516
this.isDev = options.isDev;
16-
this.onReadyCallback = options.onReadyCallback;
17+
this.onReadyCallback = options.onReadyCallback || (() => {});
1718

1819
if ((this.blockchainConfig === {} || JSON.stringify(this.blockchainConfig) === '{"enabled":true}') && this.env !== 'development') {
1920
console.log("===> " + __("warning: running default config on a non-development environment"));
2021
}
2122

23+
let defaultWsApi = ['eth', 'web3', 'net', 'shh', 'debug'];
24+
if (this.isDev) {
25+
defaultWsApi.push('personal');
26+
}
27+
2228
this.config = {
2329
geth_bin: this.blockchainConfig.geth_bin || 'geth',
2430
networkType: this.blockchainConfig.networkType || 'custom',
@@ -41,7 +47,7 @@ var Blockchain = function(options) {
4147
wsHost: this.blockchainConfig.wsHost || 'localhost',
4248
wsPort: this.blockchainConfig.wsPort || 8546,
4349
wsOrigins: this.blockchainConfig.wsOrigins || false,
44-
wsApi: (this.blockchainConfig.wsApi || ['eth', 'web3', 'net', 'shh', 'debug']),
50+
wsApi: (this.blockchainConfig.wsApi || defaultWsApi),
4551
vmdebug: this.blockchainConfig.vmdebug || false,
4652
targetGasLimit: this.blockchainConfig.targetGasLimit || false,
4753
syncMode: this.blockchainConfig.syncMode,
@@ -162,6 +168,13 @@ Blockchain.prototype.run = function() {
162168
self.child.stderr.on('data', (data) => {
163169
data = data.toString();
164170
if (self.onReadyCallback && !self.readyCalled && data.indexOf('WebSocket endpoint opened') > -1) {
171+
if (self.isDev) {
172+
self.createFundAndUnlockAccounts((err) => {
173+
if(err) console.error('Error creating, unlocking, and funding accounts', err);
174+
//self.readyCalled = true;
175+
//self.onReadyCallback();
176+
});
177+
}
165178
self.readyCalled = true;
166179
self.onReadyCallback();
167180
}
@@ -175,6 +188,13 @@ Blockchain.prototype.run = function() {
175188
});
176189
};
177190

191+
Blockchain.prototype.createFundAndUnlockAccounts = function(cb) {
192+
let devFunds = new DevFunds(this.config);
193+
devFunds.createFundAndUnlockAccounts((err) => {
194+
cb(err);
195+
});
196+
};
197+
178198
Blockchain.prototype.kill = function() {
179199
if (this.child) {
180200
this.child.kill();

lib/cmds/blockchain/dev_funds.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
const async = require('async');
2+
const Web3 = require('web3');
3+
const {getWeiBalanceFromString, buildUrl} = require('../../utils/utils.js');
4+
const {readFileSync, dappPath} = require('../../core/fs');
5+
6+
class DevFunds {
7+
constructor(blockchainConfig) {
8+
// TODO: temporary to avoid nasty suprises, should be removed
9+
try {
10+
this.web3 = null;
11+
this.blockchainConfig = blockchainConfig;
12+
this.accounts = [];
13+
this.numAccounts = this.blockchainConfig.account.numAccounts || 0;
14+
this.password = readFileSync(dappPath(blockchainConfig.account.password), 'utf8').replace('\n', '');
15+
this.web3 = new Web3();
16+
this.networkId = null;
17+
this.balance = Web3.utils.toWei("1", "ether");
18+
if (this.blockchainConfig.account.balance) {
19+
this.balance = getWeiBalanceFromString(this.blockchainConfig.account.balance, this.web3);
20+
}
21+
} catch(_err) {
22+
}
23+
}
24+
25+
_sendTx() {
26+
if (this.networkId !== 1337) {
27+
return;
28+
}
29+
this.web3.eth.sendTransaction({value: "1000000000000000", to: "0xA2817254cb8E7b6269D1689c3E0eBadbB78889d1", from: this.web3.eth.defaultAccount});
30+
}
31+
32+
// trigger regular txs due to a bug in geth and stuck transactions in --dev mode
33+
regularTxs(cb) {
34+
const self = this;
35+
self.web3.eth.net.getId().then((networkId) => {
36+
self.networkId = networkId;
37+
if (self.networkId !== 1337) {
38+
return;
39+
}
40+
41+
setInterval(function() { self._sendTx(); }, 1500);
42+
if (cb) {
43+
cb();
44+
}
45+
});
46+
}
47+
48+
regularUnlocks() {
49+
const self = this;
50+
setInterval(function() { self.unlockAccounts(self.password, () => {}); }, 20000);
51+
}
52+
53+
connectToNode(cb) {
54+
55+
this.web3.setProvider(new Web3.providers.WebsocketProvider(buildUrl('ws', this.blockchainConfig.wsHost, this.blockchainConfig.wsPort), {headers: {Origin: "http://localhost:8000"}}));
56+
57+
this.web3.eth.getAccounts().then((accounts) => {
58+
this.web3.eth.defaultAccount = accounts[0];
59+
if (accounts.length > 1) {
60+
this.accounts = accounts.slice(1);
61+
}
62+
cb();
63+
});
64+
}
65+
66+
createAccounts(numAccounts, password, cb) {
67+
const numAccountsToCreate = numAccounts - (this.accounts.length + 1);
68+
if (numAccountsToCreate === 0) return cb();
69+
70+
async.timesLimit(numAccountsToCreate, 1, (_, next) => {
71+
this.web3.eth.personal.newAccount(password, next);
72+
}, (err, accounts) => {
73+
if (err) return cb(err);
74+
this.accounts = accounts;
75+
cb();
76+
});
77+
}
78+
79+
unlockAccounts(password, cb) {
80+
async.each(this.accounts, (account, next) => {
81+
this.web3.eth.personal.unlockAccount(account, password).then((_result) => {
82+
next();
83+
}).catch(next);
84+
}, cb);
85+
}
86+
87+
fundAccounts(balance, cb) {
88+
89+
async.each(this.accounts, (account, next) => {
90+
this.web3.eth.getBalance(account).then(currBalance => {
91+
const remainingBalance = balance - currBalance;
92+
if (remainingBalance <= 0) return next();
93+
94+
this.web3.eth.sendTransaction({to: account, value: remainingBalance}).then((_result) => {
95+
next();
96+
}).catch(next);
97+
}, cb);
98+
});
99+
}
100+
101+
createFundAndUnlockAccounts(cb) {
102+
if (!this.web3) {
103+
return cb();
104+
}
105+
async.waterfall([
106+
(next) => {
107+
this.connectToNode(next);
108+
},
109+
(next) => {
110+
this.createAccounts(this.numAccounts, this.password, next);
111+
},
112+
(next) => {
113+
this.unlockAccounts(this.password, next);
114+
},
115+
(next) => {
116+
this.regularTxs();
117+
this.regularUnlocks();
118+
this.fundAccounts(this.balance, next);
119+
}
120+
], cb);
121+
}
122+
}
123+
124+
module.exports = DevFunds;

lib/cmds/blockchain/geth_commands.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,13 @@ class GethCommands {
123123
let self = this;
124124
let config = this.config;
125125
let rpc_api = (this.config.rpcApi || ['eth', 'web3', 'net', 'debug']);
126-
let ws_api = (this.config.wsApi || ['eth', 'web3', 'net', 'debug']);
126+
127+
let defaultWsApi = ['eth', 'web3', 'net', 'shh', 'debug'];
128+
if (this.isDev) {
129+
defaultWsApi.push('personal');
130+
}
131+
132+
let ws_api = (this.config.wsApi || defaultWsApi);
127133

128134
let args = [];
129135

lib/contracts/accountParser.js

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const bip39 = require("bip39");
22
const hdkey = require('ethereumjs-wallet/hdkey');
33
const fs = require('../core/fs');
4+
const {getHexBalanceFromString} = require('../utils/utils');
45

56
class AccountParser {
67
static parseAccountsConfig(accountsConfig, web3, logger) {
@@ -21,31 +22,14 @@ class AccountParser {
2122
return accounts;
2223
}
2324

24-
static getHexBalance(balanceString, web3) {
25-
if (!balanceString) {
26-
return 0xFFFFFFFFFFFFFFFFFF;
27-
}
28-
if (web3.utils.isHexStrict(balanceString)) {
29-
return balanceString;
30-
}
31-
const match = balanceString.match(/([0-9]+) ?([a-zA-Z]*)/);
32-
if (!match) {
33-
throw new Error(__('Unrecognized balance string "%s"', balanceString));
34-
}
35-
if (!match[2]) {
36-
return web3.utils.toHex(parseInt(match[1], 10));
37-
}
38-
39-
return web3.utils.toHex(web3.utils.toWei(match[1], match[2]));
40-
}
41-
4225
static getAccount(accountConfig, web3, logger) {
4326
if (!logger) {
4427
logger = console;
4528
}
4629
let hexBalance = null;
4730
if (accountConfig.balance) {
48-
hexBalance = AccountParser.getHexBalance(accountConfig.balance, web3);
31+
hexBalance = getHexBalanceFromString(accountConfig.balance, web3);
32+
//hexBalance = getHexBalanceFromString(accountConfig.balance, web3);
4933
}
5034
if (accountConfig.privateKey) {
5135
if (!accountConfig.privateKey.startsWith('0x')) {

lib/contracts/blockchain.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Blockchain {
3636
cb = function(){};
3737
}
3838
if (this.isWeb3Ready) {
39+
this.events.emit(WEB3_READY);
3940
return cb();
4041
}
4142
const self = this;
@@ -50,7 +51,6 @@ class Blockchain {
5051
const protocol = (this.contractsConfig.deployment.type === "rpc") ? this.contractsConfig.deployment.protocol : 'ws';
5152
let provider;
5253
this.web3Endpoint = utils.buildUrl(protocol, this.contractsConfig.deployment.host, this.contractsConfig.deployment.port);//`${protocol}://${this.contractsConfig.deployment.host}:${this.contractsConfig.deployment.port}`;
53-
5454
const providerOptions = {
5555
web3: this.web3,
5656
accountsConfig: this.contractsConfig.deployment.accounts,
@@ -87,6 +87,7 @@ class Blockchain {
8787
provider.startWeb3Provider(next);
8888
},
8989
function fundAccountsIfNeeded(next) {
90+
self.isWeb3Ready = true;
9091
provider.fundAccounts(next);
9192
}
9293
], (err) => {

lib/contracts/contract_deployer.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,12 @@ class ContractDeployer {
199199
function applyBeforeDeploy(next) {
200200
self.plugins.emitAndRunActionsForEvent('deploy:contract:beforeDeploy', {contract: contract}, next);
201201
},
202+
function getGasPriceForNetwork(next) {
203+
self.events.request("blockchain:gasPrice", (gasPrice) => {
204+
contract.gasPrice = contract.gasPrice || gasPrice;
205+
next();
206+
});
207+
},
202208
function createDeployObject(next) {
203209
let contractObject = self.blockchain.ContractObject({abi: contract.abiDefinition});
204210

lib/contracts/contracts.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,7 @@ class ContractsManager {
9090
callback();
9191
},
9292
function getGasPriceForNetwork(callback) {
93-
if (self.contractsConfig.gasPrice) {
94-
return callback(null, self.contractsConfig.gasPrice);
95-
}
96-
self.events.request("blockchain:gasPrice", callback);
93+
return callback(null, self.contractsConfig.gasPrice);
9794
},
9895
function prepareContractsFromCompilation(gasPrice, callback) {
9996
let className, compiledContract, contractConfig, contract;

lib/contracts/deploy_manager.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,7 @@ class DeployManager {
8383
// TODO: could be implemented as an event (beforeDeployAll)
8484
function checkIsConnectedToBlockchain(callback) {
8585
self.blockchain.onReady(() => {
86-
self.blockchain.assertNodeConnection((err) => {
87-
callback(err);
88-
});
86+
self.blockchain.assertNodeConnection(callback);
8987
});
9088
},
9189

lib/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class Embark {
4545

4646
blockchain(env, client) {
4747
this.context = [constants.contexts.blockchain];
48-
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env, this.isDev(env)).run();
48+
return require('./cmds/blockchain/blockchain.js')(this.config.blockchainConfig, client, env, this.isDev(env), () => {}).run();
4949
}
5050

5151
simulator(options) {

lib/utils/utils.js

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
let http = require('follow-redirects').http;
22
let https = require('follow-redirects').https;
33

4+
const balanceRegex = /([0-9]+) ?([a-zA-Z]*)/;
5+
46
function joinPath() {
57
const path = require('path');
68
return path.join.apply(path.join, arguments);
@@ -297,6 +299,45 @@ function buildUrlFromConfig (configObj){
297299
return this.buildUrl(configObj.protocol, configObj.host, configObj.port);
298300
}
299301

302+
function getWeiBalanceFromString(balanceString, web3){
303+
if(!web3){
304+
throw new Error(__('[utils.getWeiBalanceFromString]: Missing parameter \'web3\''));
305+
}
306+
if (!balanceString) {
307+
return 0;
308+
}
309+
const match = balanceString.match(balanceRegex);
310+
if (!match) {
311+
throw new Error(__('Unrecognized balance string "%s"', balanceString));
312+
}
313+
if (!match[2]) {
314+
return web3.utils.toHex(parseInt(match[1], 10));
315+
}
316+
317+
return web3.utils.toWei(match[1], match[2]);
318+
}
319+
320+
function getHexBalanceFromString(balanceString, web3) {
321+
if(!web3){
322+
throw new Error(__('[utils.getWeiBalanceFromString]: Missing parameter \'web3\''));
323+
}
324+
if (!balanceString) {
325+
return 0xFFFFFFFFFFFFFFFFFF;
326+
}
327+
if (web3.utils.isHexStrict(balanceString)) {
328+
return balanceString;
329+
}
330+
const match = balanceString.match(balanceRegex);
331+
if (!match) {
332+
throw new Error(__('Unrecognized balance string "%s"', balanceString));
333+
}
334+
if (!match[2]) {
335+
return web3.utils.toHex(parseInt(match[1], 10));
336+
}
337+
338+
return web3.utils.toHex(web3.utils.toWei(match[1], match[2]));
339+
}
340+
300341
module.exports = {
301342
joinPath: joinPath,
302343
filesMatchingPattern: filesMatchingPattern,
@@ -323,5 +364,7 @@ module.exports = {
323364
sha3: sha3,
324365
normalizeInput,
325366
buildUrl,
326-
buildUrlFromConfig
367+
buildUrlFromConfig,
368+
getWeiBalanceFromString,
369+
getHexBalanceFromString
327370
};

0 commit comments

Comments
 (0)