From f0cf88737bb2c39a3ce889a3eee7c78071292c04 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Fri, 15 Oct 2021 18:37:17 +0200 Subject: [PATCH 01/16] evm: Add EIP-3860 to supported extra EIPs --- core/vm/eips.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/vm/eips.go b/core/vm/eips.go index 26c450905662..2a9534f7b3e0 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -27,6 +27,7 @@ import ( var activators = map[int]func(*JumpTable){ 3855: enable3855, + 3860: enable3860, 3529: enable3529, 3198: enable3198, 2929: enable2929, @@ -233,3 +234,9 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by scope.Stack.push(new(uint256.Int)) return nil, nil } + +// ebnable3860 enables "EIP-3860: Limit and meter initcode" +// https://eips.ethereum.org/EIPS/eip-3860 +func enable3860(jt *JumpTable) { + // Do nothing. +} From 7eda5414dd0b6c2f7d086dd7ce29e38902931eff Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 9 Feb 2022 12:17:02 +0100 Subject: [PATCH 02/16] evm: EIP-3860: abort if initcode size above the limit --- core/vm/errors.go | 1 + core/vm/evm.go | 13 +++++++++++++ params/protocol_params.go | 3 ++- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/core/vm/errors.go b/core/vm/errors.go index 004f8ef1c83c..fbbf19e178bf 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -29,6 +29,7 @@ var ( ErrInsufficientBalance = errors.New("insufficient balance for transfer") ErrContractAddressCollision = errors.New("contract address collision") ErrExecutionReverted = errors.New("execution reverted") + ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded") ErrMaxCodeSizeExceeded = errors.New("max code size exceeded") ErrInvalidJump = errors.New("invalid jump destination") ErrWriteProtection = errors.New("write protection") diff --git a/core/vm/evm.go b/core/vm/evm.go index 149e9f761be3..0e68c867d0ca 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -401,6 +401,15 @@ func (c *codeAndHash) Hash() common.Hash { return c.hash } +func (vmConfig *Config) HasEip3860() bool { + for _, eip := range vmConfig.ExtraEips { + if eip == 3860 { + return true + } + } + return false +} + // create creates a new contract using code as deployment code. func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) { // Depth check execution. Fail if we're trying to execute above the @@ -426,6 +435,10 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { return nil, common.Address{}, 0, ErrContractAddressCollision } + // Check whether the init code size has been exceeded. + if evm.Config.HasEip3860() && len(codeAndHash.code) > params.MaxInitCodeSize { + return nil, address, gas, ErrMaxInitCodeSizeExceeded + } // Create a new account on the state snapshot := evm.StateDB.Snapshot() evm.StateDB.CreateAccount(address) diff --git a/params/protocol_params.go b/params/protocol_params.go index b0037fd471c4..5d20f1c3b678 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -123,7 +123,8 @@ const ( DefaultElasticityMultiplier = 2 // Bounds the maximum gas limit an EIP-1559 block may have. InitialBaseFee = 1000000000 // Initial base fee for EIP-1559 blocks. - MaxCodeSize = 24576 // Maximum bytecode to permit for a contract + MaxCodeSize = 24576 // Maximum bytecode to permit for a contract + MaxInitCodeSize = 2 * MaxCodeSize // Maximum initcode to permit in a creation transaction and create instructions // Precompiled contract gas prices From 493db58a98d534da3ee2e3445d0c3ce60d38bf48 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 22 Nov 2021 14:36:31 +0100 Subject: [PATCH 03/16] evm: EIP-3860: extra charge for CREATE and CREATE2 --- core/vm/eips.go | 8 ++++++- core/vm/gas_table.go | 47 +++++++++++++++++++++++++++++++++++++++ params/protocol_params.go | 1 + 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 2a9534f7b3e0..1991d6d0e3fc 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -238,5 +238,11 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by // ebnable3860 enables "EIP-3860: Limit and meter initcode" // https://eips.ethereum.org/EIPS/eip-3860 func enable3860(jt *JumpTable) { - // Do nothing. + createOp := *jt[CREATE] + createOp.dynamicGas = gasCreateEip3860 + jt[CREATE] = &createOp + + create2Op := *jt[CREATE2] + create2Op.dynamicGas = gasCreate2Eip3860 + jt[CREATE2] = &create2Op } diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 57fb1a8d98b2..eaf6fe8c4224 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -302,6 +302,53 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS return gas, nil } +func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + len, overflow := stack.Back(2).Uint64WithOverflow() + if overflow { + return 0, ErrGasUintOverflow + } + lenWords := toWordSize(len) + initCodeWordGas, overflow := math.SafeMul(lenWords, params.InitCodeWordGas) + if overflow { + return 0, ErrGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, initCodeWordGas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} + +func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + len, overflow := stack.Back(2).Uint64WithOverflow() + if overflow { + return 0, ErrGasUintOverflow + } + lenWords := toWordSize(len) + hashingWordGas, overflow := math.SafeMul(lenWords, params.Keccak256WordGas) + if overflow { + return 0, ErrGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, hashingWordGas); overflow { + return 0, ErrGasUintOverflow + } + initCodeWordGas, overflow := math.SafeMul(lenWords, params.InitCodeWordGas) + if overflow { + return 0, ErrGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, initCodeWordGas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} + func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) diff --git a/params/protocol_params.go b/params/protocol_params.go index 5d20f1c3b678..bb703d0b74dc 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -38,6 +38,7 @@ const ( Keccak256Gas uint64 = 30 // Once per KECCAK256 operation. Keccak256WordGas uint64 = 6 // Once per word of the KECCAK256 operation's data. + InitCodeWordGas uint64 = 2 // Once per word of the init code when creating a contract. SstoreSetGas uint64 = 20000 // Once per SSTORE operation. SstoreResetGas uint64 = 5000 // Once per SSTORE operation if the zeroness changes from zero. From b5a3aecc11aa559bb9beec5e1f9957e18b2753e8 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 7 Feb 2022 13:05:09 +0100 Subject: [PATCH 04/16] core: EIP-3860: extra charge for creation transaction --- cmd/evm/internal/t8ntool/transaction.go | 11 ++++++++-- core/bench_test.go | 2 +- core/state_transition.go | 27 +++++++++++++++++++++---- core/txpool/txpool.go | 2 +- light/txpool.go | 2 +- tests/transaction_test_util.go | 2 +- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 3409c0a3bf01..f1cd619f2a51 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -79,12 +79,19 @@ func Transaction(ctx *cli.Context) error { txStr = ctx.String(InputTxsFlag.Name) inputData = &input{} chainConfig *params.ChainConfig + eip3860 = false ) // Construct the chainconfig - if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { + if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) } else { chainConfig = cConf + for _, eip := range extraEips { + if eip == 3860 { + eip3860 = true + break + } + } } // Set the chain id chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name)) @@ -140,7 +147,7 @@ func Transaction(ctx *cli.Context) error { } // Check intrinsic gas if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, - chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int))); err != nil { + chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), eip3860); err != nil { r.Error = err results = append(results, r) continue diff --git a/core/bench_test.go b/core/bench_test.go index f7cf0146060d..7fc8a760f703 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -83,7 +83,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { return func(i int, gen *BlockGen) { toaddr := common.Address{} data := make([]byte, nbytes) - gas, _ := IntrinsicGas(data, nil, false, false, false) + gas, _ := IntrinsicGas(data, nil, false, false, false, false) signer := types.MakeSigner(gen.config, big.NewInt(int64(i))) gasPrice := big.NewInt(0) if gen.header.BaseFee != nil { diff --git a/core/state_transition.go b/core/state_transition.go index fe94161e14f9..a5be19529d50 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -120,7 +120,7 @@ func (result *ExecutionResult) Revert() []byte { } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool) (uint64, error) { +func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation bool, isHomestead, isEIP2028 bool, isEIP3860 bool) (uint64, error) { // Set the starting gas for the raw transaction var gas uint64 if isContractCreation && isHomestead { @@ -128,8 +128,9 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b } else { gas = params.TxGas } + dataLen := uint64(len(data)) // Bump the required gas by the amount of transactional data - if len(data) > 0 { + if dataLen > 0 { // Zero and non-zero bytes are priced differently var nz uint64 for _, byt := range data { @@ -147,11 +148,19 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b } gas += nz * nonZeroGas - z := uint64(len(data)) - nz + z := dataLen - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { return 0, ErrGasUintOverflow } gas += z * params.TxDataZeroGas + + if isContractCreation && isEIP3860 { + lenWords := toWordSize(dataLen) + if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { + return 0, ErrGasUintOverflow + } + gas += lenWords * params.InitCodeWordGas + } } if accessList != nil { gas += uint64(len(accessList)) * params.TxAccessListAddressGas @@ -160,6 +169,15 @@ func IntrinsicGas(data []byte, accessList types.AccessList, isContractCreation b return gas, nil } +// toWordSize returns the ceiled word size required for init code payment calculation. +func toWordSize(size uint64) uint64 { + if size > math.MaxUint64-31 { + return math.MaxUint64/32 + 1 + } + + return (size + 31) / 32 +} + // NewStateTransition initialises and returns a new state transition object. func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { return &StateTransition{ @@ -302,10 +320,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { sender = vm.AccountRef(msg.From()) rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil, st.evm.Context.Time) contractCreation = msg.To() == nil + eip3860 = st.evm.Config.HasEip3860() ) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul) + gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul, eip3860) if err != nil { return nil, err } diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 8905140fdb69..e527e7e57704 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -637,7 +637,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return core.ErrInsufficientFunds } // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, true) if err != nil { return err } diff --git a/light/txpool.go b/light/txpool.go index 3e3572faaf11..b2ccdf76ca2b 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -384,7 +384,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, true) if err != nil { return err } diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 82ee01de15c8..391aa57584cf 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -55,7 +55,7 @@ func (tt *TransactionTest) Run(config *params.ChainConfig) error { return nil, nil, err } // Intrinsic gas - requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul) + requiredGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, isHomestead, isIstanbul, false) if err != nil { return nil, nil, err } From 0fe2b2c371b378fcf31b69d3d46ef33c92a15bce Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Mon, 22 Nov 2021 14:38:20 +0100 Subject: [PATCH 05/16] core/vm: Unit tests for initcode gas charge according to EIP-3860 --- core/vm/gas_table_test.go | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 6cd126c9b4ca..897366cec567 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -105,3 +105,50 @@ func TestEIP2200(t *testing.T) { } } } + +var createGasTests = []struct { + code string + eip3860 bool + gasUsed uint64 +}{ + // create(0, 0, 0xc000) + {"0x61C00060006000f0", false, 41225}, + // create(0, 0, 0xc000) + {"0x61C00060006000f0", true, 44297}, + // create2(0, 0, 0xc000, 0) + {"0x600061C00060006000f5", false, 50444}, + // create2(0, 0, 0xc000, 0) + {"0x600061C00060006000f5", true, 53516}, +} + +func TestCreateGas(t *testing.T) { + for i, tt := range createGasTests { + address := common.BytesToAddress([]byte("contract")) + + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb.CreateAccount(address) + statedb.SetCode(address, hexutil.MustDecode(tt.code)) + statedb.Finalise(true) + + vmctx := BlockContext{ + CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + BlockNumber: big.NewInt(0), + } + config := Config{} + if tt.eip3860 { + config.ExtraEips = []int{3860} + } + + vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config) + + var startGas uint64 = math.MaxUint64 + _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int)) + if err != nil { + t.Errorf("test %d execution failed: %v", i, err) + } + if gasUsed := startGas - gas; gasUsed != tt.gasUsed { + t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed) + } + } +} From 8896d4193d96ef3900907b56b55344df95a9df04 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Thu, 6 Jan 2022 18:17:21 +0100 Subject: [PATCH 06/16] core: Make creation transaction invalid if initcode size above limit --- cmd/evm/internal/t8ntool/transaction.go | 4 ++ core/error.go | 4 ++ core/state_processor_test.go | 58 +++++++++++++++++++++++++ core/state_transition.go | 5 +++ 4 files changed, 71 insertions(+) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index f1cd619f2a51..7688fc5b3c35 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -178,6 +178,10 @@ func Transaction(ctx *cli.Context) error { case new(big.Int).Mul(tx.GasFeeCap(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256: r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits") } + // Check whether the init code size has been exceeded. + if eip3860 && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { + r.Error = errors.New("init code size limit exceeded") + } results = append(results, r) } out, err := json.MarshalIndent(results, "", " ") diff --git a/core/error.go b/core/error.go index 5b69c8dcaf26..872ba8d365d8 100644 --- a/core/error.go +++ b/core/error.go @@ -63,6 +63,10 @@ var ( // have enough funds for transfer(topmost call only). ErrInsufficientFundsForTransfer = errors.New("insufficient funds for transfer") + // ErrMaxInitCodeSizeExceeded is returned if creation transaction provides the init code bigger + // than init code size limit. + ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded") + // ErrInsufficientFunds is returned if the total cost of executing a transaction // is higher than the balance of the user's account. ErrInsufficientFunds = errors.New("insufficient funds for gas * price + value") diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 1df4a0e80418..6d26322f939c 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -75,6 +75,17 @@ func TestStateProcessorErrors(t *testing.T) { }), signer, key1) return tx } + var mkDynamicCreationTx = func(nonce uint64, gasLimit uint64, gasTipCap, gasFeeCap *big.Int, data []byte) *types.Transaction { + tx, _ := types.SignTx(types.NewTx(&types.DynamicFeeTx{ + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gasLimit, + Value: big.NewInt(0), + Data: data, + }), signer, key1) + return tx + } { // Tests against a 'recent' chain definition var ( db = rawdb.NewMemoryDatabase() @@ -294,6 +305,53 @@ func TestStateProcessorErrors(t *testing.T) { } } } + + // ErrMaxInitCodeSizeExceeded, for this we need extra EIP-3860 enabled. + { + var ( + db = rawdb.NewMemoryDatabase() + gspec = &Genesis{ + Config: config, + Alloc: GenesisAlloc{ + common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ + Balance: big.NewInt(1000000000000000000), // 1 ether + Nonce: 0, + }, + }, + } + genesis = gspec.MustCommit(db) + blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{ExtraEips: []int{3860}}, nil, nil) + tooBigInitCode = [params.MaxInitCodeSize + 1]byte{} + smallInitCode = [320]byte{} + ) + defer blockchain.Stop() + for i, tt := range []struct { + txs []*types.Transaction + want string + }{ + { // ErrMaxInitCodeSizeExceeded + txs: []*types.Transaction{ + mkDynamicCreationTx(0, 500000, common.Big0, misc.CalcBaseFee(config, genesis.Header()), tooBigInitCode[:]), + }, + want: "could not apply tx 0 [0x832b54a6c3359474a9f504b1003b2cc1b6fcaa18e4ef369eb45b5d40dad6378f]: max initcode size exceeded: code size 49153 limit 49152", + }, + { // ErrIntrinsicGas: Not enough gas to cover init code + txs: []*types.Transaction{ + mkDynamicCreationTx(0, 54299, common.Big0, misc.CalcBaseFee(config, genesis.Header()), smallInitCode[:]), + }, + want: "could not apply tx 0 [0x39b7436cb432d3662a25626474282c5c4c1a213326fd87e4e18a91477bae98b2]: intrinsic gas too low: have 54299, want 54300", + }, + } { + block := GenerateBadBlock(genesis, ethash.NewFaker(), tt.txs, gspec.Config) + _, err := blockchain.InsertChain(types.Blocks{block}) + if err == nil { + t.Fatal("block imported without errors") + } + if have, want := err.Error(), tt.want; have != want { + t.Errorf("test %d:\nhave \"%v\"\nwant \"%v\"\n", i, have, want) + } + } + } } // GenerateBadBlock constructs a "block" which contains the transactions. The transactions are not expected to be diff --git a/core/state_transition.go b/core/state_transition.go index a5be19529d50..e7b01bea3ddd 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -338,6 +338,11 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From().Hex()) } + // Check whether the init code size has been exceeded. + if eip3860 && contractCreation && len(st.data) > params.MaxInitCodeSize { + return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize) + } + // Execute the preparatory steps for state transition which includes: // - prepare accessList(post-berlin) // - reset transient storage(eip 1153) From 9a109f3a2ade741a1fb05d0df3499e03fa9bf8b0 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 23 Nov 2022 17:29:15 +0100 Subject: [PATCH 07/16] core/vm: Change order of initcode charge checks and nonce update --- core/vm/eips.go | 8 +------- core/vm/evm.go | 19 ++++++++++++++---- core/vm/gas_table.go | 47 -------------------------------------------- 3 files changed, 16 insertions(+), 58 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 1991d6d0e3fc..fa104a3d522e 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -238,11 +238,5 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by // ebnable3860 enables "EIP-3860: Limit and meter initcode" // https://eips.ethereum.org/EIPS/eip-3860 func enable3860(jt *JumpTable) { - createOp := *jt[CREATE] - createOp.dynamicGas = gasCreateEip3860 - jt[CREATE] = &createOp - - create2Op := *jt[CREATE2] - create2Op.dynamicGas = gasCreate2Eip3860 - jt[CREATE2] = &create2Op + // Additional checks and charges are done in creation transaction EVM creation implementation } diff --git a/core/vm/evm.go b/core/vm/evm.go index 0e68c867d0ca..a908d2fbde6e 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -420,6 +420,21 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, common.Address{}, gas, ErrInsufficientBalance } + // For creation transaction (evm.depth == 0) EIP-3860 checks are done when calculating intrinsic gas + if evm.Config.HasEip3860() && evm.depth != 0 { + // Check whether the init code size has been exceeded. + len := len(codeAndHash.code) + if len > params.MaxInitCodeSize { + return nil, address, gas, ErrMaxInitCodeSizeExceeded + } + // Charge for init code size. + initCodeWordGas := toWordSize(uint64(len)) * params.InitCodeWordGas + if gas < initCodeWordGas { + return nil, address, gas, ErrCodeStoreOutOfGas + } else { + gas -= initCodeWordGas + } + } nonce := evm.StateDB.GetNonce(caller.Address()) if nonce+1 < nonce { return nil, common.Address{}, gas, ErrNonceUintOverflow @@ -435,10 +450,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) { return nil, common.Address{}, 0, ErrContractAddressCollision } - // Check whether the init code size has been exceeded. - if evm.Config.HasEip3860() && len(codeAndHash.code) > params.MaxInitCodeSize { - return nil, address, gas, ErrMaxInitCodeSizeExceeded - } // Create a new account on the state snapshot := evm.StateDB.Snapshot() evm.StateDB.CreateAccount(address) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index eaf6fe8c4224..57fb1a8d98b2 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -302,53 +302,6 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS return gas, nil } -func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - len, overflow := stack.Back(2).Uint64WithOverflow() - if overflow { - return 0, ErrGasUintOverflow - } - lenWords := toWordSize(len) - initCodeWordGas, overflow := math.SafeMul(lenWords, params.InitCodeWordGas) - if overflow { - return 0, ErrGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, initCodeWordGas); overflow { - return 0, ErrGasUintOverflow - } - return gas, nil -} - -func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { - gas, err := memoryGasCost(mem, memorySize) - if err != nil { - return 0, err - } - len, overflow := stack.Back(2).Uint64WithOverflow() - if overflow { - return 0, ErrGasUintOverflow - } - lenWords := toWordSize(len) - hashingWordGas, overflow := math.SafeMul(lenWords, params.Keccak256WordGas) - if overflow { - return 0, ErrGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, hashingWordGas); overflow { - return 0, ErrGasUintOverflow - } - initCodeWordGas, overflow := math.SafeMul(lenWords, params.InitCodeWordGas) - if overflow { - return 0, ErrGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, initCodeWordGas); overflow { - return 0, ErrGasUintOverflow - } - return gas, nil -} - func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) From db41c8887ed6f1d3bb56f7fa9b58481a72387ad8 Mon Sep 17 00:00:00 2001 From: "lightclient@protonmail.com" Date: Thu, 8 Dec 2022 12:18:20 -0700 Subject: [PATCH 08/16] core/vm: don't sub hashing cost for create2 initcode if length is too long --- core/vm/gas_table.go | 3 +++ core/vm/gas_table_test.go | 2 +- core/vm/instructions.go | 8 +++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 57fb1a8d98b2..e9ff06bb2800 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -293,6 +293,9 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if overflow { return 0, ErrGasUintOverflow } + if evm.interpreter.cfg.HasEip3860() && wordGas > params.MaxInitCodeSize { + return 0, nil // instruction will handle error + } if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 897366cec567..7d70edc062bb 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -118,7 +118,7 @@ var createGasTests = []struct { // create2(0, 0, 0xc000, 0) {"0x600061C00060006000f5", false, 50444}, // create2(0, 0, 0xc000, 0) - {"0x600061C00060006000f5", true, 53516}, + {"0x600061C00160006000f5", true, 32012}, } func TestCreateGas(t *testing.T) { diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 22c72c10a491..1d9491ecd6f2 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -633,7 +633,13 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) - + // Check EIP-3860 initcode length. + if interpreter.cfg.HasEip3860() { + if size, overflow := size.Uint64WithOverflow(); overflow || size > params.MaxInitCodeSize { + scope.Stack.push(uint256.NewInt(0)) + return nil, nil + } + } // Apply EIP150 gas -= gas / 64 scope.Contract.UseGas(gas) From 8edb9d3742b59d0fa51ab9431d3efb8e8646013d Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 27 Dec 2022 10:15:07 +0100 Subject: [PATCH 09/16] core/vm: modify tests for create2 gas consumption --- core/vm/gas_table_test.go | 91 +++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 33 deletions(-) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 7d70edc062bb..9252029797db 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -21,11 +21,13 @@ import ( "math/big" "testing" + "bytes" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/params" + "sort" ) func TestMemoryGasCost(t *testing.T) { @@ -107,48 +109,71 @@ func TestEIP2200(t *testing.T) { } var createGasTests = []struct { - code string - eip3860 bool - gasUsed uint64 + code string + eip3860 bool + gasUsed uint64 + minimumGas uint64 }{ - // create(0, 0, 0xc000) - {"0x61C00060006000f0", false, 41225}, - // create(0, 0, 0xc000) - {"0x61C00060006000f0", true, 44297}, + // create(0, 0, 0xc000) no limit used + {"0x61C00060006000f0" + "600052" + "60206000F3", false, 41237, 41237}, + // create(0, 0, 0xc000) with limit + {"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44345}, + // create2(0, 0, 0xc001, 0) no limit + {"0x600061C00160006000f5" + "600052" + "60206000F3", false, 50471, 50471}, + // create2(0, 0, 0xc001, 0) (too large) + {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32012, 100_000}, // create2(0, 0, 0xc000, 0) - {"0x600061C00060006000f5", false, 50444}, - // create2(0, 0, 0xc000, 0) - {"0x600061C00160006000f5", true, 32012}, + // This case is trying to deploy code at (within) the limit + {"0x600061C00060006000f5" + "600052" + "60206000F3", true, 50456, 53564}, + // create2(0, 0, 0xc001, 0) + // This case is trying to deploy code exceeding the limit + {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32024, 100000}, } func TestCreateGas(t *testing.T) { for i, tt := range createGasTests { - address := common.BytesToAddress([]byte("contract")) - - statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) - statedb.CreateAccount(address) - statedb.SetCode(address, hexutil.MustDecode(tt.code)) - statedb.Finalise(true) + var gasUsed = uint64(0) + doCheck := func(testGas int) bool { + address := common.BytesToAddress([]byte("contract")) + statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil) + statedb.CreateAccount(address) + statedb.SetCode(address, hexutil.MustDecode(tt.code)) + statedb.Finalise(true) + vmctx := BlockContext{ + CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, + Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, + BlockNumber: big.NewInt(0), + } + config := Config{} + if tt.eip3860 { + config.ExtraEips = []int{3860} + } - vmctx := BlockContext{ - CanTransfer: func(StateDB, common.Address, *big.Int) bool { return true }, - Transfer: func(StateDB, common.Address, common.Address, *big.Int) {}, - BlockNumber: big.NewInt(0), - } - config := Config{} - if tt.eip3860 { - config.ExtraEips = []int{3860} + vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config) + var startGas = uint64(testGas) + ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int)) + if err != nil { + return false + } + gasUsed = startGas - gas + if len(ret) != 32 { + t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret)) + } + if bytes.Compare(ret, make([]byte, 32)) == 0 { + // Failure + return false + } + return true } - - vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config) - - var startGas uint64 = math.MaxUint64 - _, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(big.Int)) - if err != nil { - t.Errorf("test %d execution failed: %v", i, err) + minGas := sort.Search(100_000, doCheck) + if uint64(minGas) != tt.minimumGas { + t.Fatalf("test %d: min gas error, want %d, have %d", i, tt.minimumGas, minGas) } - if gasUsed := startGas - gas; gasUsed != tt.gasUsed { - t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed) + // If the deployment succeeded, we also check the gas used + if minGas < 100_000 { + if gasUsed != tt.gasUsed { + t.Errorf("test %d: gas used mismatch: have %v, want %v", i, gasUsed, tt.gasUsed) + } } } } From 5d73263f9def4ea316326156c56e4a81a82eb72b Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 27 Dec 2022 10:37:12 +0100 Subject: [PATCH 10/16] core/vm: move 3860-checks to separate gas calculator --- core/vm/eips.go | 3 ++- core/vm/evm.go | 15 ------------- core/vm/gas_table.go | 45 ++++++++++++++++++++++++++++++++++++--- core/vm/gas_table_test.go | 12 +++++------ core/vm/instructions.go | 7 ------ 5 files changed, 50 insertions(+), 32 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index fa104a3d522e..29ff27c55268 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -238,5 +238,6 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by // ebnable3860 enables "EIP-3860: Limit and meter initcode" // https://eips.ethereum.org/EIPS/eip-3860 func enable3860(jt *JumpTable) { - // Additional checks and charges are done in creation transaction EVM creation implementation + jt[CREATE].dynamicGas = gasCreateEip3860 + jt[CREATE2].dynamicGas = gasCreate2Eip3860 } diff --git a/core/vm/evm.go b/core/vm/evm.go index a908d2fbde6e..5565b03f73e6 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -420,21 +420,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) { return nil, common.Address{}, gas, ErrInsufficientBalance } - // For creation transaction (evm.depth == 0) EIP-3860 checks are done when calculating intrinsic gas - if evm.Config.HasEip3860() && evm.depth != 0 { - // Check whether the init code size has been exceeded. - len := len(codeAndHash.code) - if len > params.MaxInitCodeSize { - return nil, address, gas, ErrMaxInitCodeSizeExceeded - } - // Charge for init code size. - initCodeWordGas := toWordSize(uint64(len)) * params.InitCodeWordGas - if gas < initCodeWordGas { - return nil, address, gas, ErrCodeStoreOutOfGas - } else { - gas -= initCodeWordGas - } - } nonce := evm.StateDB.GetNonce(caller.Address()) if nonce+1 < nonce { return nil, common.Address{}, gas, ErrNonceUintOverflow diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index e9ff06bb2800..4770c42c9e9f 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -293,9 +293,6 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS if overflow { return 0, ErrGasUintOverflow } - if evm.interpreter.cfg.HasEip3860() && wordGas > params.MaxInitCodeSize { - return 0, nil // instruction will handle error - } if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow { return 0, ErrGasUintOverflow } @@ -305,6 +302,48 @@ func gasCreate2(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memoryS return gas, nil } +func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + if stack.Back(2).GtUint64(params.MaxInitCodeSize) { + return 0, ErrGasUintOverflow + } + wordGas, overflow := stack.Back(2).Uint64WithOverflow() + if overflow { + return 0, ErrGasUintOverflow + } + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.InitCodeWordGas); overflow { + return 0, ErrGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil + +} +func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { + gas, err := memoryGasCost(mem, memorySize) + if err != nil { + return 0, err + } + if stack.Back(2).GtUint64(params.MaxInitCodeSize) { + return 0, ErrGasUintOverflow + } + wordGas, overflow := stack.Back(2).Uint64WithOverflow() + if overflow { + return 0, ErrGasUintOverflow + } + if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.InitCodeWordGas+params.Keccak256WordGas); overflow { + return 0, ErrGasUintOverflow + } + if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + return 0, ErrGasUintOverflow + } + return gas, nil +} + func gasExpFrontier(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 9252029797db..681bd8e20cba 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -114,17 +114,17 @@ var createGasTests = []struct { gasUsed uint64 minimumGas uint64 }{ - // create(0, 0, 0xc000) no limit used + // legacy create(0, 0, 0xc000) without 3860 used {"0x61C00060006000f0" + "600052" + "60206000F3", false, 41237, 41237}, - // create(0, 0, 0xc000) with limit - {"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44345}, - // create2(0, 0, 0xc001, 0) no limit + // legacy create(0, 0, 0xc000) _with_ 3860 + {"0x61C00060006000f0" + "600052" + "60206000F3", true, 44309, 44309}, + // create2(0, 0, 0xc001, 0) without 3860 {"0x600061C00160006000f5" + "600052" + "60206000F3", false, 50471, 50471}, - // create2(0, 0, 0xc001, 0) (too large) + // create2(0, 0, 0xc001, 0) (too large), with 3860 {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32012, 100_000}, // create2(0, 0, 0xc000, 0) // This case is trying to deploy code at (within) the limit - {"0x600061C00060006000f5" + "600052" + "60206000F3", true, 50456, 53564}, + {"0x600061C00060006000f5" + "600052" + "60206000F3", true, 53528, 53528}, // create2(0, 0, 0xc001, 0) // This case is trying to deploy code exceeding the limit {"0x600061C00160006000f5" + "600052" + "60206000F3", true, 32024, 100000}, diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 1d9491ecd6f2..8fa2fc57e51f 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -633,13 +633,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) - // Check EIP-3860 initcode length. - if interpreter.cfg.HasEip3860() { - if size, overflow := size.Uint64WithOverflow(); overflow || size > params.MaxInitCodeSize { - scope.Stack.push(uint256.NewInt(0)) - return nil, nil - } - } // Apply EIP150 gas -= gas / 64 scope.Contract.UseGas(gas) From ed09df53b50a6e4ae3a6fdb0b9c3e00927299166 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 27 Dec 2022 11:12:42 +0100 Subject: [PATCH 11/16] core/vm: simplify gas calculators for 3860 --- core/vm/gas_table.go | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 4770c42c9e9f..bab457191e9e 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -307,17 +307,13 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m if err != nil { return 0, err } - if stack.Back(2).GtUint64(params.MaxInitCodeSize) { + size, overflow := stack.Back(2).Uint64WithOverflow() + if overflow || size > params.MaxInitCodeSize { return 0, ErrGasUintOverflow } - wordGas, overflow := stack.Back(2).Uint64WithOverflow() - if overflow { - return 0, ErrGasUintOverflow - } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.InitCodeWordGas); overflow { - return 0, ErrGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow + moreGas := params.InitCodeWordGas * ((size + 31) / 32) + if gas, overflow = math.SafeAdd(gas, moreGas); overflow { return 0, ErrGasUintOverflow } return gas, nil @@ -328,17 +324,13 @@ func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if err != nil { return 0, err } - if stack.Back(2).GtUint64(params.MaxInitCodeSize) { + size, overflow := stack.Back(2).Uint64WithOverflow() + if overflow || size > params.MaxInitCodeSize { return 0, ErrGasUintOverflow } - wordGas, overflow := stack.Back(2).Uint64WithOverflow() - if overflow { - return 0, ErrGasUintOverflow - } - if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.InitCodeWordGas+params.Keccak256WordGas); overflow { - return 0, ErrGasUintOverflow - } - if gas, overflow = math.SafeAdd(gas, wordGas); overflow { + // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow + moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32) + if gas, overflow = math.SafeAdd(gas, moreGas); overflow { return 0, ErrGasUintOverflow } return gas, nil From a55f5cb7a3821bb0228eae231bb0d1a96e0a9048 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 27 Dec 2022 15:33:18 +0100 Subject: [PATCH 12/16] core/vm: fix CI warnings --- core/vm/gas_table.go | 1 - core/vm/gas_table_test.go | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index bab457191e9e..65d46b3436d9 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -317,7 +317,6 @@ func gasCreateEip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m return 0, ErrGasUintOverflow } return gas, nil - } func gasCreate2Eip3860(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { gas, err := memoryGasCost(mem, memorySize) diff --git a/core/vm/gas_table_test.go b/core/vm/gas_table_test.go index 681bd8e20cba..c3d06e515b17 100644 --- a/core/vm/gas_table_test.go +++ b/core/vm/gas_table_test.go @@ -17,17 +17,17 @@ package vm import ( + "bytes" "math" "math/big" + "sort" "testing" - "bytes" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/params" - "sort" ) func TestMemoryGasCost(t *testing.T) { @@ -159,7 +159,7 @@ func TestCreateGas(t *testing.T) { if len(ret) != 32 { t.Fatalf("test %d: expected 32 bytes returned, have %d", i, len(ret)) } - if bytes.Compare(ret, make([]byte, 32)) == 0 { + if bytes.Equal(ret, make([]byte, 32)) { // Failure return false } From effeefcc23513b3c73b2e6aa8adcceab3e0de389 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 3 Jan 2023 18:40:03 +0100 Subject: [PATCH 13/16] core: activate EIP-3860 in Shanghai --- cmd/evm/internal/t8ntool/transaction.go | 13 +++---------- core/state_processor_test.go | 23 ++++++++++++++++++++--- core/state_transition.go | 5 ++--- core/txpool/txpool.go | 4 +++- core/vm/evm.go | 9 --------- core/vm/interpreter.go | 2 ++ core/vm/jump_table.go | 7 +++++++ light/txpool.go | 4 +++- 8 files changed, 40 insertions(+), 27 deletions(-) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 7688fc5b3c35..c4a029bfc114 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -79,19 +79,12 @@ func Transaction(ctx *cli.Context) error { txStr = ctx.String(InputTxsFlag.Name) inputData = &input{} chainConfig *params.ChainConfig - eip3860 = false ) // Construct the chainconfig - if cConf, extraEips, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { + if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil { return NewError(ErrorConfig, fmt.Errorf("failed constructing chain configuration: %v", err)) } else { chainConfig = cConf - for _, eip := range extraEips { - if eip == 3860 { - eip3860 = true - break - } - } } // Set the chain id chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name)) @@ -147,7 +140,7 @@ func Transaction(ctx *cli.Context) error { } // Check intrinsic gas if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, - chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), eip3860); err != nil { + chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int)), chainConfig.IsShanghai(new(big.Int))); err != nil { r.Error = err results = append(results, r) continue @@ -179,7 +172,7 @@ func Transaction(ctx *cli.Context) error { r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits") } // Check whether the init code size has been exceeded. - if eip3860 && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { + if chainConfig.IsShanghai(new(big.Int)) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { r.Error = errors.New("init code size limit exceeded") } results = append(results, r) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 6d26322f939c..473a09f2705d 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -306,12 +306,29 @@ func TestStateProcessorErrors(t *testing.T) { } } - // ErrMaxInitCodeSizeExceeded, for this we need extra EIP-3860 enabled. + // ErrMaxInitCodeSizeExceeded, for this we need extra Shanghai (EIP-3860) enabled. { var ( db = rawdb.NewMemoryDatabase() gspec = &Genesis{ - Config: config, + Config: ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + ShanghaiBlock: big.NewInt(0), + }, Alloc: GenesisAlloc{ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ Balance: big.NewInt(1000000000000000000), // 1 ether @@ -320,7 +337,7 @@ func TestStateProcessorErrors(t *testing.T) { }, } genesis = gspec.MustCommit(db) - blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{ExtraEips: []int{3860}}, nil, nil) + blockchain, _ = NewBlockChain(db, nil, gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil) tooBigInitCode = [params.MaxInitCodeSize + 1]byte{} smallInitCode = [320]byte{} ) diff --git a/core/state_transition.go b/core/state_transition.go index e7b01bea3ddd..653c6b183618 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -320,11 +320,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { sender = vm.AccountRef(msg.From()) rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil, st.evm.Context.Time) contractCreation = msg.To() == nil - eip3860 = st.evm.Config.HasEip3860() ) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul, eip3860) + gas, err := IntrinsicGas(st.data, st.msg.AccessList(), contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) if err != nil { return nil, err } @@ -339,7 +338,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { } // Check whether the init code size has been exceeded. - if eip3860 && contractCreation && len(st.data) > params.MaxInitCodeSize { + if rules.IsShanghai && contractCreation && len(st.data) > params.MaxInitCodeSize { return nil, fmt.Errorf("%w: code size %v limit %v", ErrMaxInitCodeSizeExceeded, len(st.data), params.MaxInitCodeSize) } diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index e527e7e57704..4c7e73c39ec2 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -245,6 +245,7 @@ type TxPool struct { istanbul bool // Fork indicator whether we are in the istanbul stage. eip2718 bool // Fork indicator whether we are using EIP-2718 type transactions. eip1559 bool // Fork indicator whether we are using EIP-1559 type transactions. + shanghai bool // Fork indicator whether we are in the Shanghai stage. currentState *state.StateDB // Current state in the blockchain head pendingNonces *noncer // Pending state tracking virtual nonces @@ -637,7 +638,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error { return core.ErrInsufficientFunds } // Ensure the transaction has more gas than the basic tx fee. - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, true) + intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai) if err != nil { return err } @@ -1306,6 +1307,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.istanbul = pool.chainconfig.IsIstanbul(next) pool.eip2718 = pool.chainconfig.IsBerlin(next) pool.eip1559 = pool.chainconfig.IsLondon(next) + pool.shanghai = pool.chainconfig.IsShanghai(next) } // promoteExecutables moves transactions that have become processable from the diff --git a/core/vm/evm.go b/core/vm/evm.go index 5565b03f73e6..149e9f761be3 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -401,15 +401,6 @@ func (c *codeAndHash) Hash() common.Hash { return c.hash } -func (vmConfig *Config) HasEip3860() bool { - for _, eip := range vmConfig.ExtraEips { - if eip == 3860 { - return true - } - } - return false -} - // create creates a new contract using code as deployment code. func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) { // Depth check execution. Fail if we're trying to execute above the diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index c99e54b76dc0..7b040aac9e11 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -60,6 +60,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { // If jump table was not initialised we set the default one. if cfg.JumpTable == nil { switch { + case evm.chainRules.IsShanghai: + cfg.JumpTable = &shanghaiInstructionSet case evm.chainRules.IsMerge: cfg.JumpTable = &mergeInstructionSet case evm.chainRules.IsLondon: diff --git a/core/vm/jump_table.go b/core/vm/jump_table.go index 7cbcc56c5adc..240f3734a5a3 100644 --- a/core/vm/jump_table.go +++ b/core/vm/jump_table.go @@ -55,6 +55,7 @@ var ( berlinInstructionSet = newBerlinInstructionSet() londonInstructionSet = newLondonInstructionSet() mergeInstructionSet = newMergeInstructionSet() + shanghaiInstructionSet = newShanghaiInstructionSet() ) // JumpTable contains the EVM opcodes supported at a given fork. @@ -78,6 +79,12 @@ func validate(jt JumpTable) JumpTable { return jt } +func newShanghaiInstructionSet() JumpTable { + instructionSet := newMergeInstructionSet() + enable3860(&instructionSet) + return validate(instructionSet) +} + func newMergeInstructionSet() JumpTable { instructionSet := newLondonInstructionSet() instructionSet[PREVRANDAO] = &operation{ diff --git a/light/txpool.go b/light/txpool.go index b2ccdf76ca2b..798ef1708839 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -70,6 +70,7 @@ type TxPool struct { istanbul bool // Fork indicator whether we are in the istanbul stage. eip2718 bool // Fork indicator whether we are in the eip2718 stage. + shanghai bool // Fork indicator whether we are in the shanghai stage. } // TxRelayBackend provides an interface to the mechanism that forwards transactions to the @@ -317,6 +318,7 @@ func (pool *TxPool) setNewHead(head *types.Header) { next := new(big.Int).Add(head.Number, big.NewInt(1)) pool.istanbul = pool.config.IsIstanbul(next) pool.eip2718 = pool.config.IsBerlin(next) + pool.shanghai = pool.config.IsShanghai(next) } // Stop stops the light transaction pool @@ -384,7 +386,7 @@ func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error } // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, true) + gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, pool.istanbul, pool.shanghai) if err != nil { return err } From a326b8cb7c37a4f152577c8852a41f318e5a9420 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Tue, 3 Jan 2023 19:19:45 +0100 Subject: [PATCH 14/16] cmd/evm: Support Shanghai in t8n tool --- tests/init.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/init.go b/tests/init.go index e6faa483a68d..fd0b015086a1 100644 --- a/tests/init.go +++ b/tests/init.go @@ -248,6 +248,24 @@ var Forks = map[string]*params.ChainConfig{ MergeNetsplitBlock: big.NewInt(0), TerminalTotalDifficulty: big.NewInt(0), }, + "Shanghai": { + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + MergeNetsplitBlock: big.NewInt(0), + TerminalTotalDifficulty: big.NewInt(0), + ShanghaiTime: big.NewInt(0), + }, } // AvailableForks returns the set of defined fork names From 9b558fb30be2e780afd131db8b83cd82d48271c4 Mon Sep 17 00:00:00 2001 From: Andrei Maiboroda Date: Wed, 4 Jan 2023 14:59:05 +0100 Subject: [PATCH 15/16] cmd/evm: Fix error message in t8n tool to be consistent with core error --- cmd/evm/internal/t8ntool/transaction.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index c4a029bfc114..1bd370918e3e 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -173,7 +173,7 @@ func Transaction(ctx *cli.Context) error { } // Check whether the init code size has been exceeded. if chainConfig.IsShanghai(new(big.Int)) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { - r.Error = errors.New("init code size limit exceeded") + r.Error = errors.New("max initcode size exceeded") } results = append(results, r) } From 56df86aab2291f2361642b67153c5400276442b5 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 10 Jan 2023 13:10:01 +0100 Subject: [PATCH 16/16] core/txpool: set shanghai by time correctly --- core/state_processor_test.go | 2 +- core/txpool/txpool.go | 2 +- light/txpool.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 473a09f2705d..400ccd318793 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -327,7 +327,7 @@ func TestStateProcessorErrors(t *testing.T) { ArrowGlacierBlock: big.NewInt(0), GrayGlacierBlock: big.NewInt(0), MergeNetsplitBlock: big.NewInt(0), - ShanghaiBlock: big.NewInt(0), + ShanghaiTime: big.NewInt(0), }, Alloc: GenesisAlloc{ common.HexToAddress("0x71562b71999873DB5b286dF957af199Ec94617F7"): GenesisAccount{ diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 4c7e73c39ec2..dc6a71f07ac2 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -1307,7 +1307,7 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) { pool.istanbul = pool.chainconfig.IsIstanbul(next) pool.eip2718 = pool.chainconfig.IsBerlin(next) pool.eip1559 = pool.chainconfig.IsLondon(next) - pool.shanghai = pool.chainconfig.IsShanghai(next) + pool.shanghai = pool.chainconfig.IsShanghai(big.NewInt(time.Now().Unix())) } // promoteExecutables moves transactions that have become processable from the diff --git a/light/txpool.go b/light/txpool.go index 798ef1708839..e12f6ef94425 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -318,7 +318,7 @@ func (pool *TxPool) setNewHead(head *types.Header) { next := new(big.Int).Add(head.Number, big.NewInt(1)) pool.istanbul = pool.config.IsIstanbul(next) pool.eip2718 = pool.config.IsBerlin(next) - pool.shanghai = pool.config.IsShanghai(next) + pool.shanghai = pool.config.IsShanghai(big.NewInt(time.Now().Unix())) } // Stop stops the light transaction pool