mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
core/state: use journaling approach
This commit is contained in:
parent
46be549b00
commit
c1b1faed8b
6 changed files with 47 additions and 60 deletions
|
|
@ -216,33 +216,15 @@ func (j *journal) length() int {
|
|||
return len(j.entries)
|
||||
}
|
||||
|
||||
// stateChangedBytes computes the state bytes created by the call frame
|
||||
// identified by snapshotId. Since subcalls always compute their results
|
||||
// before the parent (innermost-first), this only scans journal entries
|
||||
// between this snapshot and the next one — the frame's own entries.
|
||||
// Subcall results are summed from the cache and subtracted.
|
||||
//
|
||||
// The result is cached in stateBytesCharged[snapshotId] so the parent
|
||||
// frame can look it up instead of re-scanning.
|
||||
func (j *journal) stateChangedBytes(snapshotId int, stateObjects map[common.Address]*stateObject) int64 {
|
||||
// TODO (MariusVanDerWijden): this is a bit slop-py needs to be cleaned up
|
||||
// Resolve snapshot index.
|
||||
idx := sort.Search(len(j.validRevisions), func(i int) bool {
|
||||
return j.validRevisions[i].id >= snapshotId
|
||||
})
|
||||
if idx == len(j.validRevisions) || j.validRevisions[idx].id != snapshotId {
|
||||
panic(fmt.Errorf("snapshot id %v not found for stateChangedBytes", snapshotId))
|
||||
// stateChangedBytes computes the state bytes created by the current (topmost)
|
||||
// call frame, walking only entries that belong directly to this frame and
|
||||
// skipping over closed child frame ranges.
|
||||
func (j *journal) stateChangedBytes(stateObjects map[common.Address]*stateObject) int64 {
|
||||
if len(j.validRevisions) == 0 {
|
||||
return 0
|
||||
}
|
||||
start := j.validRevisions[idx].journalIndex
|
||||
rev := j.validRevisions[len(j.validRevisions)-1]
|
||||
|
||||
// Our range is [start, end) where end is the next revision's start,
|
||||
// or the end of the journal if we're the last revision.
|
||||
end := len(j.entries)
|
||||
if idx+1 < len(j.validRevisions) {
|
||||
end = j.validRevisions[idx+1].journalIndex
|
||||
}
|
||||
|
||||
// Walk only our own entries.
|
||||
type slotKey struct {
|
||||
addr common.Address
|
||||
key common.Hash
|
||||
|
|
@ -255,8 +237,11 @@ func (j *journal) stateChangedBytes(snapshotId int, stateObjects map[common.Addr
|
|||
created := make(map[common.Address]bool)
|
||||
codeChanged := make(map[common.Address]bool)
|
||||
|
||||
for i := start; i < end; i++ {
|
||||
switch e := j.entries[i].(type) {
|
||||
// Walk only this frame's own entries, skipping closed child ranges.
|
||||
// Add cached subcall costs from closedChildren.
|
||||
var subcallBytes int64
|
||||
visit := func(e journalEntry) {
|
||||
switch e := e.(type) {
|
||||
case createContractChange:
|
||||
created[e.account] = true
|
||||
case codeChange:
|
||||
|
|
@ -268,6 +253,18 @@ func (j *journal) stateChangedBytes(snapshotId int, stateObjects map[common.Addr
|
|||
}
|
||||
}
|
||||
}
|
||||
idx := rev.journalIndex
|
||||
for _, child := range rev.closedChildren {
|
||||
for ; idx < child.start; idx++ {
|
||||
visit(j.entries[idx])
|
||||
}
|
||||
// Add the cached cost for this subcall.
|
||||
subcallBytes += j.stateBytesCharged[child.start]
|
||||
idx = child.end
|
||||
}
|
||||
for ; idx < len(j.entries); idx++ {
|
||||
visit(j.entries[idx])
|
||||
}
|
||||
|
||||
var totalBytes int64
|
||||
for range created {
|
||||
|
|
@ -296,7 +293,7 @@ func (j *journal) stateChangedBytes(snapshotId int, stateObjects map[common.Addr
|
|||
// cleared in earlier frame, re-set here — no charge.
|
||||
// - X → Y (non-zero to non-zero): no charge.
|
||||
// - zero → zero: no change.
|
||||
// - !prevZero && curZero && !origZero: pre-exising slot was
|
||||
// - !prevZero && curZero && !origZero: pre-existing slot was
|
||||
// cleared now, don't refund to not enable gas tokens.
|
||||
}
|
||||
for addr := range codeChanged {
|
||||
|
|
@ -306,8 +303,11 @@ func (j *journal) stateChangedBytes(snapshotId int, stateObjects map[common.Addr
|
|||
}
|
||||
}
|
||||
|
||||
// Cache our result so the parent can look it up.
|
||||
j.stateBytesCharged[snapshotId] = totalBytes
|
||||
// Add subcall costs to get the total for this frame (own + children).
|
||||
totalBytes += subcallBytes
|
||||
|
||||
// Cache so the parent can look up this frame's total cost.
|
||||
j.stateBytesCharged[rev.journalIndex] = totalBytes
|
||||
return totalBytes
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -772,11 +772,10 @@ const (
|
|||
CostPerSlot = 32
|
||||
)
|
||||
|
||||
// StateChangedBytes computes the state bytes created since the given snapshot,
|
||||
// excluding bytes already charged by subcalls. See journal.stateChangedBytes
|
||||
// for the detailed accounting.
|
||||
func (s *StateDB) StateChangedBytes(snapshotId int) int64 {
|
||||
return s.journal.stateChangedBytes(snapshotId, s.stateObjects)
|
||||
// StateChangedBytes computes the state bytes created by the current (topmost)
|
||||
// call frame, excluding entries from closed child frames.
|
||||
func (s *StateDB) StateChangedBytes() int64 {
|
||||
return s.journal.stateChangedBytes(s.stateObjects)
|
||||
}
|
||||
|
||||
type removedAccountWithBalance struct {
|
||||
|
|
|
|||
|
|
@ -293,6 +293,6 @@ func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) *bal.StateAccessList {
|
|||
return s.inner.Finalise(deleteEmptyObjects)
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) StateChangedBytes(snapshotId int) int64 {
|
||||
return s.inner.StateChangedBytes(snapshotId)
|
||||
func (s *hookedStateDB) StateChangedBytes() int64 {
|
||||
return s.inner.StateChangedBytes()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -598,10 +598,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
ret []byte
|
||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||
)
|
||||
// EIP-8037: Take a snapshot for the outer call frame so we can compute
|
||||
// state gas for state changes made at the transaction level (nonce,
|
||||
// value transfer, authorizations, and contract creation overhead).
|
||||
outerSnapshot := st.state.Snapshot()
|
||||
|
||||
// Take a snapshot for gas calculation
|
||||
st.state.Snapshot()
|
||||
|
||||
var execGasUsed vm.GasUsed
|
||||
if contractCreation {
|
||||
|
|
@ -634,7 +633,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// EIP-8037: charge state gas for the outer call frame's own state changes.
|
||||
if rules.IsAmsterdam {
|
||||
if vmerr == nil {
|
||||
outerBytes := st.state.StateChangedBytes(outerSnapshot)
|
||||
outerBytes := st.state.StateChangedBytes()
|
||||
st.gasRemaining.Charge(vm.GasCosts{StateGas: outerBytes * int64(st.evm.Context.CostPerStateByte)})
|
||||
} else {
|
||||
if execGasUsed.StateGas > 0 {
|
||||
|
|
|
|||
|
|
@ -281,11 +281,6 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
}
|
||||
evm.StateDB.CreateAccount(addr)
|
||||
}
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Compute state changed bytes for account creation.
|
||||
evm.StateDB.StateChangedBytes(snapshot)
|
||||
}
|
||||
innerSnapshot := evm.StateDB.Snapshot()
|
||||
// Perform the value transfer only in non-syscall mode.
|
||||
// Calling this is required even for zero-value transfers,
|
||||
// to ensure the state clearing mechanism is applied.
|
||||
|
|
@ -327,7 +322,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
evm.StateDB.CloseSnapshot(snapshot)
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Charge state costs
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(innerSnapshot)
|
||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
if !gas.CanAfford(stateGasCost) {
|
||||
gas.Exhaust()
|
||||
|
|
@ -389,7 +384,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
} else {
|
||||
evm.StateDB.CloseSnapshot(snapshot)
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot)
|
||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
if !gas.CanAfford(stateGasCost) {
|
||||
gas.Exhaust()
|
||||
|
|
@ -444,7 +439,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
} else {
|
||||
evm.StateDB.CloseSnapshot(snapshot)
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(snapshot)
|
||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
if !gas.CanAfford(stateGasCost) {
|
||||
gas.Exhaust()
|
||||
|
|
@ -599,12 +594,6 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
}
|
||||
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
|
||||
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Compute the state changed for the contract init.
|
||||
evm.StateDB.StateChangedBytes(snapshot)
|
||||
}
|
||||
initSnapshot := evm.StateDB.Snapshot()
|
||||
|
||||
// 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)
|
||||
|
|
@ -624,7 +613,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
evm.StateDB.CloseSnapshot(snapshot)
|
||||
if evm.chainRules.IsAmsterdam {
|
||||
// Charge initcode's state changes to the created contract's gas.
|
||||
bytesCharged := evm.StateDB.StateChangedBytes(initSnapshot)
|
||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||
if !contract.Gas.CanAfford(stateGasCost) {
|
||||
contract.Gas.Exhaust()
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ type StateDB interface {
|
|||
// Finalise must be invoked at the end of a transaction
|
||||
Finalise(bool) *bal.StateAccessList
|
||||
|
||||
// StateChangedBytes returns the number of state bytes created since the
|
||||
// given snapshot. Used by EIP-8037 for state gas metering.
|
||||
StateChangedBytes(snapshotId int) int64
|
||||
// StateChangedBytes returns the number of state bytes created by the
|
||||
// current call frame. Used by EIP-8037 for state gas metering.
|
||||
StateChangedBytes() int64
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue