diff --git a/src/app/execution/txRunner.js b/src/app/execution/txRunner.js index 0d48c8e485..efa012a883 100644 --- a/src/app/execution/txRunner.js +++ b/src/app/execution/txRunner.js @@ -43,149 +43,155 @@ TxRunner.prototype.rawRun = function (args, cb) { run(this, args, Date.now(), cb) } -TxRunner.prototype.execute = function (args, callback) { - var self = this - function execute (gasPrice) { - if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice) +function executeTx (tx, gasPrice, api, callback) { + if (gasPrice) tx.gasPrice = executionContext.web3().toHex(gasPrice) - if (self._api.personalMode()) { - modal.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', (value) => { - sendTransaction(executionContext.web3().personal.sendTransaction, tx, value, callback) - }, () => { - return callback('Canceled by user.') - }) - } else { - sendTransaction(executionContext.web3().eth.sendTransaction, tx, null, callback) - } + if (api.personalMode()) { + modal.promptPassphrase(null, 'Personal mode is enabled. Please provide passphrase of account ' + tx.from, '', (value) => { + sendTransaction(executionContext.web3().personal.sendTransaction, tx, value, callback) + }, () => { + return callback('Canceled by user.') + }) + } else { + sendTransaction(executionContext.web3().eth.sendTransaction, tx, null, callback) } +} + +TxRunner.prototype.execute = function (args, callback) { + var self = this - var from = args.from - var to = args.to var data = args.data if (data.slice(0, 2) !== '0x') { data = '0x' + data } - var value = args.value - var gasLimit = args.gasLimit - var tx if (!executionContext.isVM()) { - tx = { - from: from, - to: to, - data: data, - value: value + self.runInNode(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback) + } else { + self.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback) + } +} + +TxRunner.prototype.runInVm = function (from, to, data, value, gasLimit, useCall, callback) { + const self = this + try { + var account = self.vmaccounts[from] + if (!account) { + return callback('Invalid account selected') } + var tx = new EthJSTX({ + nonce: new BN(account.nonce++), + gasPrice: new BN(1), + gasLimit: new BN(gasLimit, 10), + to: to, + value: new BN(value, 10), + data: new Buffer(data.slice(2), 'hex') + }) + tx.sign(account.privateKey) - if (args.useCall) { - tx.gas = gasLimit - executionContext.web3().eth.call(tx, function (error, result) { - callback(error, { - result: result, - transactionHash: result.transactionHash - }) - }) + const coinbases = [ '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e' ] + const difficulties = [ new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10) ] + var block = new EthJSBlock({ + header: { + timestamp: new Date().getTime() / 1000 | 0, + number: self.blockNumber, + coinbase: coinbases[self.blockNumber % coinbases.length], + difficulty: difficulties[self.blockNumber % difficulties.length], + gasLimit: new BN(gasLimit, 10).imuln(2) + }, + transactions: [], + uncleHeaders: [] + }) + if (!useCall) { + ++self.blockNumber } else { - executionContext.web3().eth.estimateGas(tx, function (err, gasEstimation) { - if (err) { - return callback(err, gasEstimation) - } - var blockGasLimit = executionContext.currentblockGasLimit() - // NOTE: estimateGas very likely will return a large limit if execution of the code failed - // we want to be able to run the code in order to debug and find the cause for the failure + executionContext.vm().stateManager.checkpoint() + } - var warnEstimation = " An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that's also the reason of strong gas estimation)." - if (gasEstimation > gasLimit) { - return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation) - } - if (gasEstimation > blockGasLimit) { - return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation) - } + executionContext.vm().runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) { + if (useCall) { + executionContext.vm().stateManager.revert(function () {}) + } + err = err ? err.message : err + result.status = '0x' + result.vm.exception.toString(16) + callback(err, { + result: result, + transactionHash: ethJSUtil.bufferToHex(new Buffer(tx.hash())) + }) + }) + } catch (e) { + callback(e, null) + } +} - tx.gas = gasEstimation +TxRunner.prototype.runInNode = function (from, to, data, value, gasLimit, useCall, callback) { + const self = this + var tx = { + from: from, + to: to, + data: data, + value: value + } - if (!self._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { - self._api.detectNetwork((err, network) => { - if (err) { - console.log(err) - } else { - if (network.name === 'Main') { - var content = confirmDialog(tx, gasEstimation, self) - modalDialog('Confirm transaction', content, - { label: 'Confirm', - fn: () => { - self._api.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) - if (!content.gasPriceStatus) { - callback('Given gas grice is not correct') - } else { - var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') - execute(gasPrice) - } - }}, { - label: 'Cancel', - fn: () => { - return callback('Transaction canceled by user.') - } - }) - } else { - execute() - } - } - }) - } else { - execute() - } + if (useCall) { + tx.gas = gasLimit + executionContext.web3().eth.call(tx, function (error, result) { + callback(error, { + result: result, + transactionHash: result.transactionHash }) - } + }) } else { - try { - var account = self.vmaccounts[from] - if (!account) { - return callback('Invalid account selected') + executionContext.web3().eth.estimateGas(tx, function (err, gasEstimation) { + if (err) { + return callback(err, gasEstimation) } - tx = new EthJSTX({ - nonce: new BN(account.nonce++), - gasPrice: new BN(1), - gasLimit: new BN(gasLimit, 10), - to: to, - value: new BN(value, 10), - data: new Buffer(data.slice(2), 'hex') - }) - tx.sign(account.privateKey) + var blockGasLimit = executionContext.currentblockGasLimit() + // NOTE: estimateGas very likely will return a large limit if execution of the code failed + // we want to be able to run the code in order to debug and find the cause for the failure - const coinbases = [ '0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e' ] - const difficulties = [ new BN('69762765929000', 10), new BN('70762765929000', 10), new BN('71762765929000', 10) ] - var block = new EthJSBlock({ - header: { - timestamp: new Date().getTime() / 1000 | 0, - number: self.blockNumber, - coinbase: coinbases[self.blockNumber % coinbases.length], - difficulty: difficulties[self.blockNumber % difficulties.length], - gasLimit: new BN(gasLimit, 10).imuln(2) - }, - transactions: [], - uncleHeaders: [] - }) - if (!args.useCall) { - ++self.blockNumber - } else { - executionContext.vm().stateManager.checkpoint() + var warnEstimation = " An important gas estimation might also be the sign of a problem in the contract code. Please check loops and be sure you did not sent value to a non payable function (that's also the reason of strong gas estimation)." + if (gasEstimation > gasLimit) { + return callback('Gas required exceeds limit: ' + gasLimit + '. ' + warnEstimation) } + if (gasEstimation > blockGasLimit) { + return callback('Gas required exceeds block gas limit: ' + gasLimit + '. ' + warnEstimation) + } + + tx.gas = gasEstimation - executionContext.vm().runTx({block: block, tx: tx, skipBalance: true, skipNonce: true}, function (err, result) { - if (args.useCall) { - executionContext.vm().stateManager.revert(function () {}) - } - err = err ? err.message : err - result.status = '0x' + result.vm.exception.toString(16) - callback(err, { - result: result, - transactionHash: ethJSUtil.bufferToHex(new Buffer(tx.hash())) + if (!self._api.config.getUnpersistedProperty('doNotShowTransactionConfirmationAgain')) { + self._api.detectNetwork((err, network) => { + if (err) { + console.log(err) + } else { + if (network.name === 'Main') { + var content = confirmDialog(tx, gasEstimation, self) + modalDialog('Confirm transaction', content, + { label: 'Confirm', + fn: () => { + self._api.config.setUnpersistedProperty('doNotShowTransactionConfirmationAgain', content.querySelector('input#confirmsetting').checked) + if (!content.gasPriceStatus) { + callback('Given gas grice is not correct') + } else { + var gasPrice = executionContext.web3().toWei(content.querySelector('#gasprice').value, 'gwei') + executeTx(tx, gasPrice, self._api, callback) + } + }}, { + label: 'Cancel', + fn: () => { + return callback('Transaction canceled by user.') + } + }) + } else { + executeTx(tx, null, self._api, callback) + } + } }) - }) - } catch (e) { - callback(e, null) - } + } else { + executeTx(tx, null, self._api, callback) + } + }) } }