diff --git a/lib/evm/eei.ts b/lib/evm/eei.ts index e0000e9d3f..991d306469 100644 --- a/lib/evm/eei.ts +++ b/lib/evm/eei.ts @@ -115,6 +115,13 @@ export default class EEI { return new BN(account.balance) } + /** + * Returns balance of self. + */ + getSelfBalance(): BN { + return new BN(this._env.contract.balance) + } + /** * Returns caller address. This is the address of the account * that is directly responsible for this execution. diff --git a/lib/evm/opFns.ts b/lib/evm/opFns.ts index 043d685b07..9c7901b748 100644 --- a/lib/evm/opFns.ts +++ b/lib/evm/opFns.ts @@ -463,6 +463,13 @@ export const handlers: { [k: string]: OpHandler } = { runState.stack.push(runState.eei.getChainId()) }, + SELFBALANCE: function(runState: RunState) { + if (!runState._common.gteHardfork('istanbul')) { + trap(ERROR.INVALID_OPCODE) + } + + runState.stack.push(runState.eei.getSelfBalance()) + }, // 0x50 range - 'storage' and execution POP: function(runState: RunState) { runState.stack.pop() diff --git a/lib/evm/opcodes.ts b/lib/evm/opcodes.ts index dbaa867121..c2e4c65571 100644 --- a/lib/evm/opcodes.ts +++ b/lib/evm/opcodes.ts @@ -170,7 +170,11 @@ let codes: any = { } const istanbulOpcodes: any = { + 0x31: ['BALANCE', 700, true], + 0x3f: ['EXTCODEHASH', 700, true], 0x46: ['CHAINID', 2, false], + 0x47: ['SELFBALANCE', 5, false], + 0x54: ['SLOAD', 800, true], } export function setOpcodes(hf: string) { diff --git a/tests/api/istanbul/eip-1884.js b/tests/api/istanbul/eip-1884.js new file mode 100644 index 0000000000..a3720dbae0 --- /dev/null +++ b/tests/api/istanbul/eip-1884.js @@ -0,0 +1,44 @@ +const tape = require('tape') +const BN = require('bn.js') +const Common = require('ethereumjs-common').default +const VM = require('../../../dist/index').default +const PStateManager = require('../../../dist/state/promisified').default +const { ERROR } = require('../../../dist/exceptions') +const { createAccount } = require('../utils') + +const testCases = [ + { chain: 'mainnet', hardfork: 'istanbul', selfbalance: '0xf1' }, + { chain: 'mainnet', hardfork: 'constantinople', err: ERROR.INVALID_OPCODE } +] + +// SELFBALANCE PUSH8 0x00 MSTORE8 PUSH8 0x01 PUSH8 0x00 RETURN +const code = ['47', '60', '00', '53', '60', '01', '60', '00', 'f3'] +tape('Istanbul: EIP-1884: SELFBALANCE', async (t) => { + const addr = Buffer.from('00000000000000000000000000000000000000ff', 'hex') + const runCodeArgs = { + code: Buffer.from(code.join(''), 'hex'), + gasLimit: new BN(0xffff), + address: addr + } + + for (const testCase of testCases) { + const common = new Common(testCase.chain, testCase.hardfork) + const vm = new VM({ common }) + const state = new PStateManager(vm.stateManager) + const account = createAccount('00', testCase.selfbalance) + await state.putAccount(addr, account) + try { + const res = await vm.runCode(runCodeArgs) + if (testCase.err) { + t.equal(res.exceptionError.error, testCase.err) + } else { + t.assert(res.exceptionError === undefined) + t.assert(new BN(Buffer.from(testCase.selfbalance.slice(2), 'hex')).eq(new BN(res.returnValue))) + } + } catch (e) { + t.fail(e.message) + } + } + + t.end() +})