From a149785052b44577cccf4df5626204a41d649db5 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 28 Dec 2021 16:21:41 +0100 Subject: [PATCH 01/10] vm/tests: add support for test networks with activated EIPs --- packages/vm/tests/tester/config.ts | 16 ++++++++++++++-- packages/vm/tests/tester/index.ts | 21 +++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/vm/tests/tester/config.ts b/packages/vm/tests/tester/config.ts index 782ca52880..83cc61f42f 100644 --- a/packages/vm/tests/tester/config.ts +++ b/packages/vm/tests/tester/config.ts @@ -258,7 +258,12 @@ export function getTestDirs(network: string, testType: string) { * @param {String} network - the network field of a test * @returns {Common} the Common which should be used */ -export function getCommon(network: string) { +export function getCommon(targetNetwork: string) { + let network = targetNetwork + if (network.includes('+')) { + const index = network.indexOf('+') + network = network.slice(0, index) + } const networkLowercase = network.toLowerCase() if (normalHardforks.map((str) => str.toLowerCase()).includes(networkLowercase)) { // normal hard fork, return the common with this hard fork @@ -289,13 +294,20 @@ export function getCommon(network: string) { }) } } - return Common.forCustomChain( + console.log(network) + console.log(hfName) + const common = Common.forCustomChain( 'mainnet', { hardforks: testHardforks, }, hfName ) + const eips = targetNetwork.match(/(?<=\+)(.\d+)/g) + if (eips) { + common.setEIPs(eips.map((e: string) => parseInt(e))) + } + return common } else { // this is not a "default fork" network, but it is a "transition" network. we will test the VM if it transitions the right way const transitionForks = diff --git a/packages/vm/tests/tester/index.ts b/packages/vm/tests/tester/index.ts index d850e5d062..d4e5595340 100755 --- a/packages/vm/tests/tester/index.ts +++ b/packages/vm/tests/tester/index.ts @@ -91,6 +91,27 @@ async function runTests() { runnerArgs.value = argv.value // GeneralStateTests runnerArgs.debug = argv.debug // BlockchainTests + /** + * Edit the forkConfig string to ensure it works with RegEx (escape + characters) + */ + + if (testGetterArgs.forkConfig.includes('+')) { + let str = testGetterArgs.forkConfig + const indicies = [] + for (let i = 0; i < str.length; i++) { + if (str[i] == '+') { + indicies.push(i) + } + } + // traverse array in reverse order to ensure indicies match when we replace the '+' with '/+' + for (let i = indicies.length - 1; i >= 0; i--) { + str = `${str.substr(0, indicies[i])}\\${str.substr(indicies[i])}` + } + testGetterArgs.forkConfig = str + } + + console.log(testGetterArgs.forkConfig) + let expectedTests: number | undefined if (argv['verify-test-amount-alltests']) { expectedTests = getExpectedTests(FORK_CONFIG_VM, name) From a21c434df87e37da23105f40fcbf76eaf89c706d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 28 Dec 2021 23:47:35 +0100 Subject: [PATCH 02/10] vm/tests: remove console.log --- packages/vm/tests/tester/config.ts | 2 -- packages/vm/tests/tester/index.ts | 2 -- 2 files changed, 4 deletions(-) diff --git a/packages/vm/tests/tester/config.ts b/packages/vm/tests/tester/config.ts index 83cc61f42f..e65c721f7c 100644 --- a/packages/vm/tests/tester/config.ts +++ b/packages/vm/tests/tester/config.ts @@ -294,8 +294,6 @@ export function getCommon(targetNetwork: string) { }) } } - console.log(network) - console.log(hfName) const common = Common.forCustomChain( 'mainnet', { diff --git a/packages/vm/tests/tester/index.ts b/packages/vm/tests/tester/index.ts index d4e5595340..9393bbf79f 100755 --- a/packages/vm/tests/tester/index.ts +++ b/packages/vm/tests/tester/index.ts @@ -110,8 +110,6 @@ async function runTests() { testGetterArgs.forkConfig = str } - console.log(testGetterArgs.forkConfig) - let expectedTests: number | undefined if (argv['verify-test-amount-alltests']) { expectedTests = getExpectedTests(FORK_CONFIG_VM, name) From 5487b015eed824aae669918f83792010f4b57028 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 29 Dec 2021 22:35:15 +0100 Subject: [PATCH 03/10] tx/common: start of EIP3860 implementation --- packages/common/src/eips/3860.json | 23 +++++++++++++++++++++++ packages/common/src/eips/index.ts | 1 + packages/tx/src/baseTransaction.ts | 12 ++++++++++-- 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 packages/common/src/eips/3860.json diff --git a/packages/common/src/eips/3860.json b/packages/common/src/eips/3860.json new file mode 100644 index 0000000000..a9a29a9b40 --- /dev/null +++ b/packages/common/src/eips/3860.json @@ -0,0 +1,23 @@ +{ + "name": "EIP-3860", + "number": 3860, + "comment": "Limit and meter initcode", + "url": "https://eips.ethereum.org/EIPS/eip-3860", + "status": "Review", + "minimumHardfork": "spuriousDragon", + "requiredEIPs": [], + "gasConfig": {}, + "gasPrices": { + "initCodeWordCost": { + "v": 2, + "d": "Gas to pay for each word (32 bytes) of initcode when creating a contract" + } + }, + "vm": { + "maxInitCodeSize": { + "v": 49152, + "d": "Maximum length of initialization code when creating a contract" + } + }, + "pow": {} + } \ No newline at end of file diff --git a/packages/common/src/eips/index.ts b/packages/common/src/eips/index.ts index 95154e96b2..cb41b8c18a 100644 --- a/packages/common/src/eips/index.ts +++ b/packages/common/src/eips/index.ts @@ -14,5 +14,6 @@ export const EIPs: eipsType = { 3554: require('./3554.json'), 3675: require('./3675.json'), 3855: require('./3855.json'), + 3860: require('./3860.json'), 4345: require('./4345.json'), } diff --git a/packages/tx/src/baseTransaction.ts b/packages/tx/src/baseTransaction.ts index 6917a6068b..d540bf2965 100644 --- a/packages/tx/src/baseTransaction.ts +++ b/packages/tx/src/baseTransaction.ts @@ -187,12 +187,20 @@ export abstract class BaseTransaction { const txDataZero = this.common.param('gasPrices', 'txDataZero') const txDataNonZero = this.common.param('gasPrices', 'txDataNonZero') - let cost = 0 + + let cost: number | BN = 0 for (let i = 0; i < this.data.length; i++) { this.data[i] === 0 ? (cost += txDataZero) : (cost += txDataNonZero) } - return new BN(cost) + cost = new BN(cost) + if ( (this.to === undefined || this.to === null) && this.common.isActivatedEIP(3860)) { + const dataLength = Math.ceil(this.data.length / 32) + const initCodeCost = new BN(this.common.param('gasPrices', 'initCodeWordCost')).imuln(dataLength) + cost.iadd(initCodeCost) + } + + return cost } /** From 1dd56872317b4d0304c82fd8c070edf91ed0b775 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 30 Dec 2021 15:12:19 +0100 Subject: [PATCH 04/10] tx: add eip3860 --- packages/tx/src/baseTransaction.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/tx/src/baseTransaction.ts b/packages/tx/src/baseTransaction.ts index d540bf2965..f9e07ce1f2 100644 --- a/packages/tx/src/baseTransaction.ts +++ b/packages/tx/src/baseTransaction.ts @@ -187,16 +187,17 @@ export abstract class BaseTransaction { const txDataZero = this.common.param('gasPrices', 'txDataZero') const txDataNonZero = this.common.param('gasPrices', 'txDataNonZero') - let cost: number | BN = 0 for (let i = 0; i < this.data.length; i++) { this.data[i] === 0 ? (cost += txDataZero) : (cost += txDataNonZero) } cost = new BN(cost) - if ( (this.to === undefined || this.to === null) && this.common.isActivatedEIP(3860)) { + if ((this.to === undefined || this.to === null) && this.common.isActivatedEIP(3860)) { const dataLength = Math.ceil(this.data.length / 32) - const initCodeCost = new BN(this.common.param('gasPrices', 'initCodeWordCost')).imuln(dataLength) + const initCodeCost = new BN(this.common.param('gasPrices', 'initCodeWordCost')).imuln( + dataLength + ) cost.iadd(initCodeCost) } From ab732568e2df43237834e2dd6634b74399e6078f Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Thu, 30 Dec 2021 15:12:28 +0100 Subject: [PATCH 05/10] vm: add eip3860 --- packages/vm/src/evm/eei.ts | 15 +++++++++++++++ packages/vm/src/evm/evm.ts | 14 ++++++++++++++ packages/vm/src/exceptions.ts | 1 + packages/vm/src/index.ts | 3 ++- 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/vm/src/evm/eei.ts b/packages/vm/src/evm/eei.ts index 97935880d9..449840c0b7 100644 --- a/packages/vm/src/evm/eei.ts +++ b/packages/vm/src/evm/eei.ts @@ -605,6 +605,21 @@ export default class EEI { // this should always be safe this.useGas(results.gasUsed, 'CREATE') + // EIP 3860 checks + if (this._common.isActivatedEIP(3860)) { + if (msg.data.length > this._common.param('vm', 'maxInitCodeSize')) { + trap(ERROR.INITCODE_SIZE_VIOLATION) + } + + // Meter initcode + // Note: this is not charged when creating a contract at tx-level. This is charged as upfront cost in the tx + // This is thus only ran if CREATE/CREATE2 is used + const initCodeCost = new BN(this._common.param('gasPrices', 'initCodeWordCost')).imuln( + Math.ceil(msg.data.length / 32) + ) + this.useGas(initCodeCost, 'Initcode cost') + } + // Set return buffer in case revert happened if ( results.execResult.exceptionError && diff --git a/packages/vm/src/evm/evm.ts b/packages/vm/src/evm/evm.ts index f4cd15fdfe..edb6eda772 100644 --- a/packages/vm/src/evm/evm.ts +++ b/packages/vm/src/evm/evm.ts @@ -300,6 +300,20 @@ export default class EVM { // Reduce tx value from sender await this._reduceSenderBalance(account, message) + if (this._vm._common.isActivatedEIP(3860)) { + if (message.data.length > this._vm._common.param('vm', 'maxInitCodeSize')) { + return { + gasUsed: message.gasLimit, + createdAddress: message.to, + execResult: { + returnValue: Buffer.alloc(0), + exceptionError: new VmError(ERROR.INITCODE_SIZE_VIOLATION), + gasUsed: message.gasLimit, + }, + } + } + } + message.code = message.data message.data = Buffer.alloc(0) message.to = await this._generateAddress(message) diff --git a/packages/vm/src/exceptions.ts b/packages/vm/src/exceptions.ts index c1a6c616c3..541ad114c4 100644 --- a/packages/vm/src/exceptions.ts +++ b/packages/vm/src/exceptions.ts @@ -17,6 +17,7 @@ export enum ERROR { INVALID_RETURNSUB = 'invalid RETURNSUB', INVALID_JUMPSUB = 'invalid JUMPSUB', INVALID_BYTECODE_RESULT = 'invalid bytecode deployed', + INITCODE_SIZE_VIOLATION = 'initcode exceeds max initcode size', // BLS errors BLS_12_381_INVALID_INPUT_LENGTH = 'invalid input length', diff --git a/packages/vm/src/index.ts b/packages/vm/src/index.ts index 00168b970f..55afbbd864 100644 --- a/packages/vm/src/index.ts +++ b/packages/vm/src/index.ts @@ -52,6 +52,7 @@ export interface VMOpts { * - [EIP-3529](https://eips.ethereum.org/EIPS/eip-3529) - Reduction in refunds * - [EIP-3541](https://eips.ethereum.org/EIPS/eip-3541) - Reject new contracts starting with the 0xEF byte * - [EIP-3855](https://eips.ethereum.org/EIPS/eip-3855) - PUSH0 instruction + * - [EIP-3860](https://eips.ethereum.org/EIPS/eip-3860) - Limit and meter initcode * * *Annotations:* * @@ -195,7 +196,7 @@ export default class VM extends AsyncEventEmitter { if (opts.common) { //EIPs - const supportedEIPs = [1559, 2315, 2537, 2565, 2718, 2929, 2930, 3198, 3529, 3541, 3855] + const supportedEIPs = [1559, 2315, 2537, 2565, 2718, 2929, 2930, 3198, 3529, 3541, 3855, 3860] for (const eip of opts.common.eips()) { if (!supportedEIPs.includes(eip)) { throw new Error(`${eip} is not supported by the VM`) From b0d8b5b23b645732d92ebf1c866de8403dca8e74 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Tue, 8 Feb 2022 20:57:43 +0100 Subject: [PATCH 06/10] vm: fix eip3860 tests --- packages/vm/src/evm/eei.ts | 23 ++++++++--------------- packages/vm/src/evm/opcodes/gas.ts | 17 +++++++++++++++++ packages/vm/src/index.ts | 4 +++- packages/vm/tests/tester/index.ts | 19 ------------------- 4 files changed, 28 insertions(+), 35 deletions(-) diff --git a/packages/vm/src/evm/eei.ts b/packages/vm/src/evm/eei.ts index 449840c0b7..68f0fc1e32 100644 --- a/packages/vm/src/evm/eei.ts +++ b/packages/vm/src/evm/eei.ts @@ -593,9 +593,17 @@ export default class EEI { if (this._env.contract.nonce.gte(MAX_UINT64)) { return new BN(0) } + this._env.contract.nonce.iaddn(1) await this._state.putAccount(this._env.address, this._env.contract) + if (this._common.isActivatedEIP(3860)) { + if (msg.data.length > this._common.param('vm', 'maxInitCodeSize')) { + this.useGas(gasLimit) + return new BN(0) + } + } + const results = await this._evm.executeMessage(msg) if (results.execResult.logs) { @@ -605,21 +613,6 @@ export default class EEI { // this should always be safe this.useGas(results.gasUsed, 'CREATE') - // EIP 3860 checks - if (this._common.isActivatedEIP(3860)) { - if (msg.data.length > this._common.param('vm', 'maxInitCodeSize')) { - trap(ERROR.INITCODE_SIZE_VIOLATION) - } - - // Meter initcode - // Note: this is not charged when creating a contract at tx-level. This is charged as upfront cost in the tx - // This is thus only ran if CREATE/CREATE2 is used - const initCodeCost = new BN(this._common.param('gasPrices', 'initCodeWordCost')).imuln( - Math.ceil(msg.data.length / 32) - ) - this.useGas(initCodeCost, 'Initcode cost') - } - // Set return buffer in case revert happened if ( results.execResult.exceptionError && diff --git a/packages/vm/src/evm/opcodes/gas.ts b/packages/vm/src/evm/opcodes/gas.ts index 4d5eaf40ce..471b3ab741 100644 --- a/packages/vm/src/evm/opcodes/gas.ts +++ b/packages/vm/src/evm/opcodes/gas.ts @@ -262,6 +262,14 @@ export const dynamicGasHandlers: Map = new Map< gas.iadd(subMemUsage(runState, offset, length, common)) + if (common.isActivatedEIP(3860)) { + // Meter initcode + const initCodeCost = new BN(common.param('gasPrices', 'initCodeWordCost')).imul( + length.addn(31).divn(32) + ) + gas.iadd(initCodeCost) + } + let gasLimit = new BN(runState.eei.getGasLeft().isub(gas)) gasLimit = maxCallGas(gasLimit.clone(), gasLimit.clone(), runState, common) @@ -419,6 +427,15 @@ export const dynamicGasHandlers: Map = new Map< } gas.iadd(new BN(common.param('gasPrices', 'sha3Word')).imul(divCeil(length, new BN(32)))) + + if (common.isActivatedEIP(3860)) { + // Meter initcode + const initCodeCost = new BN(common.param('gasPrices', 'initCodeWordCost')).imul( + length.addn(31).divn(32) + ) + gas.iadd(initCodeCost) + } + let gasLimit = new BN(runState.eei.getGasLeft().isub(gas)) gasLimit = maxCallGas(gasLimit.clone(), gasLimit.clone(), runState, common) // CREATE2 is only available after TangerineWhistle (Constantinople introduced this opcode) runState.messageGasLimit = gasLimit diff --git a/packages/vm/src/index.ts b/packages/vm/src/index.ts index eba4b753b3..634c229bb2 100644 --- a/packages/vm/src/index.ts +++ b/packages/vm/src/index.ts @@ -196,7 +196,9 @@ export default class VM extends AsyncEventEmitter { if (opts.common) { // Supported EIPs - const supportedEIPs = [1559, 2315, 2537, 2565, 2718, 2929, 2930, 3198, 3529, 3541, 3607, 3855, 3860] + const supportedEIPs = [ + 1559, 2315, 2537, 2565, 2718, 2929, 2930, 3198, 3529, 3541, 3607, 3855, 3860, + ] for (const eip of opts.common.eips()) { if (!supportedEIPs.includes(eip)) { throw new Error(`EIP-${eip} is not supported by the VM`) diff --git a/packages/vm/tests/tester/index.ts b/packages/vm/tests/tester/index.ts index 7a0174ef5e..f9b1e26f05 100755 --- a/packages/vm/tests/tester/index.ts +++ b/packages/vm/tests/tester/index.ts @@ -111,25 +111,6 @@ async function runTests() { testGetterArgs.forkConfig = str } - /** - * Edit the forkConfig string to ensure it works with RegEx (escape + characters) - */ - - if (testGetterArgs.forkConfig.includes('+')) { - let str = testGetterArgs.forkConfig - const indicies = [] - for (let i = 0; i < str.length; i++) { - if (str[i] == '+') { - indicies.push(i) - } - } - // traverse array in reverse order to ensure indicies match when we replace the '+' with '/+' - for (let i = indicies.length - 1; i >= 0; i--) { - str = `${str.substr(0, indicies[i])}\\${str.substr(indicies[i])}` - } - testGetterArgs.forkConfig = str - } - let expectedTests: number | undefined if (argv['verify-test-amount-alltests']) { expectedTests = getExpectedTests(FORK_CONFIG_VM, name) From 3a67bd4aa65fe4208fd32b1a80a203d1f777d7dd Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 9 Feb 2022 17:24:37 +0100 Subject: [PATCH 07/10] vm: update eip3860 to latest spec --- packages/vm/src/evm/eei.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/vm/src/evm/eei.ts b/packages/vm/src/evm/eei.ts index 68f0fc1e32..78519addf5 100644 --- a/packages/vm/src/evm/eei.ts +++ b/packages/vm/src/evm/eei.ts @@ -599,7 +599,6 @@ export default class EEI { if (this._common.isActivatedEIP(3860)) { if (msg.data.length > this._common.param('vm', 'maxInitCodeSize')) { - this.useGas(gasLimit) return new BN(0) } } From e566dc2b78352c15b8c6961f8b74ad7d65220705 Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Wed, 2 Mar 2022 22:05:58 +0100 Subject: [PATCH 08/10] tx: add 3860 tests + fix --- packages/tx/src/eip1559Transaction.ts | 10 ++++++++++ packages/tx/src/eip2930Transaction.ts | 10 ++++++++++ packages/tx/src/legacyTransaction.ts | 10 ++++++++++ packages/tx/test/transactionRunner.ts | 13 +++++++++++++ packages/tx/test/types.ts | 1 + 5 files changed, 44 insertions(+) diff --git a/packages/tx/src/eip1559Transaction.ts b/packages/tx/src/eip1559Transaction.ts index 032acebce5..94e3d38cdd 100644 --- a/packages/tx/src/eip1559Transaction.ts +++ b/packages/tx/src/eip1559Transaction.ts @@ -235,6 +235,16 @@ export default class FeeMarketEIP1559Transaction extends BaseTransaction this.common.param('vm', 'maxInitCodeSize')) { + throw new Error( + `the initcode size of this transaction is too large: it is ${ + this.data.length + } while the max is ${this.common.param('vm', 'maxInitCodeSize')}` + ) + } + } + const freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) diff --git a/packages/tx/src/eip2930Transaction.ts b/packages/tx/src/eip2930Transaction.ts index b62d82b522..ac72fdc348 100644 --- a/packages/tx/src/eip2930Transaction.ts +++ b/packages/tx/src/eip2930Transaction.ts @@ -212,6 +212,16 @@ export default class AccessListEIP2930Transaction extends BaseTransaction this.common.param('vm', 'maxInitCodeSize')) { + throw new Error( + `the initcode size of this transaction is too large: it is ${ + this.data.length + } while the max is ${this.common.param('vm', 'maxInitCodeSize')}` + ) + } + } + const freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) diff --git a/packages/tx/src/legacyTransaction.ts b/packages/tx/src/legacyTransaction.ts index 909bc13283..5e79ce441c 100644 --- a/packages/tx/src/legacyTransaction.ts +++ b/packages/tx/src/legacyTransaction.ts @@ -135,6 +135,16 @@ export default class Transaction extends BaseTransaction { } } + if (this.common.isActivatedEIP(3860)) { + if (this.data.length > this.common.param('vm', 'maxInitCodeSize')) { + throw new Error( + `the initcode size of this transaction is too large: it is ${ + this.data.length + } while the max is ${this.common.param('vm', 'maxInitCodeSize')}` + ) + } + } + const freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) diff --git a/packages/tx/test/transactionRunner.ts b/packages/tx/test/transactionRunner.ts index b70af21af2..2d1127763e 100644 --- a/packages/tx/test/transactionRunner.ts +++ b/packages/tx/test/transactionRunner.ts @@ -10,6 +10,7 @@ const argv = minimist(process.argv.slice(2)) const file: string | undefined = argv.file const forkNames: ForkName[] = [ + 'London+3860', 'London', 'Berlin', 'Istanbul', @@ -23,6 +24,7 @@ const forkNames: ForkName[] = [ ] const forkNameMap: ForkNamesMap = { + 'London+3860': 'london', London: 'london', Berlin: 'berlin', Istanbul: 'istanbul', @@ -35,6 +37,10 @@ const forkNameMap: ForkNamesMap = { Homestead: 'homestead', } +const EIPs: any = { + 'London+3860': [3860], +} + tape('TransactionTests', async (t) => { const fileFilterRegex = file ? new RegExp(file + '[^\\w]') : undefined await getTests( @@ -46,6 +52,9 @@ tape('TransactionTests', async (t) => { ) => { t.test(testName, (st) => { for (const forkName of forkNames) { + if (testData.result[forkName] === undefined) { + continue + } const forkTestData = testData.result[forkName] const shouldBeInvalid = !!forkTestData.exception @@ -53,6 +62,10 @@ tape('TransactionTests', async (t) => { const rawTx = toBuffer(testData.txbytes) const hardfork = forkNameMap[forkName] const common = new Common({ chain: 1, hardfork }) + const activateEIPs = EIPs[forkName] + if (activateEIPs) { + common.setEIPs(activateEIPs) + } const tx = Transaction.fromSerializedTx(rawTx, { common }) const sender = tx.getSenderAddress().toString() const hash = tx.hash().toString('hex') diff --git a/packages/tx/test/types.ts b/packages/tx/test/types.ts index b98a45b4ca..c37d36a18b 100644 --- a/packages/tx/test/types.ts +++ b/packages/tx/test/types.ts @@ -1,4 +1,5 @@ export type ForkName = + | 'London+3860' | 'London' | 'Berlin' | 'Istanbul' From 26a9ae34bcd70c4e953e0a0024528266fba0a29d Mon Sep 17 00:00:00 2001 From: Jochem Brouwer Date: Mon, 7 Mar 2022 19:11:22 +0100 Subject: [PATCH 09/10] tx: move checks to util --- packages/tx/src/eip1559Transaction.ts | 10 ++-------- packages/tx/src/eip2930Transaction.ts | 11 ++--------- packages/tx/src/legacyTransaction.ts | 9 ++------- packages/tx/src/util.ts | 11 +++++++++++ 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/packages/tx/src/eip1559Transaction.ts b/packages/tx/src/eip1559Transaction.ts index 94e3d38cdd..4a85dec630 100644 --- a/packages/tx/src/eip1559Transaction.ts +++ b/packages/tx/src/eip1559Transaction.ts @@ -20,7 +20,7 @@ import { N_DIV_2, TxOptions, } from './types' -import { AccessLists } from './util' +import { AccessLists, checkMaxInitCodeSize } from './util' const TRANSACTION_TYPE = 2 const TRANSACTION_TYPE_BUFFER = Buffer.from(TRANSACTION_TYPE.toString(16).padStart(2, '0'), 'hex') @@ -236,13 +236,7 @@ export default class FeeMarketEIP1559Transaction extends BaseTransaction this.common.param('vm', 'maxInitCodeSize')) { - throw new Error( - `the initcode size of this transaction is too large: it is ${ - this.data.length - } while the max is ${this.common.param('vm', 'maxInitCodeSize')}` - ) - } + checkMaxInitCodeSize(this.common, this.data.length) } const freeze = opts?.freeze ?? true diff --git a/packages/tx/src/eip2930Transaction.ts b/packages/tx/src/eip2930Transaction.ts index ac72fdc348..26e317a790 100644 --- a/packages/tx/src/eip2930Transaction.ts +++ b/packages/tx/src/eip2930Transaction.ts @@ -21,7 +21,7 @@ import { N_DIV_2, } from './types' -import { AccessLists } from './util' +import { AccessLists, checkMaxInitCodeSize } from './util' const TRANSACTION_TYPE = 1 const TRANSACTION_TYPE_BUFFER = Buffer.from(TRANSACTION_TYPE.toString(16).padStart(2, '0'), 'hex') @@ -213,15 +213,8 @@ export default class AccessListEIP2930Transaction extends BaseTransaction this.common.param('vm', 'maxInitCodeSize')) { - throw new Error( - `the initcode size of this transaction is too large: it is ${ - this.data.length - } while the max is ${this.common.param('vm', 'maxInitCodeSize')}` - ) - } + checkMaxInitCodeSize(this.common, this.data.length) } - const freeze = opts?.freeze ?? true if (freeze) { Object.freeze(this) diff --git a/packages/tx/src/legacyTransaction.ts b/packages/tx/src/legacyTransaction.ts index 5e79ce441c..e2627d6eba 100644 --- a/packages/tx/src/legacyTransaction.ts +++ b/packages/tx/src/legacyTransaction.ts @@ -13,6 +13,7 @@ import { import { TxOptions, TxData, JsonTx, N_DIV_2, TxValuesArray, Capability } from './types' import { BaseTransaction } from './baseTransaction' import Common from '@ethereumjs/common' +import { checkMaxInitCodeSize } from './util' const TRANSACTION_TYPE = 0 @@ -136,13 +137,7 @@ export default class Transaction extends BaseTransaction { } if (this.common.isActivatedEIP(3860)) { - if (this.data.length > this.common.param('vm', 'maxInitCodeSize')) { - throw new Error( - `the initcode size of this transaction is too large: it is ${ - this.data.length - } while the max is ${this.common.param('vm', 'maxInitCodeSize')}` - ) - } + checkMaxInitCodeSize(this.common, this.data.length) } const freeze = opts?.freeze ?? true diff --git a/packages/tx/src/util.ts b/packages/tx/src/util.ts index 0175d1ebd0..184d25cb5c 100644 --- a/packages/tx/src/util.ts +++ b/packages/tx/src/util.ts @@ -2,6 +2,17 @@ import Common from '@ethereumjs/common' import { bufferToHex, setLengthLeft, toBuffer } from 'ethereumjs-util' import { AccessList, AccessListBuffer, AccessListItem, isAccessList } from './types' +export function checkMaxInitCodeSize(common: Common, length: number) { + if (length > common.param('vm', 'maxInitCodeSize')) { + throw new Error( + `the initcode size of this transaction is too large: it is ${length} while the max is ${common.param( + 'vm', + 'maxInitCodeSize' + )}` + ) + } +} + export class AccessLists { public static getAccessListData(accessList: AccessListBuffer | AccessList) { let AccessListJSON From f3cf8f6449e281a95b42b550f9f11c2b242871d1 Mon Sep 17 00:00:00 2001 From: acolytec3 <17355484+acolytec3@users.noreply.github.com> Date: Mon, 7 Mar 2022 20:37:02 -0500 Subject: [PATCH 10/10] Add test for exceeds max initcode --- packages/vm/tests/api/EIPs/eip-3860.spec.ts | 40 +++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 packages/vm/tests/api/EIPs/eip-3860.spec.ts diff --git a/packages/vm/tests/api/EIPs/eip-3860.spec.ts b/packages/vm/tests/api/EIPs/eip-3860.spec.ts new file mode 100644 index 0000000000..f89b6ec42d --- /dev/null +++ b/packages/vm/tests/api/EIPs/eip-3860.spec.ts @@ -0,0 +1,40 @@ +import tape from 'tape' +import VM from '../../../src' +import Common, { Chain, Hardfork } from '@ethereumjs/common' +import { FeeMarketEIP1559Transaction } from '@ethereumjs/tx' +import { Address, BN, privateToAddress } from 'ethereumjs-util' +const pkey = Buffer.from('20'.repeat(32), 'hex') +const GWEI = new BN('1000000000') +const sender = new Address(privateToAddress(pkey)) + +tape('EIP 3860 tests', (t) => { + const common = new Common({ + chain: Chain.Mainnet, + hardfork: Hardfork.London, + eips: [3860], + }) + + t.test('EIP-3860 tests', async (st) => { + const vm = new VM({ common }) + const account = await vm.stateManager.getAccount(sender) + const balance = GWEI.muln(21000).muln(10000000) + account.balance = balance + await vm.stateManager.putAccount(sender, account) + + const buffer = Buffer.allocUnsafe(1000000).fill(0x60) + console.log(common.isActivatedEIP(3860)) + const tx = FeeMarketEIP1559Transaction.fromTxData({ + data: + '0x7F6000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060005260206000F3' + + buffer.toString('hex'), + gasLimit: 100000000000, + maxFeePerGas: 7, + nonce: 0, + }).sign(pkey) + const result = await vm.runTx({ tx }) + st.ok( + (result.execResult.exceptionError?.error as string) === 'initcode exceeds max initcode size', + 'initcode exceeds max size' + ) + }) +})