Skip to content

Commit

Permalink
feat: Allow parallel deploy
Browse files Browse the repository at this point in the history
  • Loading branch information
alaibe committed Nov 16, 2018
1 parent 51b8224 commit 3406ae8
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 19 deletions.
48 changes: 43 additions & 5 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@
"embarkjs": "0.4.5",
"eth-ens-namehash": "2.0.8",
"eth-lib": "0.2.8",
"ethereumjs-tx": "1.3.7",
"ethereumjs-util": "6.0.0",
"ethereumjs-wallet": "0.6.0",
"express": "4.16.3",
"express-ws": "4.0.0",
Expand Down
69 changes: 59 additions & 10 deletions src/lib/modules/blockchain_connector/provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const async = require('async');
const AccountParser = require('../../utils/accountParser');
const fundAccount = require('./fundAccount');
const constants = require('../../constants');
const Transaction = require('ethereumjs-tx');
const ethUtil = require('ethereumjs-util');

class Provider {
constructor(options) {
Expand All @@ -12,6 +14,23 @@ class Provider {
this.web3Endpoint = options.web3Endpoint;
this.logger = options.logger;
this.isDev = options.isDev;
this.nonceCache = {};
}

getNonce(address, callback) {
this.web3.eth.getTransactionCount(address, (_error, transactionCount) => {
if(!this.nonceCache[address]) {
this.nonceCache[address] = -1;
}

if (transactionCount > this.nonceCache[address]) {
this.nonceCache[address] = transactionCount;
return callback(this.nonceCache[address]);
}

this.nonceCache[address]++;
callback(this.nonceCache[address]);
});
}

startWeb3Provider(callback) {
Expand All @@ -32,7 +51,6 @@ class Provider {
} else {
return callback(__("contracts config error: unknown deployment type %s", this.type));
}

self.web3.setProvider(self.provider);

self.web3.eth.getAccounts((err, accounts) => {
Expand All @@ -43,32 +61,63 @@ class Provider {
self.accounts = AccountParser.parseAccountsConfig(self.accountsConfig, self.web3, self.logger, accounts);
self.addresses = [];

if (!self.accounts.length) {
return callback();
}

self.accounts.forEach(account => {
self.addresses.push(account.address);
if (account.privateKey) {
self.web3.eth.accounts.wallet.add(account);
}
});
self.web3.eth.defaultAccount = self.addresses[0];

if (self.accounts.length) {
self.web3.eth.defaultAccount = self.addresses[0];
}

const realSend = self.provider.send.bind(self.provider);
self.provider.send = function (payload, cb) {

// Allow to run transaction in parallel by resolving
// the nonce manually.
// For each transaction, resolve the nonce by taking the
// max of current transaction count and the cache we keep
// locally.
// Deconstruct the transaction and update the nonce.
// Before updating the transaction, it must be signed.
self.runTransaction = async.queue(({payload}, callback) => {
const rawTx = payload.params[0];
const rawData = Buffer.from(ethUtil.stripHexPrefix(rawTx), 'hex');
const tx = new Transaction(rawData, 'hex');
const address = '0x' + tx.getSenderAddress().toString('hex').toLowerCase();

self.getNonce(address, (newNonce) => {
tx.nonce = newNonce;
const key = ethUtil.stripHexPrefix(self.web3.eth.accounts.wallet[address].privateKey);
const privKey = Buffer.from(key, 'hex');
tx.sign(privKey);
payload.params[0] = '0x' + tx.serialize().toString('hex');
return realSend(payload, (error, result) => {
self.web3.eth.getTransaction(result.result, () => {
callback(error, result);
});
});
});
}, 1);

self.provider.send = function(payload, cb) {
if (payload.method === 'eth_accounts') {
return realSend(payload, function (err, result) {
return realSend(payload, function(err, result) {
if (err) {
return cb(err);
}
result.result = self.addresses; // Send our addresses
if (self.accounts.length) {
result.result = self.addresses; // Send our addresses
}
cb(null, result);
});
} else if (payload.method === 'eth_sendRawTransaction') {
return self.runTransaction.push({payload}, cb);
}

realSend(payload, cb);
};

callback();
});
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib/modules/blockchain_process/blockchain.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ Blockchain.prototype.initChainAndGetAddress = function (callback) {
if (!self.config.genesisBlock || self.client.name === 'parity') {
return next();
}
this.logger.info(__("initializing genesis block").green);
self.logger.info(__("initializing genesis block").green);
self.runCommand(self.client.initGenesisCommmand(), {}, (err, _stdout, _stderr) => {
next(err);
});
Expand Down
4 changes: 2 additions & 2 deletions src/lib/modules/deployment/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class DeployManager {
this.events = embark.events;
this.plugins = options.plugins;
this.blockchain = options.blockchain;
this.gasLimit = false;
this.gasLimit = 6000000;
this.fatalErrors = false;
this.deployOnlyOnConfig = false;
this.onlyCompile = options.onlyCompile !== undefined ? options.onlyCompile : false;
Expand Down Expand Up @@ -81,7 +81,7 @@ class DeployManager {
contractDeploys[className].push(deploy);
});

async.auto(contractDeploys, 1, function(_err, _results) {
async.auto(contractDeploys, function(_err, _results) {
if (errors.length) {
_err = __("Error deploying contracts. Please fix errors to continue.");
self.logger.error(_err);
Expand Down
2 changes: 1 addition & 1 deletion src/lib/modules/specialconfigs/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class SpecialConfigs {
self.events.request('runcode:eval', cmd, (err, result) => {
if (err) {
self.logger.error(params.contract.className + ' deployIf directive has an error; contract will not deploy');
self.logger.error(err);
self.logger.error(err.message || err);
params.shouldDeploy = false;
} else if (!result) {
self.logger.info(params.contract.className + ' deployIf directive returned false; contract will not deploy');
Expand Down
1 change: 1 addition & 0 deletions test_apps/test_app/config/contracts.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ module.exports = {
},
SomeContract: {
deployIf: 'await MyToken.methods.isAvailable().call()',
onDeploy: ['$MyToken'], // Needed because otherwise Embark doesn't know that we depend on MyToken. Would be cleaner with `dependsOn`
args: [
["$MyToken2", "$SimpleStorage"],
100
Expand Down

0 comments on commit 3406ae8

Please sign in to comment.