diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 253ebe1111..7a1877e731 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -282,6 +282,9 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, gaspool.Set(gp) continue } + // On success, ApplyTransactionWithEVM has already called + // StateDB.Finalise, which resets the journal and consumes any open + // snapshots. No CloseSnapshot here: the journal is empty by now. if receipt.Logs == nil { receipt.Logs = []*types.Log{} } diff --git a/core/state/journal.go b/core/state/journal.go index a82c3e87d7..acb7a2b7ab 100644 --- a/core/state/journal.go +++ b/core/state/journal.go @@ -152,8 +152,6 @@ func (j *journal) closeSnapshot(revid int) { // current (topmost) call frame, skipping entries that lie within any closed // child frame's range. Entries are visited in append order. If no frame is // open, frameEntries is a no-op. -// -// nolint:unused func (j *journal) frameEntries(visit func(entry journalEntry)) { if len(j.validRevisions) == 0 { return diff --git a/core/vm/evm.go b/core/vm/evm.go index 829321b876..8fc157da6c 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -561,6 +561,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas) if consumed < wanted { gas.Exhaust() + evm.StateDB.CloseSnapshot(snapshot) return nil, common.Address{}, gas, ErrOutOfGas } prior, _ := gas.Charge(GasCosts{RegularGas: consumed}) diff --git a/core/vm/interface.go b/core/vm/interface.go index 979c75eace..ebbd70e55b 100644 --- a/core/vm/interface.go +++ b/core/vm/interface.go @@ -77,23 +77,32 @@ type StateDB interface { AddressInAccessList(addr common.Address) bool SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) + // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform // even if the feature/fork is not active yet AddAddressToAccessList(addr common.Address) + // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform // even if the feature/fork is not active yet AddSlotToAccessList(addr common.Address, slot common.Hash) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) - RevertToSnapshot(int) + // Snapshot creates a journal revision before entering a child call frame, + // or whenever state transitions need to be applied or merged atomically. + // Each snapshot must be either closed with CloseSnapshot or reverted + // using RevertToSnapshot. + Snapshot() int // CloseSnapshot marks the given snapshot's call frame as completed without // reverting any state. The call frame's entry range is recorded on the // parent frame so the parent can later iterate its own entries while // skipping over closed children. Snapshots must be closed in LIFO order. CloseSnapshot(int) - Snapshot() int + + // RevertToSnapshot rolls back all state changes within the current frame + // and discards the corresponding journal entries. + RevertToSnapshot(int) AddLog(*types.Log) LogsForBurnAccounts() []*types.Log diff --git a/miner/worker.go b/miner/worker.go index 158f1b26c0..8ca4024b5e 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -423,6 +423,9 @@ func (miner *Miner) applyTransaction(env *environment, tx *types.Transaction) (* env.gasPool.Set(gp) return nil, err } + // On success, ApplyTransaction has already called StateDB.Finalise, which + // resets the journal and consumes any open snapshots. No CloseSnapshot + // here: the journal stack is empty by this point. env.header.GasUsed = env.gasPool.Used() return receipt, nil } diff --git a/tests/state_test_util.go b/tests/state_test_util.go index f7cf1c0697..e71feaaaba 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -350,6 +350,8 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh } return st, common.Hash{}, 0, err } + st.StateDB.CloseSnapshot(snapshot) + // Add 0-value mining reward. This only makes a difference in the cases // where // - the coinbase self-destructed, or