From 72b800501cc9216bda5f0dcc47d2996cfb86bcec Mon Sep 17 00:00:00 2001 From: Matus Kysel Date: Wed, 11 Jun 2025 11:25:29 +0200 Subject: [PATCH] core/vm: implement contract pool --- core/vm/contract_pool.go | 48 +++++++++++++++++++++++++++++++ core/vm/evm.go | 22 ++++++++++---- core/vm/instructions_test.go | 3 +- eth/tracers/js/tracer_test.go | 11 +++++-- eth/tracers/logger/logger_test.go | 4 ++- tests/state_test.go | 4 +-- 6 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 core/vm/contract_pool.go diff --git a/core/vm/contract_pool.go b/core/vm/contract_pool.go new file mode 100644 index 0000000000..c7fa5feea8 --- /dev/null +++ b/core/vm/contract_pool.go @@ -0,0 +1,48 @@ +package vm + +import ( + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" +) + +var contractPool = sync.Pool{ + New: func() any { + return &Contract{} + }, +} + +// GetContract returns a contract from the pool or creates a new one +func GetContract(caller common.Address, address common.Address, value *uint256.Int, gas uint64, jumpDests map[common.Hash]bitvec) *Contract { + contract := contractPool.Get().(*Contract) + + // Reset the contract with new values + contract.caller = caller + contract.address = address + contract.jumpdests = jumpDests + if contract.jumpdests == nil { + // Initialize the jump analysis map if it's nil, mostly for tests + contract.jumpdests = make(map[common.Hash]bitvec) + } + contract.Gas = gas + contract.value = value + + contract.Code = nil + contract.CodeHash = common.Hash{} + contract.Input = nil + contract.IsDeployment = false + contract.IsSystemCall = false + + contract.analysis = nil + + return contract +} + +// ReturnContract returns a contract to the pool +func ReturnContract(contract *Contract) { + if contract == nil { + return + } + contractPool.Put(contract) +} diff --git a/core/vm/evm.go b/core/vm/evm.go index b45a434545..e538118115 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -238,7 +238,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g ret, err = nil, nil // gas is unchanged } else { // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, addr, value, gas, evm.jumpDests) + contract := GetContract(caller, addr, value, gas, evm.jumpDests) + defer ReturnContract(contract) + contract.IsSystemCall = isSystemCall(caller) contract.SetCallCode(evm.resolveCodeHash(addr), code) ret, err = evm.interpreter.Run(contract, input, false) @@ -298,7 +300,9 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, caller, value, gas, evm.jumpDests) + contract := GetContract(caller, caller, value, gas, evm.jumpDests) + defer ReturnContract(contract) + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas @@ -342,7 +346,9 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address, // Initialise a new contract and make initialise the delegate values // // Note: The value refers to the original value from the parent call. - contract := NewContract(originCaller, caller, value, gas, evm.jumpDests) + contract := GetContract(originCaller, caller, value, gas, evm.jumpDests) + defer ReturnContract(contract) + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) ret, err = evm.interpreter.Run(contract, input, false) gas = contract.Gas @@ -393,7 +399,9 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b } else { // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) + contract := GetContract(caller, addr, new(uint256.Int), gas, evm.jumpDests) + defer ReturnContract(contract) + contract.SetCallCode(evm.resolveCodeHash(addr), evm.resolveCode(addr)) // When an error was returned by the EVM or when setting the creation code @@ -500,7 +508,8 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui // Initialise a new contract and set the code that is to be used by the EVM. // The contract is a scoped environment for this execution context only. - contract := NewContract(caller, address, value, gas, evm.jumpDests) + contract := GetContract(caller, address, value, gas, evm.jumpDests) + defer ReturnContract(contract) // Explicitly set the code to a null hash to prevent caching of jump analysis // for the initialization code. @@ -514,7 +523,8 @@ func (evm *EVM) create(caller common.Address, code []byte, gas uint64, value *ui contract.UseGas(contract.Gas, evm.Config.Tracer, tracing.GasChangeCallFailedExecution) } } - return ret, address, contract.Gas, err + leftOverGas = contract.Gas + return ret, address, leftOverGas, err } // initNewContract runs a new contract's creation code, performs checks on the diff --git a/core/vm/instructions_test.go b/core/vm/instructions_test.go index 0902d17c54..5e711c2957 100644 --- a/core/vm/instructions_test.go +++ b/core/vm/instructions_test.go @@ -567,10 +567,11 @@ func TestOpTstore(t *testing.T) { mem = NewMemory() caller = common.Address{} to = common.Address{1} - contract = NewContract(caller, to, new(uint256.Int), 0, nil) + contract = GetContract(caller, to, new(uint256.Int), 0, nil) scopeContext = ScopeContext{mem, stack, contract} value = common.Hex2Bytes("abcdef00000000000000abba000000000deaf000000c0de00100000000133700") ) + defer ReturnContract(contract) // Add a stateObject for the caller and the contract being called statedb.CreateAccount(caller) diff --git a/eth/tracers/js/tracer_test.go b/eth/tracers/js/tracer_test.go index a12b990a93..92a3961c75 100644 --- a/eth/tracers/js/tracer_test.go +++ b/eth/tracers/js/tracer_test.go @@ -55,8 +55,10 @@ func runTrace(tracer *tracers.Tracer, vmctx *vmContext, chaincfg *params.ChainCo gasLimit uint64 = 31000 startGas uint64 = 10000 value = uint256.NewInt(0) - contract = vm.NewContract(common.Address{}, common.Address{}, value, startGas, nil) + contract = vm.GetContract(common.Address{}, common.Address{}, value, startGas, nil) ) + defer vm.ReturnContract(contract) + evm.SetTxContext(vmctx.txCtx) contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0} if contractCode != nil { @@ -183,8 +185,10 @@ func TestHaltBetweenSteps(t *testing.T) { t.Fatal(err) } scope := &vm.ScopeContext{ - Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), + Contract: vm.GetContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), } + defer vm.ReturnContract(scope.Contract) + evm := vm.NewEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, chainConfig, vm.Config{Tracer: tracer.Hooks}) evm.SetTxContext(vm.TxContext{GasPrice: big.NewInt(1)}) tracer.OnTxStart(evm.GetVMContext(), types.NewTx(&types.LegacyTx{}), common.Address{}) @@ -281,8 +285,9 @@ func TestEnterExit(t *testing.T) { t.Fatal(err) } scope := &vm.ScopeContext{ - Contract: vm.NewContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), + Contract: vm.GetContract(common.Address{}, common.Address{}, uint256.NewInt(0), 0, nil), } + defer vm.ReturnContract(scope.Contract) tracer.OnEnter(1, byte(vm.CALL), scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int)) tracer.OnExit(1, []byte{}, 400, nil, false) diff --git a/eth/tracers/logger/logger_test.go b/eth/tracers/logger/logger_test.go index b1e38bf627..dd6927b9ed 100644 --- a/eth/tracers/logger/logger_test.go +++ b/eth/tracers/logger/logger_test.go @@ -43,8 +43,10 @@ func TestStoreCapture(t *testing.T) { var ( logger = NewStructLogger(nil) evm = vm.NewEVM(vm.BlockContext{}, &dummyStatedb{}, params.TestChainConfig, vm.Config{Tracer: logger.Hooks()}) - contract = vm.NewContract(common.Address{}, common.Address{}, new(uint256.Int), 100000, nil) + contract = vm.GetContract(common.Address{}, common.Address{}, new(uint256.Int), 100000, nil) ) + defer vm.ReturnContract(contract) + contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x0, byte(vm.SSTORE)} var index common.Hash logger.OnTxStart(evm.GetVMContext(), nil, common.Address{}) diff --git a/tests/state_test.go b/tests/state_test.go index 301bc3a7a9..8f334d5389 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -301,8 +301,8 @@ func runBenchmark(b *testing.B, t *StateTest) { evm.SetTxContext(txContext) // Create "contract" for sender to cache code analysis. - sender := vm.NewContract(msg.From, msg.From, nil, 0, nil) - + sender := vm.GetContract(msg.From, msg.From, nil, 0, nil) + defer vm.ReturnContract(sender) var ( gasUsed uint64 elapsed uint64