From 6f86fdaf87db4b7d7ccefc8612d88c7c8907fa15 Mon Sep 17 00:00:00 2001 From: ramtinms Date: Wed, 17 Jan 2024 14:35:20 -0800 Subject: [PATCH] make getPrecompile configurable --- cmd/evm/internal/t8ntool/execution.go | 17 +++---- core/evm.go | 42 ++++++++++++----- core/vm/evm.go | 39 +++++++-------- core/vm/runtime/env.go | 23 ++++----- .../internal/tracetest/calltrace_test.go | 47 ++++++++++--------- .../internal/tracetest/flat_calltrace_test.go | 15 +++--- .../internal/tracetest/prestate_test.go | 17 +++---- eth/tracers/tracers_test.go | 17 +++---- 8 files changed, 120 insertions(+), 97 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index b654cb2196..16fb8251de 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -146,14 +146,15 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, ) gaspool.AddGas(pre.Env.GasLimit) vmContext := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: pre.Env.Coinbase, - BlockNumber: new(big.Int).SetUint64(pre.Env.Number), - Time: pre.Env.Timestamp, - Difficulty: pre.Env.Difficulty, - GasLimit: pre.Env.GasLimit, - GetHash: getHash, + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + Coinbase: pre.Env.Coinbase, + BlockNumber: new(big.Int).SetUint64(pre.Env.Number), + Time: pre.Env.Timestamp, + Difficulty: pre.Env.Difficulty, + GasLimit: pre.Env.GasLimit, + GetHash: getHash, + GetPrecompile: core.GetPrecompile, } // If currentBaseFee is defined, add it to the vmContext. if pre.Env.BaseFee != nil { diff --git a/core/evm.go b/core/evm.go index c4801dc797..53983fa6a8 100644 --- a/core/evm.go +++ b/core/evm.go @@ -24,6 +24,7 @@ import ( "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" ) // ChainContext supports retrieving headers and consensus parameters from the @@ -61,17 +62,18 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common random = &header.MixDigest } return vm.BlockContext{ - CanTransfer: CanTransfer, - Transfer: Transfer, - GetHash: GetHashFn(header, chain), - Coinbase: beneficiary, - BlockNumber: new(big.Int).Set(header.Number), - Time: header.Time, - Difficulty: new(big.Int).Set(header.Difficulty), - BaseFee: baseFee, - BlobBaseFee: blobBaseFee, - GasLimit: header.GasLimit, - Random: random, + CanTransfer: CanTransfer, + Transfer: Transfer, + GetHash: GetHashFn(header, chain), + GetPrecompile: GetPrecompile, + Coinbase: beneficiary, + BlockNumber: new(big.Int).Set(header.Number), + Time: header.Time, + Difficulty: new(big.Int).Set(header.Difficulty), + BaseFee: baseFee, + BlobBaseFee: blobBaseFee, + GasLimit: header.GasLimit, + Random: random, } } @@ -127,6 +129,24 @@ func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash } } +func GetPrecompile(rules params.Rules, addr common.Address) (vm.PrecompiledContract, bool) { + var precompiles map[common.Address]vm.PrecompiledContract + switch { + case rules.IsCancun: + precompiles = vm.PrecompiledContractsCancun + case rules.IsBerlin: + precompiles = vm.PrecompiledContractsBerlin + case rules.IsIstanbul: + precompiles = vm.PrecompiledContractsIstanbul + case rules.IsByzantium: + precompiles = vm.PrecompiledContractsByzantium + default: + precompiles = vm.PrecompiledContractsHomestead + } + p, ok := precompiles[addr] + return p, ok +} + // CanTransfer checks whether there are enough funds in the address' account to make a transfer. // This does not take the necessary gas in to account to make the transfer valid. func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { diff --git a/core/vm/evm.go b/core/vm/evm.go index 088b18aaa4..cdd7991435 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -35,26 +35,11 @@ type ( // GetHashFunc returns the n'th block hash in the blockchain // and is used by the BLOCKHASH EVM op code. GetHashFunc func(uint64) common.Hash + // GetPrecompileFunc returns a precompile contract if available for the given + // address, it returns false if no precompile is deployed at that address + GetPrecompileFunc func(rules params.Rules, addr common.Address) (PrecompiledContract, bool) ) -func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) { - var precompiles map[common.Address]PrecompiledContract - switch { - case evm.chainRules.IsCancun: - precompiles = PrecompiledContractsCancun - case evm.chainRules.IsBerlin: - precompiles = PrecompiledContractsBerlin - case evm.chainRules.IsIstanbul: - precompiles = PrecompiledContractsIstanbul - case evm.chainRules.IsByzantium: - precompiles = PrecompiledContractsByzantium - default: - precompiles = PrecompiledContractsHomestead - } - p, ok := precompiles[addr] - return p, ok -} - // BlockContext provides the EVM with auxiliary information. Once provided // it shouldn't be modified. type BlockContext struct { @@ -65,6 +50,8 @@ type BlockContext struct { Transfer TransferFunc // GetHash returns the hash corresponding to n GetHash GetHashFunc + // GetPrecompileFunc gets a precompile by address if available + GetPrecompile GetPrecompileFunc // Block information Coinbase common.Address // Provides information for COINBASE @@ -172,6 +159,14 @@ func (evm *EVM) Interpreter() *EVMInterpreter { return evm.interpreter } +func (evm *EVM) GetPrecompile(addr common.Address) (PrecompiledContract, bool) { + if evm.Context.GetPrecompile != nil { + p, isPrecompile := evm.Context.GetPrecompile(evm.chainRules, addr) + return p, isPrecompile + } + return nil, false +} + // Call executes the contract associated with the addr with the given input as // parameters. It also handles any necessary value transfer required and takes // the necessary steps to create accounts and reverses the state in case of an @@ -186,7 +181,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas return nil, gas, ErrInsufficientBalance } snapshot := evm.StateDB.Snapshot() - p, isPrecompile := evm.precompile(addr) + p, isPrecompile := evm.GetPrecompile(addr) debug := evm.Config.Tracer != nil if !evm.StateDB.Exist(addr) { @@ -286,7 +281,7 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, } // It is allowed to call precompiles, even via delegatecall - if p, isPrecompile := evm.precompile(addr); isPrecompile { + if p, isPrecompile := evm.GetPrecompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) } else { addrCopy := addr @@ -331,7 +326,7 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by } // It is allowed to call precompiles, even via delegatecall - if p, isPrecompile := evm.precompile(addr); isPrecompile { + if p, isPrecompile := evm.GetPrecompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) } else { addrCopy := addr @@ -380,7 +375,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte }(gas) } - if p, isPrecompile := evm.precompile(addr); isPrecompile { + if p, isPrecompile := evm.GetPrecompile(addr); isPrecompile { ret, gas, err = RunPrecompiledContract(p, input, gas) } else { // At this point, we use a copy of address. If we don't, the go compiler will diff --git a/core/vm/runtime/env.go b/core/vm/runtime/env.go index 34335b8e9e..0aeea28991 100644 --- a/core/vm/runtime/env.go +++ b/core/vm/runtime/env.go @@ -29,17 +29,18 @@ func NewEnv(cfg *Config) *vm.EVM { BlobFeeCap: cfg.BlobFeeCap, } blockContext := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - GetHash: cfg.GetHashFn, - Coinbase: cfg.Coinbase, - BlockNumber: cfg.BlockNumber, - Time: cfg.Time, - Difficulty: cfg.Difficulty, - GasLimit: cfg.GasLimit, - BaseFee: cfg.BaseFee, - BlobBaseFee: cfg.BlobBaseFee, - Random: cfg.Random, + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetHash: cfg.GetHashFn, + GetPrecompile: core.GetPrecompile, + Coinbase: cfg.Coinbase, + BlockNumber: cfg.BlockNumber, + Time: cfg.Time, + Difficulty: cfg.Difficulty, + GasLimit: cfg.GasLimit, + BaseFee: cfg.BaseFee, + BlobBaseFee: cfg.BlobBaseFee, + Random: cfg.Random, } return vm.NewEVM(blockContext, txContext, cfg.State, cfg.ChainConfig, cfg.EVMConfig) diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index 0b43a021ea..db97a8a3e9 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -124,14 +124,15 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { var ( signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: test.Context.Miner, - BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), - BaseFee: test.Genesis.BaseFee, + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetPrecompile: core.GetPrecompile, + Coinbase: test.Context.Miner, + BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), + Time: uint64(test.Context.Time), + Difficulty: (*big.Int)(test.Context.Difficulty), + GasLimit: uint64(test.Context.GasLimit), + BaseFee: test.Genesis.BaseFee, } triedb, _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) ) @@ -223,13 +224,14 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { GasPrice: tx.GasPrice(), } context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: test.Context.Miner, - BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetPrecompile: core.GetPrecompile, + Coinbase: test.Context.Miner, + BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), + Time: uint64(test.Context.Time), + Difficulty: (*big.Int)(test.Context.Difficulty), + GasLimit: uint64(test.Context.GasLimit), } msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) if err != nil { @@ -267,13 +269,14 @@ func TestInternals(t *testing.T) { GasPrice: big.NewInt(1), } context = vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: common.Address{}, - BlockNumber: new(big.Int).SetUint64(8000000), - Time: 5, - Difficulty: big.NewInt(0x30000), - GasLimit: uint64(6000000), + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetPrecompile: core.GetPrecompile, + Coinbase: common.Address{}, + BlockNumber: new(big.Int).SetUint64(8000000), + Time: 5, + Difficulty: big.NewInt(0x30000), + GasLimit: uint64(6000000), } ) mkTracer := func(name string, cfg json.RawMessage) tracers.Tracer { diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index b318548bc1..c4b35a1f43 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -87,13 +87,14 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string } signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: test.Context.Miner, - BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetPrecompile: core.GetPrecompile, + Coinbase: test.Context.Miner, + BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), + Time: uint64(test.Context.Time), + Difficulty: (*big.Int)(test.Context.Difficulty), + GasLimit: uint64(test.Context.GasLimit), } triedb, _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) defer triedb.Close() diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 666a5fda78..393e7eeb40 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -94,14 +94,15 @@ func testPrestateDiffTracer(tracerName string, dirPath string, t *testing.T) { var ( signer = types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) context = vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: test.Context.Miner, - BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), - Time: uint64(test.Context.Time), - Difficulty: (*big.Int)(test.Context.Difficulty), - GasLimit: uint64(test.Context.GasLimit), - BaseFee: test.Genesis.BaseFee, + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetPrecompile: core.GetPrecompile, + Coinbase: test.Context.Miner, + BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)), + Time: uint64(test.Context.Time), + Difficulty: (*big.Int)(test.Context.Difficulty), + GasLimit: uint64(test.Context.GasLimit), + BaseFee: test.Genesis.BaseFee, } triedb, _, statedb = tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) ) diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index 54d34ec5d1..c2a979c6a8 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -52,14 +52,15 @@ func BenchmarkTransactionTrace(b *testing.B) { GasPrice: tx.GasPrice(), } context := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - Coinbase: common.Address{}, - BlockNumber: new(big.Int).SetUint64(uint64(5)), - Time: 5, - Difficulty: big.NewInt(0xffffffff), - GasLimit: gas, - BaseFee: big.NewInt(8), + CanTransfer: core.CanTransfer, + Transfer: core.Transfer, + GetPrecompile: core.GetPrecompile, + Coinbase: common.Address{}, + BlockNumber: new(big.Int).SetUint64(uint64(5)), + Time: 5, + Difficulty: big.NewInt(0xffffffff), + GasLimit: gas, + BaseFee: big.NewInt(8), } alloc := core.GenesisAlloc{} // The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns