From 8d68a86bf04a13873b2b4c635e9d6b1360b25891 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Wed, 11 Sep 2024 17:30:13 -0600 Subject: [PATCH 01/10] remove println on bad opcode Fixes fill of opcode validiiy tests --- core/state_transition.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/state_transition.go b/core/state_transition.go index 05132a315b..88e6d38871 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -520,7 +520,6 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { st.gasRemaining = 0 st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1) } - fmt.Println(vmerr) } else { ret, st.gasRemaining, vmerr = st.evm.Call(sender, st.to(), msg.Data, st.gasRemaining, value) } From 4a4d3b07e421b859cd164266bc99103c700deae5 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 12 Sep 2024 22:23:45 -0600 Subject: [PATCH 02/10] EXTCALL test fixes * use corret stack heights for value and address in EXTCALL * New extCallGas function that handles min retained and min callee gas * Handle min gas failures via tempCallGas == 0 * remove stipend refund for EXT*CALL * Call Stack too deep should return 1 --- core/vm/eips.go | 37 +++++++++++++++++++++++++++++-------- core/vm/errors.go | 2 +- core/vm/gas.go | 30 ++++++++++++++++++++++++++++++ core/vm/gas_table.go | 38 ++++++++++++++++---------------------- params/protocol_params.go | 2 ++ 5 files changed, 78 insertions(+), 31 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index ceb74d51e6..18ce837792 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -1055,13 +1055,20 @@ func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] if interpreter.readOnly && !value.IsZero() { return nil, ErrWriteProtection } - if !value.IsZero() { - gas += params.CallStipend + + var ( + ret []byte + returnGas uint64 + err error + ) + if interpreter.evm.callGasTemp == 0 { + // zero temp call gas indicates a min retained gas error + ret, returnGas, err = nil, 0, ErrExecutionReverted + } else { + ret, returnGas, err = interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value) } - ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value) - - if err == ErrExecutionReverted { + if err == ErrExecutionReverted || err == ErrInsufficientBalance || err == ErrDepth { temp.SetOne() } else if err != nil { temp.SetUint64(2) @@ -1102,11 +1109,14 @@ func opExtDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeCont err = ErrExecutionReverted ret = nil returnGas = gas + } else if interpreter.evm.callGasTemp == 0 { + // zero temp call gas indicates a min retained gas error + ret, returnGas, err = nil, 0, ErrExecutionReverted } else { ret, returnGas, err = interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas, true) } - if err == ErrExecutionReverted { + if err == ErrExecutionReverted || err == ErrDepth { temp.SetOne() } else if err != nil { temp.SetUint64(2) @@ -1136,8 +1146,19 @@ func opExtStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContex // Get arguments from the memory. args := scope.Memory.GetPtr(inOffset.Uint64(), inSize.Uint64()) - ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas) - if err == ErrExecutionReverted { + var ( + ret []byte + returnGas uint64 + err error + ) + if interpreter.evm.callGasTemp == 0 { + // zero temp call gas indicates a min retained gas error + ret, returnGas, err = nil, 0, ErrExecutionReverted + } else { + ret, returnGas, err = interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas) + } + + if err == ErrExecutionReverted || err == ErrDepth { temp.SetOne() } else if err != nil { temp.SetUint64(2) diff --git a/core/vm/errors.go b/core/vm/errors.go index 95534ab96d..026d0b2533 100644 --- a/core/vm/errors.go +++ b/core/vm/errors.go @@ -42,7 +42,7 @@ var ( ErrInvalidEOFInitcode = errors.New("invalid eof initcode") ErrNonceUintOverflow = errors.New("nonce uint64 overflow") ErrInvalidNumberOfOutputs = errors.New("invalid number of outputs") - ErrInvalidNonReturningFlag = errors.New("Invalid non-returning flag, bad RETF") + ErrInvalidNonReturningFlag = errors.New("invalid non-returning flag, bad RETF") // errStopToken is an internal token indicating interpreter loop termination, // never returned to outside callers. diff --git a/core/vm/gas.go b/core/vm/gas.go index 5aaa7eb473..f77a271a43 100644 --- a/core/vm/gas.go +++ b/core/vm/gas.go @@ -17,6 +17,7 @@ package vm import ( + "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -53,3 +54,32 @@ func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (u return callCost.Uint64(), nil } + +// extCallGas returns the actual gas cost for ext*call operations. +// +// EOF v1 includes EIP-150 rules (all but 1/64) with a floor of MIN_RETAINED_GAS (5000) +// and a minimum returned value of MIN_CALLE_GASS (2300). +// There is also no call gas, so all available gas is used. +// +// If the minimum retained gas constraint is violated, zero gas and no error is returned +func extCallGas(availableGas, base uint64) (uint64, error) { + if availableGas < base { + return 0, ErrOutOfGas + } + availableGas = availableGas - base + if availableGas < params.ExtCallMinRetainedGas { + return 0, nil + } + + retainedGas := availableGas / 64 + if retainedGas < params.ExtCallMinRetainedGas { + retainedGas = params.ExtCallMinRetainedGas + } + gas := availableGas - retainedGas + + if gas < params.ExtCallMinCalleeGas { + return 0, nil + } else { + return gas, nil + } +} diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index e79a56f053..73a2d63f22 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -23,7 +23,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" - "github.com/holiman/uint256" ) // memoryGasCost calculates the quadratic gas for memory expansion. It does so @@ -484,37 +483,32 @@ func gasStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo func gasExtCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) { var ( gas uint64 - transfersValue = !stack.Back(2).IsZero() - address = common.Address(stack.Back(1).Bytes20()) + transfersValue = !stack.Back(3).IsZero() + address = common.Address(stack.Back(0).Bytes20()) + overflow bool ) - if evm.chainRules.IsEIP158 { - if transfersValue && evm.StateDB.Empty(address) { + if transfersValue { + if evm.StateDB.Empty(address) { gas += params.CallNewAccountGas } - } else if !evm.StateDB.Exist(address) { - gas += params.CallNewAccountGas - } - if transfersValue && !evm.chainRules.IsEIP4762 { - gas += params.CallValueTransferGas + if evm.chainRules.IsEIP4762 { + gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) + if overflow { + return 0, ErrGasUintOverflow + } + } else { + gas += params.CallValueTransferGas + } } memoryGas, err := memoryGasCost(mem, memorySize) if err != nil { return 0, err } - var overflow bool if gas, overflow = math.SafeAdd(gas, memoryGas); overflow { return 0, ErrGasUintOverflow } - if evm.chainRules.IsEIP4762 { - if transfersValue { - gas, overflow = math.SafeAdd(gas, evm.AccessEvents.ValueTransferGas(contract.Address(), address)) - if overflow { - return 0, ErrGasUintOverflow - } - } - } - evm.callGasTemp, err = callGas(true, contract.Gas, gas, new(uint256.Int).SetUint64(contract.Gas)) + evm.callGasTemp, err = extCallGas(contract.Gas, gas) if err != nil { return 0, err } @@ -531,7 +525,7 @@ func gasExtDelegateCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, if err != nil { return 0, err } - evm.callGasTemp, err = callGas(true, contract.Gas, gas, new(uint256.Int).SetUint64(contract.Gas)) + evm.callGasTemp, err = extCallGas(contract.Gas, gas) if err != nil { return 0, err } @@ -547,7 +541,7 @@ func gasExtStaticCall(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m if err != nil { return 0, err } - evm.callGasTemp, err = callGas(true, contract.Gas, gas, new(uint256.Int).SetUint64(contract.Gas)) + evm.callGasTemp, err = extCallGas(contract.Gas, gas) if err != nil { return 0, err } diff --git a/params/protocol_params.go b/params/protocol_params.go index dcd0592938..3705f8ba0a 100644 --- a/params/protocol_params.go +++ b/params/protocol_params.go @@ -89,6 +89,8 @@ const ( CreateNGasEip4762 uint64 = 1000 // Once per CREATEn operations post-verkle SelfdestructRefundGas uint64 = 24000 // Refunded following a selfdestruct operation. MemoryGas uint64 = 3 // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL. + ExtCallMinRetainedGas uint64 = 5000 // For EXT*CALL this is the minimum gas that the EIp158 1/64th rule must retain + ExtCallMinCalleeGas uint64 = 2300 // For EXT*CALL this is the minimum gas that must be passed to the callee, ignoring 63/64 TxDataNonZeroGasFrontier uint64 = 68 // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions. TxDataNonZeroGasEIP2028 uint64 = 16 // Per byte of non zero data attached to a transaction after EIP 2028 (part in Istanbul) From 389edbdf22e30927937fdabb1fb57d1d82dde55a Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 13 Sep 2024 19:02:06 -0600 Subject: [PATCH 03/10] EOFCREATE validation Handle Aux Data in EOF Create w/o validation failures --- core/vm/eips.go | 20 +++++++++++++------- core/vm/eof.go | 11 ++++++++--- core/vm/validate.go | 3 --- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 18ce837792..194e13a2e2 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -924,22 +924,28 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) containerCode := scope.Contract.Container.ContainerCode[idx] - deployedCode := append(containerCode, ret...) - if len(deployedCode) == 0 { + //deployedCode := append(containerCode, ret...) + if len(containerCode) == 0 { return nil, errors.New("nonexistant subcontainer") } // Validate the subcontainer var c Container - if err := c.UnmarshalBinary(deployedCode, true); err != nil { - return nil, err - } - if err := c.ValidateCode(interpreter.tableEOF, true); err != nil { + if err := c.UnmarshalSubContainer(containerCode, false); err != nil { return nil, err } + + // append the auxdata + c.Data = append(c.Data, ret...) if len(c.Data) < c.DataSize { - return nil, errors.New("invalid subcontainer") + return nil, errors.New("incomplete aux data") } c.DataSize = len(c.Data) + + // probably unneeded as subcontainers are deeply validated + if err := c.ValidateCode(interpreter.tableEOF, false); err != nil { + return nil, err + } + // Restore context retCtx := scope.ReturnStack.Pop() scope.CodeSection = retCtx.Section diff --git a/core/vm/eof.go b/core/vm/eof.go index 7b005ca326..5808fd2c79 100644 --- a/core/vm/eof.go +++ b/core/vm/eof.go @@ -145,10 +145,15 @@ func (c *Container) MarshalBinary() []byte { // UnmarshalBinary decodes an EOF container. func (c *Container) UnmarshalBinary(b []byte, isInitcode bool) error { - return c.unmarshalSubContainer(b, isInitcode, true) + return c.unmarshalContainer(b, isInitcode, true) } -func (c *Container) unmarshalSubContainer(b []byte, isInitcode bool, topLevel bool) error { +// UnmarshalSubContainer decodes an EOF container that is container in another container +func (c *Container) UnmarshalSubContainer(b []byte, isInitcode bool) error { + return c.unmarshalContainer(b, isInitcode, false) +} + +func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) error { if !hasEOFMagic(b) { return fmt.Errorf("%w: want %x", ErrInvalidMagic, eofMagic) } @@ -294,7 +299,7 @@ func (c *Container) unmarshalSubContainer(b []byte, isInitcode bool, topLevel bo } c := new(Container) end := min(idx+size, len(b)) - if err := c.unmarshalSubContainer(b[idx:end], isInitcode, false); err != nil { + if err := c.unmarshalContainer(b[idx:end], isInitcode, false); err != nil { if topLevel { return fmt.Errorf("%w in sub container %d", err, i) } diff --git a/core/vm/validate.go b/core/vm/validate.go index 5cce0cb004..7f4e759db2 100644 --- a/core/vm/validate.go +++ b/core/vm/validate.go @@ -155,9 +155,6 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable, if ct := container.ContainerSections[arg]; len(ct.Data) != ct.DataSize { return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", ErrEOFCreateWithTruncatedSection, arg, len(ct.Data), ct.DataSize, i) } - if _, ok := visitedSubcontainers[arg]; ok { - return nil, fmt.Errorf("section already referenced, arg :%d", arg) - } // We need to store per subcontainer how it was referenced if v, ok := visitedSubcontainers[arg]; ok && v != RefByEOFCreate { return nil, fmt.Errorf("section already referenced, arg :%d", arg) From 4a93fe961299532e27e8ded3a2771203e78c43c8 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 13 Sep 2024 20:22:40 -0600 Subject: [PATCH 04/10] Create Transaction Allow Create Transactions --- core/state_transition.go | 2 +- core/vm/evm.go | 10 +++++----- core/vm/instructions.go | 2 +- core/vm/runtime/runtime.go | 7 +------ 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 88e6d38871..48c082ec65 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -512,7 +512,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { vmerr error // vm errors do not effect consensus and are therefore not assigned to err ) if contractCreation { - ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value) + ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value, rules.IsPrague) // Special case for EOF, if the initcode or deployed code is // invalid, the tx is considered valid (so update nonce), but // is to be treated as an exceptional abort (so burn all gas). diff --git a/core/vm/evm.go b/core/vm/evm.go index 6fec499265..d75bec157c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -457,7 +457,7 @@ func (c *codeAndHash) Hash() common.Hash { } // create creates a new contract using code as deployment code. -func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode, input []byte, fromEOF bool) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { +func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *uint256.Int, address common.Address, typ OpCode, input []byte, allowEOF bool) (ret []byte, createAddress common.Address, leftOverGas uint64, err error) { if evm.Config.Tracer != nil { evm.captureBegin(evm.depth, typ, caller.Address(), address, codeAndHash.code, gas, value.ToBig()) defer func(startGas uint64) { @@ -484,7 +484,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, isInitcodeEOF := hasEOFMagic(codeAndHash.code) if evm.chainRules.IsPrague { if isInitcodeEOF { - if !fromEOF { + if !allowEOF { return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, ErrLegacyCode) } // If the initcode is EOF, verify it is well-formed. @@ -496,7 +496,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, err) } contract.Container = &c - } else if fromEOF { + } else if allowEOF { // Don't allow EOF contract to execute legacy initcode. return nil, common.Address{}, gas, ErrLegacyCode } @@ -619,9 +619,9 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, } // Create creates a new contract using code as deployment code. -func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { +func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *uint256.Int, allowEOF bool) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) { contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address())) - return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE, nil, false) + return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE, nil, allowEOF) } // Create2 creates a new contract using code as deployment code. diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 8ca9bac296..e6bb41c446 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -715,7 +715,7 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation) - res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value) + res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, &value, false) // Push item on the stack based on the returned error. If the ruleset is // homestead we must check for CodeStoreOutOfGasError (homestead only // rule) and treat as an error, if the ruleset is frontier we must diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 1181e5fccd..1f49a29d03 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -180,12 +180,7 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // - reset transient storage(eip 1153) cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil) // Call the code with the given configuration. - code, address, leftOverGas, err := vmenv.Create( - sender, - input, - cfg.GasLimit, - uint256.MustFromBig(cfg.Value), - ) + code, address, leftOverGas, err := vmenv.Create(sender, input, cfg.GasLimit, uint256.MustFromBig(cfg.Value), false) return code, address, leftOverGas, err } From 810397f56d962d901fbc0be92526f8cb62d3adb5 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Fri, 13 Sep 2024 21:38:35 -0600 Subject: [PATCH 05/10] formatting --- core/vm/eips.go | 1 - core/vm/runtime/runtime.go | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 194e13a2e2..d30f8a153f 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -924,7 +924,6 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte } ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) containerCode := scope.Contract.Container.ContainerCode[idx] - //deployedCode := append(containerCode, ret...) if len(containerCode) == 0 { return nil, errors.New("nonexistant subcontainer") } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 1f49a29d03..cae6e2a24a 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -180,7 +180,13 @@ func Create(input []byte, cfg *Config) ([]byte, common.Address, uint64, error) { // - reset transient storage(eip 1153) cfg.State.Prepare(rules, cfg.Origin, cfg.Coinbase, nil, vm.ActivePrecompiles(rules), nil) // Call the code with the given configuration. - code, address, leftOverGas, err := vmenv.Create(sender, input, cfg.GasLimit, uint256.MustFromBig(cfg.Value), false) + code, address, leftOverGas, err := vmenv.Create( + sender, + input, + cfg.GasLimit, + uint256.MustFromBig(cfg.Value), + false, + ) return code, address, leftOverGas, err } From c7efbf466e7ac03aa2244a385c531e16dce9ebd0 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Sat, 14 Sep 2024 16:00:46 -0600 Subject: [PATCH 06/10] create transaction Some corner cases for create * Don't consume all gas with an invalid EOF contract * don't allow CREATE/CREATE2 opcodes to create EOF --- core/state_transition.go | 2 +- core/vm/evm.go | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index 48c082ec65..ed909e7558 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -517,7 +517,7 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { // invalid, the tx is considered valid (so update nonce), but // is to be treated as an exceptional abort (so burn all gas). if errors.Is(vmerr, vm.ErrInvalidEOFInitcode) { - st.gasRemaining = 0 + //st.gasRemaining = 0 st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1) } } else { diff --git a/core/vm/evm.go b/core/vm/evm.go index d75bec157c..7499991536 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -482,11 +482,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Validate initcode per EOF rules. If caller is EOF and initcode is legacy, fail. isInitcodeEOF := hasEOFMagic(codeAndHash.code) - if evm.chainRules.IsPrague { - if isInitcodeEOF { - if !allowEOF { - return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, ErrLegacyCode) - } + if isInitcodeEOF { + if allowEOF { // If the initcode is EOF, verify it is well-formed. var c Container if err := c.UnmarshalBinary(codeAndHash.code, isInitcodeEOF); err != nil { @@ -496,7 +493,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, err) } contract.Container = &c - } else if allowEOF { + } else { // Don't allow EOF contract to execute legacy initcode. return nil, common.Address{}, gas, ErrLegacyCode } @@ -573,7 +570,8 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Reject code starting with 0xEF if EIP-3541 is enabled. if err == nil && len(ret) >= 1 && HasEOFByte(ret) { - if evm.chainRules.IsShanghai { + if evm.chainRules.IsPrague && isInitcodeEOF { + fmt.Printf("FIXME - valid EOF deployment\n") // Don't reject EOF contracts after Shanghai } else if evm.chainRules.IsLondon { err = ErrInvalidCode From 12bcb4929a983b9c0a56e31aeb49aa9bf04466c4 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Mon, 16 Sep 2024 08:43:37 -0600 Subject: [PATCH 07/10] cleanup from review comments --- core/state_transition.go | 1 - core/vm/eips.go | 2 +- core/vm/evm.go | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/state_transition.go b/core/state_transition.go index ed909e7558..03e4250688 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -517,7 +517,6 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { // invalid, the tx is considered valid (so update nonce), but // is to be treated as an exceptional abort (so burn all gas). if errors.Is(vmerr, vm.ErrInvalidEOFInitcode) { - //st.gasRemaining = 0 st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1) } } else { diff --git a/core/vm/eips.go b/core/vm/eips.go index d30f8a153f..7a85d6fe73 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -1073,7 +1073,7 @@ func opExtCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] ret, returnGas, err = interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value) } - if err == ErrExecutionReverted || err == ErrInsufficientBalance || err == ErrDepth { + if errors.Is(err, ErrExecutionReverted) || errors.Is(err, ErrInsufficientBalance) || errors.Is(err, ErrDepth) { temp.SetOne() } else if err != nil { temp.SetUint64(2) diff --git a/core/vm/evm.go b/core/vm/evm.go index 7499991536..648af525c9 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -571,7 +571,6 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, // Reject code starting with 0xEF if EIP-3541 is enabled. if err == nil && len(ret) >= 1 && HasEOFByte(ret) { if evm.chainRules.IsPrague && isInitcodeEOF { - fmt.Printf("FIXME - valid EOF deployment\n") // Don't reject EOF contracts after Shanghai } else if evm.chainRules.IsLondon { err = ErrInvalidCode From 4392a1c4e6d4ccc98f4e773a7d8a009b8ccd143e Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 17 Sep 2024 15:44:04 -0600 Subject: [PATCH 08/10] Fix dangling data in container for initcode --- core/vm/eof.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/vm/eof.go b/core/vm/eof.go index 5808fd2c79..9423495c03 100644 --- a/core/vm/eof.go +++ b/core/vm/eof.go @@ -245,7 +245,7 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) return fmt.Errorf("%w: have %d, want %d", ErrInvalidContainerSize, len(b), expectedSize) } // Only check that the expected size is not exceed on non-initcode - if !isInitcode && len(b) > expectedSize { + if (!topLevel || !isInitcode) && len(b) > expectedSize { return fmt.Errorf("%w: have %d, want %d", ErrInvalidContainerSize, len(b), expectedSize) } From 995310c2a91c23a6046b343d60a7c54125577f55 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 17 Sep 2024 16:48:49 -0600 Subject: [PATCH 09/10] Update core/state_transition.go Co-authored-by: Marius van der Wijden --- core/state_transition.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/state_transition.go b/core/state_transition.go index 03e4250688..c42b47d50c 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -515,7 +515,8 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value, rules.IsPrague) // Special case for EOF, if the initcode or deployed code is // invalid, the tx is considered valid (so update nonce), but - // is to be treated as an exceptional abort (so burn all gas). + // gas for initcode execution is not consumed. + // Only intrinsic creation transaction costs are charged. if errors.Is(vmerr, vm.ErrInvalidEOFInitcode) { st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1) } From 740ad8cc98370010f3214a23af3e95b5854204e5 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Tue, 17 Sep 2024 17:22:51 -0600 Subject: [PATCH 10/10] don't do beacon chain updates in state tests --- tests/state_test_util.go | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/state_test_util.go b/tests/state_test_util.go index af54340956..a8477eb7cd 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -259,10 +259,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh vmconfig.ExtraEips = eips block := t.genesis(config).ToBlock() - genesisAlloc := t.json.Pre - genesisAlloc[params.BeaconRootsAddress] = types.Account{Nonce: 1, Code: params.BeaconRootsCode} - //genesisAlloc[params.HistoryStorageAddress] = types.Account{Nonce: 1, Code: params.HistoryStorageCode} - st = MakePreState(rawdb.NewMemoryDatabase(), genesisAlloc, snapshotter, scheme) + st = MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme) var baseFee *big.Int if config.IsLondon(new(big.Int)) { @@ -319,10 +316,6 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh if config.IsCancun(new(big.Int), block.Time()) && t.json.Env.ExcessBlobGas != nil { context.BlobBaseFee = eip4844.CalcBlobFee(*t.json.Env.ExcessBlobGas) } - { - evm := vm.NewEVM(context, vm.TxContext{}, st.StateDB, config, vmconfig) - core.ProcessBeaconBlockRoot(common.HexToHash("0x00"), evm, st.StateDB) - } evm := vm.NewEVM(context, txContext, st.StateDB, config, vmconfig)