Skip to content

core/vm: implement contract pool #32009

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions core/vm/contract_pool.go
Original file line number Diff line number Diff line change
@@ -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)
}
22 changes: 16 additions & 6 deletions core/vm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion core/vm/instructions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
11 changes: 8 additions & 3 deletions eth/tracers/js/tracer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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{})
Expand Down Expand Up @@ -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)

Expand Down
4 changes: 3 additions & 1 deletion eth/tracers/logger/logger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{})
Expand Down
4 changes: 2 additions & 2 deletions tests/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down