mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-11 09:21:37 +00:00
core/state: charge account creation to parent
This commit is contained in:
parent
c1b1faed8b
commit
87641c7265
6 changed files with 73 additions and 43 deletions
|
|
@ -216,14 +216,23 @@ func (j *journal) length() int {
|
||||||
return len(j.entries)
|
return len(j.entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
// stateChangedBytes computes the state bytes created by the current (topmost)
|
// stateChangedBytes computes the state bytes created by the call frame
|
||||||
// call frame, walking only entries that belong directly to this frame and
|
// identified by revid, walking only entries that belong directly to this
|
||||||
// skipping over closed child frame ranges.
|
// frame and skipping over closed child frame ranges. The result is cached
|
||||||
func (j *journal) stateChangedBytes(stateObjects map[common.Address]*stateObject) int64 {
|
// in stateBytesCharged so that parent frames can look it up.
|
||||||
if len(j.validRevisions) == 0 {
|
//
|
||||||
return 0
|
// When excludeSubcalls is true, cached subcall costs are not added to the
|
||||||
|
// total. This is useful when subcalls have already been charged to their
|
||||||
|
// own gas budgets and shouldn't bubble up to the ancestor frames.
|
||||||
|
func (j *journal) stateChangedBytes(revid int, stateObjects map[common.Address]*stateObject, excludeSubcalls bool) int64 {
|
||||||
|
// Find the revision by ID.
|
||||||
|
idx := sort.Search(len(j.validRevisions), func(i int) bool {
|
||||||
|
return j.validRevisions[i].id >= revid
|
||||||
|
})
|
||||||
|
if idx == len(j.validRevisions) || j.validRevisions[idx].id != revid {
|
||||||
|
panic(fmt.Errorf("revision id %v not found for stateChangedBytes", revid))
|
||||||
}
|
}
|
||||||
rev := j.validRevisions[len(j.validRevisions)-1]
|
rev := j.validRevisions[idx]
|
||||||
|
|
||||||
type slotKey struct {
|
type slotKey struct {
|
||||||
addr common.Address
|
addr common.Address
|
||||||
|
|
@ -253,17 +262,19 @@ func (j *journal) stateChangedBytes(stateObjects map[common.Address]*stateObject
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
idx := rev.journalIndex
|
pos := rev.journalIndex
|
||||||
for _, child := range rev.closedChildren {
|
for _, child := range rev.closedChildren {
|
||||||
for ; idx < child.start; idx++ {
|
for ; pos < child.start; pos++ {
|
||||||
visit(j.entries[idx])
|
visit(j.entries[pos])
|
||||||
}
|
}
|
||||||
// Add the cached cost for this subcall.
|
if !excludeSubcalls {
|
||||||
subcallBytes += j.stateBytesCharged[child.start]
|
// Add the cached cost for this subcall.
|
||||||
idx = child.end
|
subcallBytes += j.stateBytesCharged[child.start]
|
||||||
|
}
|
||||||
|
pos = child.end
|
||||||
}
|
}
|
||||||
for ; idx < len(j.entries); idx++ {
|
for ; pos < len(j.entries); pos++ {
|
||||||
visit(j.entries[idx])
|
visit(j.entries[pos])
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalBytes int64
|
var totalBytes int64
|
||||||
|
|
|
||||||
|
|
@ -772,10 +772,11 @@ const (
|
||||||
CostPerSlot = 32
|
CostPerSlot = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateChangedBytes computes the state bytes created by the current (topmost)
|
// StateChangedBytes computes the state bytes created by the call frame
|
||||||
// call frame, excluding entries from closed child frames.
|
// identified by revid, excluding entries from closed child frames. When
|
||||||
func (s *StateDB) StateChangedBytes() int64 {
|
// excludeSubcalls is true, cached subcall costs are not added to the total.
|
||||||
return s.journal.stateChangedBytes(s.stateObjects)
|
func (s *StateDB) StateChangedBytes(revid int, excludeSubcalls bool) int64 {
|
||||||
|
return s.journal.stateChangedBytes(revid, s.stateObjects, excludeSubcalls)
|
||||||
}
|
}
|
||||||
|
|
||||||
type removedAccountWithBalance struct {
|
type removedAccountWithBalance struct {
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,6 @@ func (s *hookedStateDB) Finalise(deleteEmptyObjects bool) *bal.StateAccessList {
|
||||||
return s.inner.Finalise(deleteEmptyObjects)
|
return s.inner.Finalise(deleteEmptyObjects)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *hookedStateDB) StateChangedBytes() int64 {
|
func (s *hookedStateDB) StateChangedBytes(revid int, excludeSubcalls bool) int64 {
|
||||||
return s.inner.StateChangedBytes()
|
return s.inner.StateChangedBytes(revid, excludeSubcalls)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -600,7 +600,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Take a snapshot for gas calculation
|
// Take a snapshot for gas calculation
|
||||||
st.state.Snapshot()
|
outerSnapshot := st.state.Snapshot()
|
||||||
|
|
||||||
var execGasUsed vm.GasUsed
|
var execGasUsed vm.GasUsed
|
||||||
if contractCreation {
|
if contractCreation {
|
||||||
|
|
@ -633,7 +633,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
// EIP-8037: charge state gas for the outer call frame's own state changes.
|
// EIP-8037: charge state gas for the outer call frame's own state changes.
|
||||||
if rules.IsAmsterdam {
|
if rules.IsAmsterdam {
|
||||||
if vmerr == nil {
|
if vmerr == nil {
|
||||||
outerBytes := st.state.StateChangedBytes()
|
outerBytes := st.state.StateChangedBytes(outerSnapshot, false)
|
||||||
st.gasRemaining.Charge(vm.GasCosts{StateGas: outerBytes * int64(st.evm.Context.CostPerStateByte)})
|
st.gasRemaining.Charge(vm.GasCosts{StateGas: outerBytes * int64(st.evm.Context.CostPerStateByte)})
|
||||||
} else {
|
} else {
|
||||||
if execGasUsed.StateGas > 0 {
|
if execGasUsed.StateGas > 0 {
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
if !syscall && !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
if !syscall && !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||||
return nil, gas, ErrInsufficientBalance
|
return nil, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
snapshot := evm.StateDB.Snapshot()
|
snapshot1 := evm.StateDB.Snapshot()
|
||||||
p, isPrecompile := evm.precompile(addr)
|
p, isPrecompile := evm.precompile(addr)
|
||||||
if !evm.StateDB.Exist(addr) {
|
if !evm.StateDB.Exist(addr) {
|
||||||
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
||||||
|
|
@ -268,7 +268,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
// Thus, only pay for the creation of the code hash leaf here.
|
// Thus, only pay for the creation of the code hash leaf here.
|
||||||
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
|
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
|
||||||
if _, ok := gas.Charge(GasCosts{RegularGas: wgas}); !ok {
|
if _, ok := gas.Charge(GasCosts{RegularGas: wgas}); !ok {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
return nil, gas, ErrOutOfGas
|
return nil, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
|
|
@ -276,7 +276,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
|
|
||||||
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
if !isPrecompile && evm.chainRules.IsEIP158 && value.IsZero() {
|
||||||
// Calling a non-existing account, don't do anything.
|
// Calling a non-existing account, don't do anything.
|
||||||
evm.StateDB.CloseSnapshot(snapshot)
|
evm.StateDB.CloseSnapshot(snapshot1)
|
||||||
return nil, gas, nil
|
return nil, gas, nil
|
||||||
}
|
}
|
||||||
evm.StateDB.CreateAccount(addr)
|
evm.StateDB.CreateAccount(addr)
|
||||||
|
|
@ -288,6 +288,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
evm.Context.Transfer(evm.StateDB, caller, addr, value, &evm.chainRules)
|
evm.Context.Transfer(evm.StateDB, caller, addr, value, &evm.chainRules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Second snapshot: callee execution frame.
|
||||||
|
snapshot2 := evm.StateDB.Snapshot()
|
||||||
|
|
||||||
if isPrecompile {
|
if isPrecompile {
|
||||||
ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
ret, gas, err = RunPrecompiledContract(evm.StateDB, p, addr, input, gas, evm.Config.Tracer, evm.chainRules)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -308,28 +311,31 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
// above we revert to the snapshot and consume any gas remaining. Additionally,
|
||||||
// when we're in homestead this also counts for code storage gas errors.
|
// when we're in homestead this also counts for code storage gas errors.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
// TODO: consider clearing up unused snapshots:
|
|
||||||
//} else {
|
|
||||||
// evm.StateDB.DiscardSnapshot(snapshot)
|
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.CloseSnapshot(snapshot)
|
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
// Charge state costs
|
// Charge callee's state changes to the callee's gas.
|
||||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
bytesCharged := evm.StateDB.StateChangedBytes(snapshot2, false)
|
||||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||||
if !gas.CanAfford(stateGasCost) {
|
if !gas.CanAfford(stateGasCost) {
|
||||||
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
return ret, gas, ErrOutOfGas
|
return ret, gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
gas.Charge(stateGasCost)
|
gas.Charge(stateGasCost)
|
||||||
}
|
}
|
||||||
|
evm.StateDB.CloseSnapshot(snapshot2)
|
||||||
|
if evm.chainRules.IsAmsterdam {
|
||||||
|
// Cache parents costs (excluding subcalls)
|
||||||
|
evm.StateDB.StateChangedBytes(snapshot1, true)
|
||||||
|
}
|
||||||
|
evm.StateDB.CloseSnapshot(snapshot1)
|
||||||
}
|
}
|
||||||
return ret, gas, err
|
return ret, gas, err
|
||||||
}
|
}
|
||||||
|
|
@ -382,9 +388,8 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.CloseSnapshot(snapshot)
|
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
bytesCharged := evm.StateDB.StateChangedBytes(snapshot, false)
|
||||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||||
if !gas.CanAfford(stateGasCost) {
|
if !gas.CanAfford(stateGasCost) {
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
|
|
@ -392,6 +397,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
}
|
}
|
||||||
gas.Charge(stateGasCost)
|
gas.Charge(stateGasCost)
|
||||||
}
|
}
|
||||||
|
evm.StateDB.CloseSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
return ret, gas, err
|
return ret, gas, err
|
||||||
}
|
}
|
||||||
|
|
@ -437,9 +443,8 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.CloseSnapshot(snapshot)
|
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
bytesCharged := evm.StateDB.StateChangedBytes(snapshot, false)
|
||||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||||
if !gas.CanAfford(stateGasCost) {
|
if !gas.CanAfford(stateGasCost) {
|
||||||
gas.Exhaust()
|
gas.Exhaust()
|
||||||
|
|
@ -447,6 +452,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
}
|
}
|
||||||
gas.Charge(stateGasCost)
|
gas.Charge(stateGasCost)
|
||||||
}
|
}
|
||||||
|
evm.StateDB.CloseSnapshot(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, gas, err
|
return ret, gas, err
|
||||||
|
|
@ -567,7 +573,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
// Create a new account on the state only if the object was not present.
|
// Create a new account on the state only if the object was not present.
|
||||||
// It might be possible the contract code is deployed to a pre-existent
|
// It might be possible the contract code is deployed to a pre-existent
|
||||||
// account with non-zero balance.
|
// account with non-zero balance.
|
||||||
snapshot := evm.StateDB.Snapshot()
|
snapshot1 := evm.StateDB.Snapshot()
|
||||||
if !evm.StateDB.Exist(address) {
|
if !evm.StateDB.Exist(address) {
|
||||||
evm.StateDB.CreateAccount(address)
|
evm.StateDB.CreateAccount(address)
|
||||||
}
|
}
|
||||||
|
|
@ -594,6 +600,9 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
}
|
}
|
||||||
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
|
evm.Context.Transfer(evm.StateDB, caller, address, value, &evm.chainRules)
|
||||||
|
|
||||||
|
// Second snapshot: initcode execution frame.
|
||||||
|
snapshot2 := evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
// 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.
|
// The contract is a scoped environment for this execution context only.
|
||||||
contract := NewContract(caller, address, value, gas, evm.jumpDests)
|
contract := NewContract(caller, address, value, gas, evm.jumpDests)
|
||||||
|
|
@ -605,22 +614,29 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
|
|
||||||
ret, err = evm.initNewContract(contract, address)
|
ret, err = evm.initNewContract(contract, address)
|
||||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
// Revert to snapshot1 to undo both account creation and initcode changes.
|
||||||
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
contract.UseGas(GasCosts{RegularGas: contract.Gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
contract.UseGas(GasCosts{RegularGas: contract.Gas.RegularGas}, evm.Config.Tracer, tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
evm.StateDB.CloseSnapshot(snapshot)
|
|
||||||
if evm.chainRules.IsAmsterdam {
|
if evm.chainRules.IsAmsterdam {
|
||||||
// Charge initcode's state changes to the created contract's gas.
|
// Charge initcode's state changes to the created contract's gas.
|
||||||
bytesCharged := evm.StateDB.StateChangedBytes()
|
bytesCharged := evm.StateDB.StateChangedBytes(snapshot2, false)
|
||||||
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
stateGasCost := GasCosts{StateGas: bytesCharged * int64(evm.Context.CostPerStateByte)}
|
||||||
if !contract.Gas.CanAfford(stateGasCost) {
|
if !contract.Gas.CanAfford(stateGasCost) {
|
||||||
|
evm.StateDB.RevertToSnapshot(snapshot1)
|
||||||
contract.Gas.Exhaust()
|
contract.Gas.Exhaust()
|
||||||
return ret, address, contract.Gas, ErrOutOfGas
|
return ret, address, contract.Gas, ErrOutOfGas
|
||||||
}
|
}
|
||||||
contract.Gas.Charge(stateGasCost)
|
contract.Gas.Charge(stateGasCost)
|
||||||
}
|
}
|
||||||
|
evm.StateDB.CloseSnapshot(snapshot2)
|
||||||
|
if evm.chainRules.IsAmsterdam {
|
||||||
|
// Cache snapshot1's state bytes (exclude subcalls)
|
||||||
|
evm.StateDB.StateChangedBytes(snapshot1, true)
|
||||||
|
}
|
||||||
|
evm.StateDB.CloseSnapshot(snapshot1)
|
||||||
}
|
}
|
||||||
return ret, address, contract.Gas, err
|
return ret, address, contract.Gas, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ type StateDB interface {
|
||||||
Finalise(bool) *bal.StateAccessList
|
Finalise(bool) *bal.StateAccessList
|
||||||
|
|
||||||
// StateChangedBytes returns the number of state bytes created by the
|
// StateChangedBytes returns the number of state bytes created by the
|
||||||
// current call frame. Used by EIP-8037 for state gas metering.
|
// call frame identified by the given snapshot ID. When excludeSubcalls
|
||||||
StateChangedBytes() int64
|
// is true, cached subcall costs are not added to the total. Used by
|
||||||
|
// EIP-8037 for state gas metering.
|
||||||
|
StateChangedBytes(revid int, excludeSubcalls bool) int64
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue