Skip to content

Commit 68b252b

Browse files
rjl493456442holiman
authored andcommitted
core, eth, internal, cmd: rework EVM constructor (ethereum#30745)
This pull request refactors the EVM constructor by removing the TxContext parameter. The EVM object is frequently overused. Ideally, only a single EVM instance should be created and reused throughout the entire state transition of a block, with the transaction context switched as needed by calling evm.SetTxContext. Unfortunately, in some parts of the code, the EVM object is repeatedly created, resulting in unnecessary complexity. This pull request is the first step towards gradually improving and simplifying this setup. --------- Co-authored-by: Martin Holst Swende <[email protected]>
1 parent 840c162 commit 68b252b

29 files changed

+220
-225
lines changed

cmd/evm/internal/t8ntool/execution.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,15 +201,14 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
201201
chainConfig.DAOForkBlock.Cmp(new(big.Int).SetUint64(pre.Env.Number)) == 0 {
202202
misc.ApplyDAOHardFork(statedb)
203203
}
204+
evm := vm.NewEVM(vmContext, statedb, chainConfig, vmConfig)
204205
if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil {
205-
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
206206
core.ProcessBeaconBlockRoot(*beaconRoot, evm, statedb)
207207
}
208208
if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) {
209209
var (
210210
prevNumber = pre.Env.Number - 1
211211
prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)]
212-
evm = vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vmConfig)
213212
)
214213
core.ProcessParentBlockHash(prevHash, evm, statedb)
215214
}
@@ -246,8 +245,10 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
246245
if err != nil {
247246
return nil, nil, nil, err
248247
}
248+
// TODO (rjl493456442) it's a bit weird to reset the tracer in the
249+
// middle of block execution, please improve it somehow.
249250
if tracer != nil {
250-
vmConfig.Tracer = tracer.Hooks
251+
evm.SetTracer(tracer.Hooks)
251252
}
252253
statedb.SetTxContext(tx.Hash(), txIndex)
253254

@@ -256,12 +257,12 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
256257
snapshot = statedb.Snapshot()
257258
prevGas = gaspool.Gas()
258259
)
259-
evm := vm.NewEVM(vmContext, txContext, statedb, chainConfig, vmConfig)
260-
261260
if tracer != nil && tracer.OnTxStart != nil {
262261
tracer.OnTxStart(evm.GetVMContext(), tx, msg.From)
263262
}
264263
// (ret []byte, usedGas uint64, failed bool, err error)
264+
265+
evm.SetTxContext(txContext)
265266
msgResult, err := core.ApplyMessage(evm, msg, gaspool)
266267
if err != nil {
267268
statedb.RevertToSnapshot(snapshot)
@@ -375,12 +376,11 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
375376
return nil, nil, nil, NewError(ErrorEVM, fmt.Errorf("could not parse requests logs: %v", err))
376377
}
377378
requests = append(requests, depositRequests)
378-
// create EVM for system calls
379-
vmenv := vm.NewEVM(vmContext, vm.TxContext{}, statedb, chainConfig, vm.Config{})
379+
380380
// EIP-7002 withdrawals
381-
requests = append(requests, core.ProcessWithdrawalQueue(vmenv, statedb))
381+
requests = append(requests, core.ProcessWithdrawalQueue(evm, statedb))
382382
// EIP-7251 consolidations
383-
requests = append(requests, core.ProcessConsolidationQueue(vmenv, statedb))
383+
requests = append(requests, core.ProcessConsolidationQueue(evm, statedb))
384384
}
385385

386386
// Commit block

core/chain_makers.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ func (b *BlockGen) SetParentBeaconRoot(root common.Hash) {
100100
b.header.ParentBeaconRoot = &root
101101
var (
102102
blockContext = NewEVMBlockContext(b.header, b.cm, &b.header.Coinbase)
103-
vmenv = vm.NewEVM(blockContext, vm.TxContext{}, b.statedb, b.cm.config, vm.Config{})
103+
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vm.Config{})
104104
)
105-
ProcessBeaconBlockRoot(root, vmenv, b.statedb)
105+
ProcessBeaconBlockRoot(root, evm, b.statedb)
106106
}
107107

108108
// addTx adds a transaction to the generated block. If no coinbase has
@@ -116,8 +116,12 @@ func (b *BlockGen) addTx(bc *BlockChain, vmConfig vm.Config, tx *types.Transacti
116116
if b.gasPool == nil {
117117
b.SetCoinbase(common.Address{})
118118
}
119+
var (
120+
blockContext = NewEVMBlockContext(b.header, bc, &b.header.Coinbase)
121+
evm = vm.NewEVM(blockContext, b.statedb, b.cm.config, vmConfig)
122+
)
119123
b.statedb.SetTxContext(tx.Hash(), len(b.txs))
120-
receipt, err := ApplyTransaction(b.cm.config, bc, &b.header.Coinbase, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed, vmConfig)
124+
receipt, err := ApplyTransaction(b.cm.config, evm, b.gasPool, b.statedb, b.header, tx, &b.header.GasUsed)
121125
if err != nil {
122126
panic(err)
123127
}
@@ -360,12 +364,12 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
360364
requests = append(requests, depositRequests)
361365
// create EVM for system calls
362366
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
363-
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, cm.config, vm.Config{})
367+
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
364368
// EIP-7002 withdrawals
365-
withdrawalRequests := ProcessWithdrawalQueue(vmenv, statedb)
369+
withdrawalRequests := ProcessWithdrawalQueue(evm, statedb)
366370
requests = append(requests, withdrawalRequests)
367371
// EIP-7251 consolidations
368-
consolidationRequests := ProcessConsolidationQueue(vmenv, statedb)
372+
consolidationRequests := ProcessConsolidationQueue(evm, statedb)
369373
requests = append(requests, consolidationRequests)
370374
}
371375
if requests != nil {
@@ -466,8 +470,8 @@ func GenerateVerkleChain(config *params.ChainConfig, parent *types.Block, engine
466470
if config.IsPrague(b.header.Number, b.header.Time) {
467471
// EIP-2935
468472
blockContext := NewEVMBlockContext(b.header, cm, &b.header.Coinbase)
469-
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, cm.config, vm.Config{})
470-
ProcessParentBlockHash(b.header.ParentHash, vmenv, statedb)
473+
evm := vm.NewEVM(blockContext, statedb, cm.config, vm.Config{})
474+
ProcessParentBlockHash(b.header.ParentHash, evm, statedb)
471475
}
472476

473477
// Execute any user modifications to the block.

core/state_prefetcher.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
4949
header = block.Header()
5050
gaspool = new(GasPool).AddGas(block.GasLimit())
5151
blockContext = NewEVMBlockContext(header, p.chain, nil)
52-
evm = vm.NewEVM(blockContext, vm.TxContext{}, statedb, p.config, cfg)
52+
evm = vm.NewEVM(blockContext, statedb, p.config, cfg)
5353
signer = types.MakeSigner(p.config, header.Number, header.Time)
5454
)
5555
// Iterate over and process the individual transactions
@@ -65,7 +65,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
6565
return // Also invalid block, bail out
6666
}
6767
statedb.SetTxContext(tx.Hash(), i)
68-
if err := precacheTransaction(msg, p.config, gaspool, statedb, header, evm); err != nil {
68+
if err := precacheTransaction(msg, gaspool, evm); err != nil {
6969
return // Ugh, something went horribly wrong, bail out
7070
}
7171
// If we're pre-byzantium, pre-load trie nodes for the intermediate root
@@ -82,9 +82,9 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c
8282
// precacheTransaction attempts to apply a transaction to the given state database
8383
// and uses the input parameters for its environment. The goal is not to execute
8484
// the transaction successfully, rather to warm up touched data slots.
85-
func precacheTransaction(msg *Message, config *params.ChainConfig, gaspool *GasPool, statedb *state.StateDB, header *types.Header, evm *vm.EVM) error {
85+
func precacheTransaction(msg *Message, gaspool *GasPool, evm *vm.EVM) error {
8686
// Update the evm with the new transaction context.
87-
evm.Reset(NewEVMTxContext(msg), statedb)
87+
evm.SetTxContext(NewEVMTxContext(msg))
8888
// Add addresses to access list if applicable
8989
_, err := ApplyMessage(evm, msg, gaspool)
9090
return err

core/state_processor.go

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
7474
)
7575

7676
// Apply pre-execution system calls.
77-
context = NewEVMBlockContext(header, p.chain, nil)
78-
79-
vmenv := vm.NewEVM(context, vm.TxContext{}, statedb, p.config, cfg)
8077
var tracingStateDB = vm.StateDB(statedb)
8178
if hooks := cfg.Tracer; hooks != nil {
8279
tracingStateDB = state.NewHookedState(statedb, hooks)
8380
}
81+
context = NewEVMBlockContext(header, p.chain, nil)
82+
evm := vm.NewEVM(context, tracingStateDB, p.config, cfg)
83+
8484
if beaconRoot := block.BeaconRoot(); beaconRoot != nil {
85-
ProcessBeaconBlockRoot(*beaconRoot, vmenv, tracingStateDB)
85+
ProcessBeaconBlockRoot(*beaconRoot, evm, tracingStateDB)
8686
}
8787
if p.config.IsPrague(block.Number(), block.Time()) {
88-
ProcessParentBlockHash(block.ParentHash(), vmenv, tracingStateDB)
88+
ProcessParentBlockHash(block.ParentHash(), evm, tracingStateDB)
8989
}
9090

9191
// Iterate over and process the individual transactions
@@ -96,7 +96,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
9696
}
9797
statedb.SetTxContext(tx.Hash(), i)
9898

99-
receipt, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, vmenv)
99+
receipt, err := ApplyTransactionWithEVM(msg, p.config, gp, statedb, blockNumber, blockHash, tx, usedGas, evm)
100100
if err != nil {
101101
return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err)
102102
}
@@ -113,10 +113,10 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
113113
}
114114
requests = append(requests, depositRequests)
115115
// EIP-7002 withdrawals
116-
withdrawalRequests := ProcessWithdrawalQueue(vmenv, tracingStateDB)
116+
withdrawalRequests := ProcessWithdrawalQueue(evm, tracingStateDB)
117117
requests = append(requests, withdrawalRequests)
118118
// EIP-7251 consolidations
119-
consolidationRequests := ProcessConsolidationQueue(vmenv, tracingStateDB)
119+
consolidationRequests := ProcessConsolidationQueue(evm, tracingStateDB)
120120
requests = append(requests, consolidationRequests)
121121
}
122122

