From 3c31052504fb8a6a88fc761f2028502bcc26979e Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 17 Jun 2020 16:05:16 +0200 Subject: [PATCH] VM: return precompile functions based on hardfork --- packages/vm/lib/evm/evm.ts | 2 +- packages/vm/lib/evm/precompiles/index.ts | 27 ++++++- .../vm/tests/api/evm/precompiles/06-ecadd.js | 2 +- .../vm/tests/api/evm/precompiles/07-ecmul.js | 2 +- .../tests/api/evm/precompiles/08-ecpairing.js | 2 +- .../api/evm/precompiles/hardfork-tests.js | 73 +++++++++++++++++++ packages/vm/tests/api/istanbul/eip-1108.js | 6 +- 7 files changed, 105 insertions(+), 9 deletions(-) create mode 100644 packages/vm/tests/api/evm/precompiles/hardfork-tests.js diff --git a/packages/vm/lib/evm/evm.ts b/packages/vm/lib/evm/evm.ts index d658787d99..f2afa09323 100644 --- a/packages/vm/lib/evm/evm.ts +++ b/packages/vm/lib/evm/evm.ts @@ -342,7 +342,7 @@ export default class EVM { * if no such precompile exists. */ getPrecompile(address: Buffer): PrecompileFunc { - return getPrecompile(address.toString('hex')) + return getPrecompile(address.toString('hex'), this._vm._common) } /** diff --git a/packages/vm/lib/evm/precompiles/index.ts b/packages/vm/lib/evm/precompiles/index.ts index 1e95c6b613..39874758e9 100644 --- a/packages/vm/lib/evm/precompiles/index.ts +++ b/packages/vm/lib/evm/precompiles/index.ts @@ -8,11 +8,16 @@ import { default as p6 } from './06-ecadd' import { default as p7 } from './07-ecmul' import { default as p8 } from './08-ecpairing' import { default as p9 } from './09-blake2f' +import Common from '@ethereumjs/common' interface Precompiles { [key: string]: PrecompileFunc } +interface PrecompileAvailability { + [key: string]: string +} + const ripemdPrecompileAddress = '0000000000000000000000000000000000000003' const precompiles: Precompiles = { '0000000000000000000000000000000000000001': p1, @@ -26,8 +31,26 @@ const precompiles: Precompiles = { '0000000000000000000000000000000000000009': p9, } -function getPrecompile(address: string): PrecompileFunc { - return precompiles[address] +const precompileAvailability: PrecompileAvailability = { + '0000000000000000000000000000000000000001': 'homestead', + '0000000000000000000000000000000000000002': 'homestead', + [ripemdPrecompileAddress]: 'homestead', + '0000000000000000000000000000000000000004': 'homestead', + '0000000000000000000000000000000000000005': 'byzantium', + '0000000000000000000000000000000000000006': 'byzantium', + '0000000000000000000000000000000000000007': 'byzantium', + '0000000000000000000000000000000000000008': 'byzantium', + '0000000000000000000000000000000000000009': 'istanbul', +} + +function getPrecompile(address: string, common: Common): PrecompileFunc { + if (precompiles[address]) { + const availability = precompileAvailability[address] + if (common.gteHardfork(availability)) { + return precompiles[address] + } + } + return precompiles[''] } export { precompiles, getPrecompile, PrecompileFunc, PrecompileInput, ripemdPrecompileAddress } diff --git a/packages/vm/tests/api/evm/precompiles/06-ecadd.js b/packages/vm/tests/api/evm/precompiles/06-ecadd.js index 0b57ee64b9..178b4ec994 100644 --- a/packages/vm/tests/api/evm/precompiles/06-ecadd.js +++ b/packages/vm/tests/api/evm/precompiles/06-ecadd.js @@ -9,7 +9,7 @@ tape('Precompiles: ECADD', (t) => { t.test('ECADD', (st) => { const common = new Common('mainnet', 'petersburg') let vm = new VM({ common: common }) - let ECADD = getPrecompile('0000000000000000000000000000000000000006') + let ECADD = getPrecompile('0000000000000000000000000000000000000006', common) let result = ECADD({ data: Buffer.alloc(0), diff --git a/packages/vm/tests/api/evm/precompiles/07-ecmul.js b/packages/vm/tests/api/evm/precompiles/07-ecmul.js index 5ec9593ad8..578a739855 100644 --- a/packages/vm/tests/api/evm/precompiles/07-ecmul.js +++ b/packages/vm/tests/api/evm/precompiles/07-ecmul.js @@ -9,7 +9,7 @@ tape('Precompiles: ECMUL', (t) => { t.test('ECMUL', (st) => { const common = new Common('mainnet', 'petersburg') let vm = new VM({ common: common }) - let ECMUL = getPrecompile('0000000000000000000000000000000000000007') + let ECMUL = getPrecompile('0000000000000000000000000000000000000007', common) let result = ECMUL({ data: Buffer.alloc(0), diff --git a/packages/vm/tests/api/evm/precompiles/08-ecpairing.js b/packages/vm/tests/api/evm/precompiles/08-ecpairing.js index 3dffac03e1..617e1e8822 100644 --- a/packages/vm/tests/api/evm/precompiles/08-ecpairing.js +++ b/packages/vm/tests/api/evm/precompiles/08-ecpairing.js @@ -9,7 +9,7 @@ tape('Precompiles: ECPAIRING', (t) => { t.test('ECPAIRING', (st) => { const common = new Common('mainnet', 'petersburg') let vm = new VM({ common: common }) - let ECPAIRING = getPrecompile('0000000000000000000000000000000000000008') + let ECPAIRING = getPrecompile('0000000000000000000000000000000000000008', common) let result = ECPAIRING({ data: Buffer.from('00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa', 'hex'), diff --git a/packages/vm/tests/api/evm/precompiles/hardfork-tests.js b/packages/vm/tests/api/evm/precompiles/hardfork-tests.js new file mode 100644 index 0000000000..c014f784ce --- /dev/null +++ b/packages/vm/tests/api/evm/precompiles/hardfork-tests.js @@ -0,0 +1,73 @@ +const BN = require('bn.js') +const tape = require('tape') +const Common = require('@ethereumjs/common').default +const util = require('ethereumjs-util') +const VM = require('../../../../dist/index').default +const { getPrecompile } = require('../../../../dist/evm/precompiles') + +tape('Precompiles: hardfork availability', (t) => { + t.test('Test ECPAIRING availability', async (st) => { + const ECPAIR_Address = "0000000000000000000000000000000000000008" + + // ECPAIR was introduced in Byzantium; check if available from Byzantium. + const commonByzantium = new Common('mainnet', 'byzantium') + let ECPAIRING = getPrecompile(ECPAIR_Address, commonByzantium) + + if (!ECPAIRING) { + st.fail("ECPAIRING is not available in petersburg while it should be available") + } else { + st.pass("ECPAIRING available in petersburg") + } + + let vm = new VM({ common: commonByzantium }) + let result = await vm.runCall({ + caller: Buffer.from('0000000000000000000000000000000000000000', 'hex'), + gasLimit: new BN(0xffffffffff), + to: Buffer.from(ECPAIR_Address, 'hex'), + value: new BN(0) + }) + st.assert(result.gasUsed.toNumber() == 100000) // check that we are using gas (if address would contain no code we use 0 gas) + + + // Check if ECPAIR is available in future hard forks. + const commonPetersburg = new Common('mainnet', 'petersburg') + ECPAIRING = getPrecompile(ECPAIR_Address, commonPetersburg) + + if (!ECPAIRING) { + st.fail("ECPAIRING is not available in petersburg while it should be available") + } else { + st.pass("ECPAIRING available in petersburg") + } + + vm = new VM({ common: commonPetersburg }) + result = await vm.runCall({ + caller: Buffer.from('0000000000000000000000000000000000000000', 'hex'), + gasLimit: new BN(0xffffffffff), + to: Buffer.from(ECPAIR_Address, 'hex'), + value: new BN(0) + }) + st.assert(result.gasUsed.toNumber() == 100000) + + + // Check if ECPAIR is not available in Homestead. + const commonHomestead = new Common('mainnet', 'homestead') + ECPAIRING = getPrecompile(ECPAIR_Address, commonHomestead) + + if (ECPAIRING) { + st.fail("ECPAIRING is available in homestead while it should not be available") + } else { + st.pass("ECPAIRING not available in homestead") + } + + vm = new VM({ common: commonHomestead }) + result = await vm.runCall({ + caller: Buffer.from('0000000000000000000000000000000000000000', 'hex'), + gasLimit: new BN(0xffffffffff), + to: Buffer.from(ECPAIR_Address, 'hex'), + value: new BN(0) + }) + st.assert(result.gasUsed.toNumber() == 0) // check that we use no gas, because we are calling into an address without code. + + st.end() + }) + }) \ No newline at end of file diff --git a/packages/vm/tests/api/istanbul/eip-1108.js b/packages/vm/tests/api/istanbul/eip-1108.js index e3b77384b9..a9321a9f8e 100644 --- a/packages/vm/tests/api/istanbul/eip-1108.js +++ b/packages/vm/tests/api/istanbul/eip-1108.js @@ -9,7 +9,7 @@ tape('Istanbul: EIP-1108 tests', (t) => { t.test('ECADD', (st) => { const common = new Common('mainnet', 'istanbul') let vm = new VM({ common: common }) - let ECADD = getPrecompile('0000000000000000000000000000000000000006') + let ECADD = getPrecompile('0000000000000000000000000000000000000006', common) let result = ECADD({ data: Buffer.alloc(0), @@ -23,7 +23,7 @@ tape('Istanbul: EIP-1108 tests', (t) => { t.test('ECMUL', (st) => { const common = new Common('mainnet', 'istanbul') let vm = new VM({ common: common }) - let ECMUL = getPrecompile('0000000000000000000000000000000000000007') + let ECMUL = getPrecompile('0000000000000000000000000000000000000007', common) let result = ECMUL({ data: Buffer.alloc(0), @@ -37,7 +37,7 @@ tape('Istanbul: EIP-1108 tests', (t) => { t.test('ECPAIRING', (st) => { const common = new Common('mainnet', 'istanbul') let vm = new VM({ common: common }) - let ECPAIRING = getPrecompile('0000000000000000000000000000000000000008') + let ECPAIRING = getPrecompile('0000000000000000000000000000000000000008', common) let result = ECPAIRING({ data: Buffer.from('00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa', 'hex'),