core, miner, cmd, tests: ensure snapshot is properly closed

This commit is contained in:
Gary Rong 2026-04-29 15:10:57 +08:00
parent 988454463a
commit f8eb88e94a
6 changed files with 20 additions and 4 deletions

View file

@ -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{}
}

View file

@ -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

View file

@ -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})

View file

@ -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

View file

@ -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
}

View file

@ -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