From c65d117a921b7dc070409297c3e63ab82a76b48e Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 26 Apr 2024 17:35:36 +0200 Subject: [PATCH] add verkle op handlers --- core/state_processor_test.go | 2 +- core/vm/eips.go | 131 ++++++++++++++++++++++++++++++++++- core/vm/instructions.go | 55 +-------------- 3 files changed, 133 insertions(+), 55 deletions(-) diff --git a/core/state_processor_test.go b/core/state_processor_test.go index 1c29ef39dc..3beffa002b 100644 --- a/core/state_processor_test.go +++ b/core/state_processor_test.go @@ -481,7 +481,7 @@ func TestProcessVerkle(t *testing.T) { txCost1 := params.TxGas txCost2 := params.TxGas contractCreationCost := intrinsicContractCreationGas + uint64(2039 /* execution costs */) - codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(179444 /* execution costs */) + codeWithExtCodeCopyGas := intrinsicCodeWithExtCodeCopyGas + uint64(57444 /* execution costs */) blockGasUsagesExpected := []uint64{ txCost1*2 + txCost2, txCost1*2 + txCost2 + contractCreationCost + codeWithExtCodeCopyGas, diff --git a/core/vm/eips.go b/core/vm/eips.go index ccf6e8f9b4..5037740b57 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -18,9 +18,12 @@ package vm import ( "fmt" + "math" "sort" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/tracing" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/holiman/uint256" ) @@ -321,6 +324,125 @@ func enable6780(jt *JumpTable) { } } +func opCreateEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } + var endowment = scope.Stack.peek() + + contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address())) + statelessGas := interpreter.evm.Accesses.ContractCreateInitGas(contractAddress.Bytes()[:], endowment.Sign() != 0) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + return nil, ErrExecutionReverted + } + + return opCreate(pc, interpreter, scope) +} + +func opCreate2EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + if interpreter.readOnly { + return nil, ErrWriteProtection + } + var ( + endowment = scope.Stack.Back(0) + offset, size = scope.Stack.Back(1), scope.Stack.Back(2) + salt = scope.Stack.Back(3) + input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) + ) + + codeAndHash := &codeAndHash{code: input} + contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) + statelessGas := interpreter.evm.Accesses.ContractCreateInitGas(contractAddress.Bytes()[:], endowment.Sign() != 0) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + return nil, ErrExecutionReverted + } + + return opCreate2(pc, interpreter, scope) +} + +func opExtCodeCopyEIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + stack = scope.Stack + a = stack.pop() + memOffset = stack.pop() + codeOffset = stack.pop() + length = stack.pop() + ) + uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow() + if overflow { + uint64CodeOffset = math.MaxUint64 + } + addr := common.Address(a.Bytes20()) + code := interpreter.evm.StateDB.GetCode(addr) + contract := &Contract{ + Code: code, + self: AccountRef(addr), + } + paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) + statelessGas := interpreter.evm.Accesses.CodeChunksRangeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) + + return nil, nil +} + +// opPush1EIP4762 is a specialized version of pushN +func opPush1EIP4762(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = uint64(len(scope.Contract.Code)) + integer = new(uint256.Int) + ) + *pc += 1 + if *pc < codeLen { + scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) + + if !scope.Contract.IsDeployment && *pc%31 == 0 { + // touch next chunk if PUSH1 is at the boundary. if so, *pc has + // advanced past this boundary. + contractAddr := scope.Contract.Address() + statelessGas := interpreter.evm.Accesses.CodeChunksRangeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + } + } else { + scope.Stack.push(integer.Clear()) + } + return nil, nil +} + +func makePushEIP4762(size uint64, pushByteSize int) executionFunc { + return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { + var ( + codeLen = len(scope.Contract.Code) + start = min(codeLen, int(*pc+1)) + end = min(codeLen, start+pushByteSize) + ) + scope.Stack.push(new(uint256.Int).SetBytes( + common.RightPadBytes( + scope.Contract.Code[start:end], + pushByteSize, + )), + ) + + if !scope.Contract.IsDeployment { + contractAddr := scope.Contract.Address() + statelessGas := interpreter.evm.Accesses.CodeChunksRangeGas(contractAddr[:], uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) + if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { + scope.Contract.Gas = 0 + return nil, ErrOutOfGas + } + } + + *pc += size + return nil, nil + } +} + func enable4762(jt *JumpTable) { jt[SSTORE].constantGas = 0 jt[SSTORE].dynamicGas = gasSStore4762 @@ -334,16 +456,23 @@ func enable4762(jt *JumpTable) { jt[EXTCODEHASH].dynamicGas = gasExtCodeHash4762 jt[EXTCODECOPY].constantGas = 0 jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP4762 + jt[EXTCODECOPY].execute = opExtCodeCopyEIP4762 jt[CODECOPY].dynamicGas = gasCodeCopyEip4762 jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP4762 jt[CREATE].constantGas = params.CreateNGasEip4762 + jt[CREATE].execute = opCreateEIP4762 jt[CREATE2].constantGas = params.CreateNGasEip4762 + jt[CREATE2].execute = opCreate2EIP4762 jt[CALL].constantGas = 0 jt[CALL].dynamicGas = gasCallEIP4762 jt[CALLCODE].constantGas = 0 - jt[CALLCODE].dynamicGas = gasCallCodeEIP4762 + jt[CALLCODE].dynamicGas = gasDelegateCallEIP4762 jt[STATICCALL].constantGas = 0 jt[STATICCALL].dynamicGas = gasStaticCallEIP4762 jt[DELEGATECALL].constantGas = 0 jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP4762 + jt[PUSH1].execute = opPush1EIP4762 + for i := 1; i < 32; i++ { + jt[PUSH1+OpCode(i)].execute = makePushEIP4762(uint64(i+1), i+1) + } } diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0771c5f052..95b48fbe60 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -382,23 +382,8 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) uint64CodeOffset = math.MaxUint64 } addr := common.Address(a.Bytes20()) - if interpreter.evm.chainRules.IsEIP4762 { - code := interpreter.evm.StateDB.GetCode(addr) - contract := &Contract{ - Code: code, - self: AccountRef(addr), - } - paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - statelessGas := interpreter.evm.Accesses.CodeChunksRangeGas(addr[:], copyOffset, nonPaddedCopyLength, uint64(len(contract.Code)), false) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - scope.Contract.Gas = 0 - return nil, ErrOutOfGas - } - scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) - } else { - codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) - scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) - } + codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) + scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) return nil, nil } @@ -604,14 +589,6 @@ func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b gas -= gas / 64 } - if interpreter.evm.chainRules.IsEIP4762 { - contractAddress := crypto.CreateAddress(scope.Contract.Address(), interpreter.evm.StateDB.GetNonce(scope.Contract.Address())) - statelessGas := interpreter.evm.Accesses.ContractCreateInitGas(contractAddress.Bytes()[:], value.Sign() != 0) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - return nil, ErrExecutionReverted - } - } - // reuse size int for stackvalue stackvalue := size @@ -652,14 +629,6 @@ func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([] input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) gas = scope.Contract.Gas ) - if interpreter.evm.chainRules.IsEIP4762 { - codeAndHash := &codeAndHash{code: input} - contractAddress := crypto.CreateAddress2(scope.Contract.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes()) - statelessGas := interpreter.evm.Accesses.ContractCreateInitGas(contractAddress.Bytes()[:], endowment.Sign() != 0) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - return nil, ErrExecutionReverted - } - } // Apply EIP150 gas -= gas / 64 @@ -913,17 +882,6 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by *pc += 1 if *pc < codeLen { scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc]))) - - if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsEIP4762 && *pc%31 == 0 { - // touch next chunk if PUSH1 is at the boundary. if so, *pc has - // advanced past this boundary. - contractAddr := scope.Contract.Address() - statelessGas := interpreter.evm.Accesses.CodeChunksRangeGas(contractAddr[:], *pc+1, uint64(1), uint64(len(scope.Contract.Code)), false) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - scope.Contract.Gas = 0 - return nil, ErrOutOfGas - } - } } else { scope.Stack.push(integer.Clear()) } @@ -945,15 +903,6 @@ func makePush(size uint64, pushByteSize int) executionFunc { )), ) - if !scope.Contract.IsDeployment && interpreter.evm.chainRules.IsEIP4762 { - contractAddr := scope.Contract.Address() - statelessGas := interpreter.evm.Accesses.CodeChunksRangeGas(contractAddr[:], uint64(start), uint64(pushByteSize), uint64(len(scope.Contract.Code)), false) - if !scope.Contract.UseGas(statelessGas, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified) { - scope.Contract.Gas = 0 - return nil, ErrOutOfGas - } - } - *pc += size return nil, nil }