diff --git a/lib/evm/eei.js b/lib/evm/eei.js index 8469e3f053..d43b5116c9 100644 --- a/lib/evm/eei.js +++ b/lib/evm/eei.js @@ -1,3 +1,4 @@ +const promisify = require('util.promisify') const BN = require('bn.js') const { VmError, ERROR } = require('../exceptions') const PStateManager = require('../state/promisified') @@ -34,21 +35,17 @@ module.exports = class EEI { * Returns balance of the given account. * @param {BN} address - Address of account */ - getExternalBalance (address, cb) { + async getExternalBalance (address) { address = addressToBuffer(address) // shortcut if current account if (address.toString('hex') === this._env.address.toString('hex')) { - return cb(null, new BN(this._env.contract.balance)) + return new BN(this._env.contract.balance) } // otherwise load account then return balance - this._env.stateManager.getAccount(address, (err, account) => { - if (err) { - return cb(err, null) - } - cb(null, new BN(account.balance)) - }) + const account = await this._state.getAccount(address) + return new BN(account.balance) } /** @@ -111,26 +108,22 @@ module.exports = class EEI { /** * Get size of an account’s code. * @param {BN} address - Address of account - * @param {function} cb */ - getExternalCodeSize (address, cb) { + async getExternalCodeSize (address) { address = addressToBuffer(address) - this._env.stateManager.getContractCode(address, (err, code) => { - if (err) return cb(err) - cb(null, new BN(code.length)) - }) + const code = await this._state.getContractCode(address) + return new BN(code.length) } /** * Returns code of an account. * @param {BN} address - Address of account - * @param {function} cb */ - getExternalCode (address, cb) { + async getExternalCode (address) { if (!Buffer.isBuffer(address)) { address = addressToBuffer(address) } - this._env.stateManager.getContractCode(address, cb) + return this._state.getContractCode(address) } /** @@ -214,14 +207,10 @@ module.exports = class EEI { /** * Returns Gets the hash of one of the 256 most recent complete blocks. * @param {BN} - Number of block - * @param {function} cb */ - getBlockHash (number, cb) { - this._env.blockchain.getBlock(number, (err, block) => { - if (err) return cb(err) - const blockHash = new BN(block.hash()) - cb(null, blockHash) - }) + async getBlockHash (number) { + const block = await promisify(this._env.blockchain.getBlock).bind(this._env.blockchain)(number) + return new BN(block.hash()) } /** @@ -229,14 +218,10 @@ module.exports = class EEI { * @param {Buffer} key * @param {Buffer} value */ - storageStore (key, value, cb) { - this._state.putContractStorage(this._env.address, key, value) - .then(() => this._state.getAccount(this._env.address)) - .then((account) => { - this._env.contract = account - cb(null) - }) - .catch(cb) + async storageStore (key, value) { + await this._state.putContractStorage(this._env.address, key, value) + const account = await this._state.getAccount(this._env.address) + this._env.contract = account } /** @@ -244,8 +229,8 @@ module.exports = class EEI { * @param {Buffer} key - Storage key * @returns {Buffer} */ - storageLoad (key, cb) { - this._env.stateManager.getContractStorage(this._env.address, key, cb) + async storageLoad (key) { + return this._state.getContractStorage(this._env.address, key) } /** @@ -280,8 +265,8 @@ module.exports = class EEI { * execution will be aborted immediately. * @param {Buffer} toAddress - Beneficiary address */ - selfDestruct (toAddress, cb) { - this._selfDestruct(toAddress).then(() => cb(null)).catch(cb) + async selfDestruct (toAddress) { + return this._selfDestruct(toAddress) } async _selfDestruct (toAddress) { @@ -343,15 +328,8 @@ module.exports = class EEI { * @param {Buffer} address * @param {BN} value * @param {Buffer} data - * @param {Function} cb */ - call (gasLimit, address, value, data, cb) { - this._call(gasLimit, address, value, data) - .then((ret) => cb(null, ret)) - .catch((err) => cb(err, null)) - } - - async _call (gasLimit, address, value, data) { + async call (gasLimit, address, value, data) { const msg = new Message({ caller: this._env.address, gasLimit: gasLimit, @@ -371,15 +349,8 @@ module.exports = class EEI { * @param {Buffer} address * @param {BN} value * @param {Buffer} data - * @param {Function} cb */ - callCode (gasLimit, address, value, data, cb) { - this._callCode(gasLimit, address, value, data) - .then((ret) => cb(null, ret)) - .catch((err) => cb(err, null)) - } - - async _callCode (gasLimit, address, value, data) { + async callCode (gasLimit, address, value, data) { let { compiled, code } = await this._getCode(address) const msg = new Message({ @@ -405,15 +376,8 @@ module.exports = class EEI { * @param {Buffer} address * @param {BN} value * @param {Buffer} data - * @param {Function} cb */ - callStatic (gasLimit, address, value, data, cb) { - this._callStatic(gasLimit, address, value, data) - .then((ret) => cb(null, ret)) - .catch((err) => cb(err, null)) - } - - async _callStatic (gasLimit, address, value, data) { + async callStatic (gasLimit, address, value, data) { const msg = new Message({ caller: this._env.address, gasLimit: gasLimit, @@ -434,15 +398,8 @@ module.exports = class EEI { * @param {Buffer} address * @param {BN} value * @param {Buffer} data - * @param {Function} cb */ - callDelegate (gasLimit, address, value, data, cb) { - this._callDelegate(gasLimit, address, value, data) - .then((ret) => cb(null, ret)) - .catch((err) => cb(err, null)) - } - - async _callDelegate (gasLimit, address, value, data) { + async callDelegate (gasLimit, address, value, data) { let { compiled, code } = await this._getCode(address) const msg = new Message({ @@ -522,15 +479,8 @@ module.exports = class EEI { * @param {BN} gasLimit * @param {BN} value * @param {Buffer} data - * @param {Function} cb */ - create (gasLimit, value, data, cb) { - this._create(gasLimit, value, data) - .then((ret) => cb(null, ret)) - .catch((err) => cb(err, null)) - } - - async _create (gasLimit, value, data, salt = null) { + async create (gasLimit, value, data, salt = null) { const selfdestruct = Object.assign({}, this._env.selfdestruct) const msg = new Message({ caller: this._env.address, @@ -599,12 +549,9 @@ module.exports = class EEI { * @param {BN} value * @param {Buffer} data * @param {Buffer} salt - * @param {Function} cb */ - create2 (gasLimit, value, data, salt, cb) { - this._create(gasLimit, value, data, salt) - .then((ret) => cb(null, ret)) - .catch((err) => cb(err, null)) + async create2 (gasLimit, value, data, salt) { + return this.create(gasLimit, value, data, salt) } } diff --git a/lib/evm/loop.js b/lib/evm/loop.js index 5a8af009f4..f6899b8fa5 100644 --- a/lib/evm/loop.js +++ b/lib/evm/loop.js @@ -136,34 +136,16 @@ module.exports = class Loop { runState.static = prevStatic } - handleOp (runState, opInfo) { - return new Promise((resolve, reject) => { - const opFn = this.getOpHandler(opInfo) - let args = [runState] - // create a callback for async opFunc - if (opInfo.async) { - args.push((err) => { - if (err) return reject(err) - return resolve() - }) - } - - try { - // run the opcode - opFn.apply(null, args) - } catch (e) { - if (e.errorType && e.errorType === 'VmError') { - return reject(e) - } else { - throw e - } - } + async handleOp (runState, opInfo) { + const opFn = this.getOpHandler(opInfo) + let args = [runState] - // call the callback if opFn was sync - if (!opInfo.async) { - return resolve() - } - }) + // run the opcode + if (opInfo.async) { + await opFn.apply(null, args) + } else { + opFn.apply(null, args) + } } getOpHandler (opInfo) { diff --git a/lib/evm/opFns.js b/lib/evm/opFns.js index 3a20786afe..9ec68f6270 100644 --- a/lib/evm/opFns.js +++ b/lib/evm/opFns.js @@ -4,6 +4,7 @@ const BN = utils.BN const exceptions = require('../exceptions.js') const ERROR = exceptions.ERROR const VmError = exceptions.VmError +const PStateManager = require('../state/promisified') const MASK_160 = new BN(1).shln(160).subn(1) // Find Ceil(`this` / `num`) @@ -284,16 +285,10 @@ module.exports = { ADDRESS: function (runState) { runState.stack.push(new BN(runState.eei.getAddress())) }, - BALANCE: function (runState, cb) { + BALANCE: async function (runState) { const address = runState.stack.pop() - runState.eei.getExternalBalance(address, (err, balance) => { - if (err) { - return cb(err) - } - - runState.stack.push(balance) - cb(null) - }) + const balance = await runState.eei.getExternalBalance(address) + runState.stack.push(balance) }, ORIGIN: function (runState) { runState.stack.push(runState.eei.getTxOrigin()) @@ -349,18 +344,12 @@ module.exports = { runState.memory.extend(memOffset, length) runState.memory.write(memOffset, length, data) }, - EXTCODESIZE: function (runState, cb) { + EXTCODESIZE: async function (runState) { const address = runState.stack.pop() - runState.eei.getExternalCodeSize(address, (err, size) => { - if (err) { - return cb(err) - } - - runState.stack.push(size) - cb(null) - }) + const size = await runState.eei.getExternalCodeSize(address) + runState.stack.push(size) }, - EXTCODECOPY: function (runState, cb) { + EXTCODECOPY: async function (runState) { let [address, memOffset, codeOffset, length] = runState.stack.popN(4) // FIXME: for some reason this must come before subGas @@ -368,47 +357,35 @@ module.exports = { // copy fee runState.eei.useGas(new BN(runState._common.param('gasPrices', 'copy')).imul(length.divCeil(new BN(32)))) - runState.eei.getExternalCode(address, (err, code) => { - if (err) { - return cb(err) - } - - const data = getDataSlice(code, codeOffset, length) - memOffset = memOffset.toNumber() - length = length.toNumber() - runState.memory.extend(memOffset, length) - runState.memory.write(memOffset, length, data) + const code = await runState.eei.getExternalCode(address) - cb(null) - }) + const data = getDataSlice(code, codeOffset, length) + memOffset = memOffset.toNumber() + length = length.toNumber() + runState.memory.extend(memOffset, length) + runState.memory.write(memOffset, length, data) }, - EXTCODEHASH: function (runState, cb) { + EXTCODEHASH: async function (runState) { let address = runState.stack.pop() if (!runState._common.gteHardfork('constantinople')) { trap(ERROR.INVALID_OPCODE) } - var stateManager = runState.stateManager + const stateManager = new PStateManager(runState.stateManager) address = addressToBuffer(address) - stateManager.getAccount(address, function (err, account) { - if (err) return cb(err) - - if (account.isEmpty()) { - runState.stack.push(new BN(0)) - return cb(null) - } + const account = await stateManager.getAccount(address) + if (account.isEmpty()) { + runState.stack.push(new BN(0)) + return + } - runState.eei.getExternalCode(address, function (err, code) { - if (err) return cb(err) - if (code.length === 0) { - runState.stack.push(new BN(utils.KECCAK256_NULL)) - return cb(null) - } + const code = await runState.eei.getExternalCode(address) + if (code.length === 0) { + runState.stack.push(new BN(utils.KECCAK256_NULL)) + return + } - runState.stack.push(new BN(utils.keccak256(code))) - return cb(null) - }) - }) + runState.stack.push(new BN(utils.keccak256(code))) }, RETURNDATASIZE: function (runState) { runState.stack.push(runState.eei.getReturnDataSize()) @@ -433,25 +410,18 @@ module.exports = { runState.stack.push(runState.eei.getTxGasPrice()) }, // '0x40' range - block operations - BLOCKHASH: function (runState, cb) { + BLOCKHASH: async function (runState) { const number = runState.stack.pop() const diff = runState.eei.getBlockNumber().sub(number) // block lookups must be within the past 256 blocks if (diff.gtn(256) || diff.lten(0)) { runState.stack.push(new BN(0)) - cb(null) return } - runState.eei.getBlockHash(number, (err, hash) => { - if (err) { - return cb(err) - } - - runState.stack.push(hash) - cb(null) - }) + const hash = await runState.eei.getBlockHash(number) + runState.stack.push(hash) }, COINBASE: function (runState) { runState.stack.push(runState.eei.getBlockCoinbase()) @@ -496,21 +466,15 @@ module.exports = { runState.memory.extend(offset, 1) runState.memory.write(offset, 1, byte) }, - SLOAD: function (runState, cb) { + SLOAD: async function (runState) { let key = runState.stack.pop() key = key.toArrayLike(Buffer, 'be', 32) - runState.eei.storageLoad(key, (err, value) => { - if (err) { - return cb(err) - } - - value = value.length ? new BN(value) : new BN(0) - runState.stack.push(value) - cb(null) - }) + let value = await runState.eei.storageLoad(key) + value = value.length ? new BN(value) : new BN(0) + runState.stack.push(value) }, - SSTORE: function (runState, cb) { + SSTORE: async function (runState) { if (runState.static) { trap(ERROR.STATIC_STATE_CHANGE) } @@ -527,17 +491,9 @@ module.exports = { } // TODO: Replace getContractStorage with EEI method - getContractStorage(runState, runState.eei.getAddress(), key, function (err, found) { - if (err) return cb(err) - try { - updateSstoreGas(runState, found, value) - } catch (e) { - cb(e.error) - return - } - - runState.eei.storageStore(key, value, cb) - }) + const found = await getContractStorage(runState, runState.eei.getAddress(), key) + updateSstoreGas(runState, found, value) + await runState.eei.storageStore(key, value) }, JUMP: function (runState) { let dest = runState.stack.pop() @@ -621,7 +577,7 @@ module.exports = { }, // '0xf0' range - closures - CREATE: function (runState, done) { + CREATE: async function (runState) { if (runState.static) { trap(ERROR.STATIC_STATE_CHANGE) } @@ -637,16 +593,10 @@ module.exports = { data = runState.memory.read(offset.toNumber(), length.toNumber()) } - runState.eei.create(gasLimit, value, data, (err, ret) => { - if (err) { - return done(err) - } - - runState.stack.push(ret) - done(null) - }) + const ret = await runState.eei.create(gasLimit, value, data) + runState.stack.push(ret) }, - CREATE2: function (runState, done) { + CREATE2: async function (runState) { if (!runState._common.gteHardfork('constantinople')) { trap(ERROR.INVALID_OPCODE) } @@ -668,16 +618,10 @@ module.exports = { data = runState.memory.read(offset.toNumber(), length.toNumber()) } - runState.eei.create2(gasLimit, value, data, salt.toArrayLike(Buffer, 'be', 32), (err, ret) => { - if (err) { - return done(err) - } - - runState.stack.push(ret) - done(null) - }) + const ret = await runState.eei.create2(gasLimit, value, data, salt.toArrayLike(Buffer, 'be', 32)) + runState.stack.push(ret) }, - CALL: function (runState, done) { + CALL: async function (runState) { let [gasLimit, toAddress, value, inOffset, inLength, outOffset, outLength] = runState.stack.popN(7) toAddress = addressToBuffer(toAddress) @@ -697,42 +641,25 @@ module.exports = { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()) } - runState.stateManager.accountIsEmpty(toAddress, function (err, empty) { - if (err) { - done(err) - return - } - - if (empty) { - if (!value.isZero()) { - try { - runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount'))) - } catch (e) { - done(e.error) - return - } - } - } - + const state = new PStateManager(runState.stateManager) + const empty = await state.accountIsEmpty(toAddress) + if (empty) { if (!value.isZero()) { - runState.gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend')) - gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend')) + runState.eei.useGas(new BN(runState._common.param('gasPrices', 'callNewAccount'))) } + } - runState.eei.call(gasLimit, toAddress, value, data, (err, ret) => { - if (err) { - return done(err) - } - - // Write return data to memory - writeCallOutput(runState, outOffset, outLength) + if (!value.isZero()) { + runState.gasLeft.iaddn(runState._common.param('gasPrices', 'callStipend')) + gasLimit.iaddn(runState._common.param('gasPrices', 'callStipend')) + } - runState.stack.push(ret) - done(null) - }) - }) + const ret = await runState.eei.call(gasLimit, toAddress, value, data) + // Write return data to memory + writeCallOutput(runState, outOffset, outLength) + runState.stack.push(ret) }, - CALLCODE: function (runState, done) { + CALLCODE: async function (runState) { let [gasLimit, toAddress, value, inOffset, inLength, outOffset, outLength] = runState.stack.popN(7) toAddress = addressToBuffer(toAddress) @@ -752,19 +679,12 @@ module.exports = { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()) } - runState.eei.callCode(gasLimit, toAddress, value, data, (err, ret) => { - if (err) { - return done(err) - } - - // Write return data to memory - writeCallOutput(runState, outOffset, outLength) - - runState.stack.push(ret) - done(null) - }) + const ret = await runState.eei.callCode(gasLimit, toAddress, value, data) + // Write return data to memory + writeCallOutput(runState, outOffset, outLength) + runState.stack.push(ret) }, - DELEGATECALL: function (runState, done) { + DELEGATECALL: async function (runState) { const value = runState.callValue let [gasLimit, toAddress, inOffset, inLength, outOffset, outLength] = runState.stack.popN(6) toAddress = addressToBuffer(toAddress) @@ -778,19 +698,12 @@ module.exports = { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()) } - runState.eei.callDelegate(gasLimit, toAddress, value, data, (err, ret) => { - if (err) { - return done(err) - } - - // Write return data to memory - writeCallOutput(runState, outOffset, outLength) - - runState.stack.push(ret) - done(null) - }) + const ret = await runState.eei.callDelegate(gasLimit, toAddress, value, data) + // Write return data to memory + writeCallOutput(runState, outOffset, outLength) + runState.stack.push(ret) }, - STATICCALL: function (runState, done) { + STATICCALL: async function (runState) { const value = new BN(0) let [gasLimit, toAddress, inOffset, inLength, outOffset, outLength] = runState.stack.popN(6) toAddress = addressToBuffer(toAddress) @@ -804,17 +717,10 @@ module.exports = { data = runState.memory.read(inOffset.toNumber(), inLength.toNumber()) } - runState.eei.callStatic(gasLimit, toAddress, value, data, (err, ret) => { - if (err) { - return done(err) - } - - // Write return data to memory - writeCallOutput(runState, outOffset, outLength) - - runState.stack.push(ret) - done(null) - }) + const ret = await runState.eei.callStatic(gasLimit, toAddress, value, data) + // Write return data to memory + writeCallOutput(runState, outOffset, outLength) + runState.stack.push(ret) }, RETURN: function (runState) { const [offset, length] = runState.stack.popN(2) @@ -836,14 +742,14 @@ module.exports = { runState.eei.revert(returnData) }, // '0x70', range - other - SELFDESTRUCT: function (runState, cb) { + SELFDESTRUCT: async function (runState) { let selfdestructToAddress = runState.stack.pop() if (runState.static) { trap(ERROR.STATIC_STATE_CHANGE) } selfdestructToAddress = addressToBuffer(selfdestructToAddress) - runState.eei.selfDestruct(selfdestructToAddress, cb) + return runState.eei.selfDestruct(selfdestructToAddress) } } @@ -922,12 +828,18 @@ function maxCallGas (gasLimit, gasLeft) { return gasLimit.gt(gasAllowed) ? gasAllowed : gasLimit } -function getContractStorage (runState, address, key, cb) { - if (runState._common.hardfork() === 'constantinople') { - runState.storageReader.getContractStorage(address, key, cb) - } else { - runState.stateManager.getContractStorage(address, key, cb) - } +function getContractStorage (runState, address, key) { + return new Promise((resolve, reject) => { + const cb = (err, res) => { + if (err) return reject(err) + resolve(res) + } + if (runState._common.hardfork() === 'constantinople') { + runState.storageReader.getContractStorage(address, key, cb) + } else { + runState.stateManager.getContractStorage(address, key, cb) + } + }) } function updateSstoreGas (runState, found, value) {