@@ -135,9 +135,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
135135
// and uses the input parameters for its environment similar to ApplyTransaction. However,
136136
// this method takes an already created EVM instance as input.
137137
func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) {
138-
var tracingStateDB = vm.StateDB(statedb)
139138
if hooks := evm.Config.Tracer; hooks != nil {
140-
tracingStateDB = state.NewHookedState(statedb, hooks)
141139
if hooks.OnTxStart != nil {
142140
hooks.OnTxStart(evm.GetVMContext(), tx, msg.From)
143141
}
@@ -148,7 +146,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
148146

149147
// Create a new context to be used in the EVM environment.
150148
txContext := NewEVMTxContext(msg)
151-
evm.Reset(txContext, tracingStateDB)
149+
evm.SetTxContext(txContext)
152150

153151
// Apply the transaction to the current state (included in the env).
154152
result, err := ApplyMessage(evm, msg, gp)
@@ -159,7 +157,7 @@ func ApplyTransactionWithEVM(msg *Message, config *params.ChainConfig, gp *GasPo
159157
// Update the state with pending changes.
160158
var root []byte
161159
if config.IsByzantium(blockNumber) {
162-
tracingStateDB.Finalise(true)
160+
evm.StateDB.Finalise(true)
163161
} else {
164162
root = statedb.IntermediateRoot(config.IsEIP158(blockNumber)).Bytes()
165163
}
@@ -210,16 +208,13 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b
210208
// and uses the input parameters for its environment. It returns the receipt
211209
// for the transaction, gas used and an error if the transaction failed,
212210
// indicating the block was invalid.
213-
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) {
211+
func ApplyTransaction(config *params.ChainConfig, evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) {
214212
msg, err := TransactionToMessage(tx, types.MakeSigner(config, header.Number, header.Time), header.BaseFee)
215213
if err != nil {
216214
return nil, err
217215
}
218216
// Create a new context to be used in the EVM environment
219-
blockContext := NewEVMBlockContext(header, bc, author)
220-
txContext := NewEVMTxContext(msg)
221-
vmenv := vm.NewEVM(blockContext, txContext, statedb, config, cfg)
222-
return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, vmenv)
217+
return ApplyTransactionWithEVM(msg, config, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm)
223218
}
224219

225220
// ProcessBeaconBlockRoot applies the EIP-4788 system call to the beacon block root
@@ -242,7 +237,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, vmenv *vm.EVM, statedb vm.St
242237
To: &params.BeaconRootsAddress,
243238
Data: beaconRoot[:],
244239
}
245-
vmenv.Reset(NewEVMTxContext(msg), statedb)
240+
vmenv.SetTxContext(NewEVMTxContext(msg))
246241
statedb.AddAddressToAccessList(params.BeaconRootsAddress)
247242
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
248243
statedb.Finalise(true)
@@ -268,7 +263,7 @@ func ProcessParentBlockHash(prevHash common.Hash, vmenv *vm.EVM, statedb vm.Stat
268263
To: &params.HistoryStorageAddress,
269264
Data: prevHash.Bytes(),
270265
}
271-
vmenv.Reset(NewEVMTxContext(msg), statedb)
266+
vmenv.SetTxContext(NewEVMTxContext(msg))
272267
statedb.AddAddressToAccessList(params.HistoryStorageAddress)
273268
_, _, _ = vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
274269
statedb.Finalise(true)
@@ -304,7 +299,7 @@ func processRequestsSystemCall(vmenv *vm.EVM, statedb vm.StateDB, requestType by
304299
GasTipCap: common.Big0,
305300
To: &addr,
306301
}
307-
vmenv.Reset(NewEVMTxContext(msg), statedb)
302+
vmenv.SetTxContext(NewEVMTxContext(msg))
308303
statedb.AddAddressToAccessList(addr)
309304
ret, _, _ := vmenv.Call(vm.AccountRef(msg.From), *msg.To, msg.Data, 30_000_000, common.U2560)
310305
statedb.Finalise(true)

core/verkle_witness_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ func TestProcessParentBlockHash(t *testing.T) {
225225
for i := 1; i <= num; i++ {
226226
header := &types.Header{ParentHash: common.Hash{byte(i)}, Number: big.NewInt(int64(i)), Difficulty: new(big.Int)}
227227
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
228-
evm := vm.NewEVM(vmContext, vm.TxContext{}, statedb, params.MergedTestChainConfig, vm.Config{})
228+
evm := vm.NewEVM(vmContext, statedb, params.MergedTestChainConfig, vm.Config{})
229229
ProcessParentBlockHash(header.ParentHash, evm, statedb)
230230
}
231231
// Read block hashes for block 0 .. num-1

core/vm/evm.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,13 @@ type EVM struct {
116116
precompiles map[common.Address]PrecompiledContract
117117
}
118118

119-
// NewEVM returns a new EVM. The returned EVM is not thread safe and should
120-
// only ever be used *once*.
121-
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
119+
// NewEVM constructs an EVM instance with the supplied block context, state
120+
// database and several configs. It meant to be used throughout the entire
121+
// state transition of a block, with the transaction context switched as
122+
// needed by calling evm.SetTxContext.
123+
func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
122124
evm := &EVM{
123125
Context: blockCtx,
124-
TxContext: txCtx,
125126
StateDB: statedb,
126127
Config: config,
127128
chainConfig: chainConfig,
@@ -132,21 +133,25 @@ func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig
132133
return evm
133134
}
134135

136+
// SetTracer sets the tracer for following state transition.
137+
func (evm *EVM) SetTracer(tracer *tracing.Hooks) {
138+
evm.Config.Tracer = tracer
139+
}
140+
135141
// SetPrecompiles sets the precompiled contracts for the EVM.
136142
// This method is only used through RPC calls.
137143
// It is not thread-safe.
138144
func (evm *EVM) SetPrecompiles(precompiles PrecompiledContracts) {
139145
evm.precompiles = precompiles
140146
}
141147

142-
// Reset resets the EVM with a new transaction context.Reset
148+
// SetTxContext resets the EVM with a new transaction context.
143149
// This is not threadsafe and should only be done very cautiously.
144-
func (evm *EVM) Reset(txCtx TxContext, statedb StateDB) {
150+
func (evm *EVM) SetTxContext(txCtx TxContext) {
145151
if evm.chainRules.IsEIP4762 {
146-
txCtx.AccessEvents = state.NewAccessEvents(statedb.PointCache())
152+
txCtx.AccessEvents = state.NewAccessEvents(evm.StateDB.PointCache())
147153
}
148154
evm.TxContext = txCtx
149-
evm.StateDB = statedb
150155
}
151156

152157
// Cancel cancels any running EVM operation. This may be called concurrently and

core/vm/gas_table_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,16 +95,16 @@ func TestEIP2200(t *testing.T) {
9595
CanTransfer: func(StateDB, common.Address, *uint256.Int) bool { return true },
9696
Transfer: func(StateDB, common.Address, common.Address, *uint256.Int) {},
9797
}
98-
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
98+
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, Config{ExtraEips: []int{2200}})
9999

100-
_, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
100+
_, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, tt.gaspool, new(uint256.Int))
101101
if !errors.Is(err, tt.failure) {
102102
t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure)
103103
}
104104
if used := tt.gaspool - gas; used != tt.used {
105105
t.Errorf("test %d: gas used mismatch: have %v, want %v", i, used, tt.used)
106106
}
107-
if refund := vmenv.StateDB.GetRefund(); refund != tt.refund {
107+
if refund := evm.StateDB.GetRefund(); refund != tt.refund {
108108
t.Errorf("test %d: gas refund mismatch: have %v, want %v", i, refund, tt.refund)
109109
}
110110
}
@@ -151,9 +151,9 @@ func TestCreateGas(t *testing.T) {
151151
config.ExtraEips = []int{3860}
152152
}
153153

154-
vmenv := NewEVM(vmctx, TxContext{}, statedb, params.AllEthashProtocolChanges, config)
154+
evm := NewEVM(vmctx, statedb, params.AllEthashProtocolChanges, config)
155155
var startGas = uint64(testGas)
156-
ret, gas, err := vmenv.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int))
156+
ret, gas, err := evm.Call(AccountRef(common.Address{}), address, nil, startGas, new(uint256.Int))
157157
if err != nil {
158158
return false
159159
}

0 commit comments

Comments
 (0)