mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-18 02:40:45 +00:00
core: use source-based refund mechanism
This commit is contained in:
parent
0b3dffcf00
commit
906488333c
5 changed files with 78 additions and 78 deletions
|
|
@ -674,11 +674,6 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
ret []byte
|
ret []byte
|
||||||
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
|
||||||
result vm.GasBudget
|
result vm.GasBudget
|
||||||
|
|
||||||
// Capture the forwarded regular-gas amount BEFORE ForwardAll consumes
|
|
||||||
// it, so Absorb can back out state-gas spillover from UsedRegularGas
|
|
||||||
// per EIP-8037.
|
|
||||||
forwarded = st.gasRemaining.RegularGas
|
|
||||||
)
|
)
|
||||||
if contractCreation {
|
if contractCreation {
|
||||||
// Check whether the init code size has been exceeded.
|
// Check whether the init code size has been exceeded.
|
||||||
|
|
@ -688,7 +683,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
// Execute the transaction's creation.
|
// Execute the transaction's creation.
|
||||||
var creation bool
|
var creation bool
|
||||||
ret, _, result, creation, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining.ForwardAll(), value)
|
ret, _, result, creation, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining.ForwardAll(), value)
|
||||||
st.gasRemaining.Absorb(result, forwarded)
|
st.gasRemaining.Absorb(result)
|
||||||
|
|
||||||
// If the contract creation failed, or the destination was pre-existing,
|
// If the contract creation failed, or the destination was pre-existing,
|
||||||
// refund the account-creation state gas pre-charged in IntrinsicGas.
|
// refund the account-creation state gas pre-charged in IntrinsicGas.
|
||||||
|
|
@ -712,7 +707,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
||||||
}
|
}
|
||||||
// Execute the transaction's call.
|
// Execute the transaction's call.
|
||||||
ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value)
|
ret, result, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining.ForwardAll(), value)
|
||||||
st.gasRemaining.Absorb(result, forwarded)
|
st.gasRemaining.Absorb(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Settle down the gas usage and refund the ETH back if any remaining
|
// Settle down the gas usage and refund the ETH back if any remaining
|
||||||
|
|
|
||||||
|
|
@ -163,9 +163,9 @@ func (c *Contract) refundState(s uint64, logger *tracing.Hooks, reason tracing.G
|
||||||
}
|
}
|
||||||
|
|
||||||
// refundGas absorbs a sub-call's leftover GasBudget into this contract's gas state.
|
// refundGas absorbs a sub-call's leftover GasBudget into this contract's gas state.
|
||||||
func (c *Contract) refundGas(child GasBudget, forwarded uint64, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
func (c *Contract) refundGas(child GasBudget, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||||
prior := c.Gas
|
prior := c.Gas
|
||||||
c.Gas.Absorb(child, forwarded)
|
c.Gas.Absorb(child)
|
||||||
if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
if logger.HasGasHook() && reason != tracing.GasChangeIgnored {
|
||||||
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
|
logger.EmitGasChange(prior.AsTracing(), c.Gas.AsTracing(), reason)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,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, reservoir := evm.StateDB.Snapshot(), gas.StateGas
|
snapshot := 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) {
|
||||||
|
|
@ -279,7 +279,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
|
wgas := evm.AccessEvents.CodeHashGas(addr, true, gas.RegularGas, false)
|
||||||
if _, ok := gas.ChargeRegular(wgas); !ok {
|
if _, ok := gas.ChargeRegular(wgas); !ok {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
return nil, gas.ExitHalt(reservoir), ErrOutOfGas
|
return nil, gas.ExitHalt(), ErrOutOfGas
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -314,7 +314,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the remaining gas at the end of frame
|
// Calculate the remaining gas at the end of frame
|
||||||
exitGas := gas.Exit(err, reservoir)
|
exitGas := gas.Exit(err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
|
@ -350,7 +350,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
if !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||||
return nil, gas, ErrInsufficientBalance
|
return nil, gas, ErrInsufficientBalance
|
||||||
}
|
}
|
||||||
snapshot, reservoir := evm.StateDB.Snapshot(), gas.StateGas
|
snapshot := evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
|
|
@ -365,7 +365,7 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the remaining gas at the end of frame
|
// Calculate the remaining gas at the end of frame
|
||||||
exitGas := gas.Exit(err, reservoir)
|
exitGas := gas.Exit(err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
|
@ -396,7 +396,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
if evm.depth > int(params.CallCreateDepth) {
|
if evm.depth > int(params.CallCreateDepth) {
|
||||||
return nil, gas, ErrDepth
|
return nil, gas, ErrDepth
|
||||||
}
|
}
|
||||||
snapshot, reservoir := evm.StateDB.Snapshot(), gas.StateGas
|
snapshot := evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// It is allowed to call precompiles, even via delegatecall
|
// It is allowed to call precompiles, even via delegatecall
|
||||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||||
|
|
@ -409,7 +409,7 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the remaining gas at the end of frame
|
// Calculate the remaining gas at the end of frame
|
||||||
exitGas := gas.Exit(err, reservoir)
|
exitGas := gas.Exit(err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
|
|
@ -443,7 +443,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
// after all empty accounts were deleted, so this is not required. However, if we omit this,
|
// after all empty accounts were deleted, so this is not required. However, if we omit this,
|
||||||
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
|
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
|
||||||
// We could change this, but for now it's left for legacy reasons
|
// We could change this, but for now it's left for legacy reasons
|
||||||
snapshot, reservoir := evm.StateDB.Snapshot(), gas.StateGas
|
snapshot := evm.StateDB.Snapshot()
|
||||||
|
|
||||||
// We do an AddBalance of zero here, just in order to trigger a touch.
|
// We do an AddBalance of zero here, just in order to trigger a touch.
|
||||||
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
|
||||||
|
|
@ -461,7 +461,7 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the remaining gas at the end of frame
|
// Calculate the remaining gas at the end of frame
|
||||||
exitGas := gas.Exit(err, reservoir)
|
exitGas := gas.Exit(err)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
|
|
@ -499,14 +499,13 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
}
|
}
|
||||||
// Increment the caller's nonce after passing all validations
|
// Increment the caller's nonce after passing all validations
|
||||||
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
evm.StateDB.SetNonce(caller, nonce+1, tracing.NonceChangeContractCreator)
|
||||||
reservoir := gas.StateGas
|
|
||||||
|
|
||||||
// Charge the contract creation init gas in verkle mode
|
// Charge the contract creation init gas in verkle mode
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
|
statelessGas := evm.AccessEvents.ContractCreatePreCheckGas(address, gas.RegularGas)
|
||||||
prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
|
prior, ok := gas.Charge(GasCosts{RegularGas: statelessGas})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, common.Address{}, gas.ExitHalt(reservoir), false, ErrOutOfGas
|
return nil, common.Address{}, gas.ExitHalt(), false, ErrOutOfGas
|
||||||
}
|
}
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractCollisionCheck)
|
evm.Config.Tracer.EmitGasChange(prior.AsTracing(), gas.AsTracing(), tracing.GasChangeWitnessContractCollisionCheck)
|
||||||
|
|
@ -527,7 +526,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
if evm.StateDB.GetNonce(address) != 0 ||
|
if evm.StateDB.GetNonce(address) != 0 ||
|
||||||
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
(contractHash != (common.Hash{}) && contractHash != types.EmptyCodeHash) || // non-empty code
|
||||||
isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) {
|
isEIP7610RejectedAccount(evm.ChainConfig().ChainID, address, evm.chainRules.IsEIP158) {
|
||||||
halt := gas.ExitHalt(reservoir)
|
halt := gas.ExitHalt()
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), halt.AsTracing(), tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(gas.AsTracing(), halt.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
}
|
}
|
||||||
|
|
@ -556,7 +555,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
if evm.chainRules.IsEIP4762 {
|
if evm.chainRules.IsEIP4762 {
|
||||||
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
|
consumed, wanted := evm.AccessEvents.ContractCreateInitGas(address, gas.RegularGas)
|
||||||
if consumed < wanted {
|
if consumed < wanted {
|
||||||
return nil, common.Address{}, gas.ExitHalt(reservoir), false, ErrOutOfGas
|
return nil, common.Address{}, gas.ExitHalt(), false, ErrOutOfGas
|
||||||
}
|
}
|
||||||
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
|
prior, _ := gas.Charge(GasCosts{RegularGas: consumed})
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
|
|
@ -581,7 +580,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
||||||
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
|
||||||
evm.StateDB.RevertToSnapshot(snapshot)
|
evm.StateDB.RevertToSnapshot(snapshot)
|
||||||
|
|
||||||
exit := contract.Gas.Exit(err, reservoir)
|
exit := contract.Gas.Exit(err)
|
||||||
if err != ErrExecutionReverted {
|
if err != ErrExecutionReverted {
|
||||||
if evm.Config.Tracer.HasGasHook() {
|
if evm.Config.Tracer.HasGasHook() {
|
||||||
evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)
|
evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,11 @@ type GasBudget struct {
|
||||||
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
|
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
|
||||||
UsedRegularGas uint64 // gross regular gas consumed in this frame
|
UsedRegularGas uint64 // gross regular gas consumed in this frame
|
||||||
UsedStateGas int64 // signed net state-gas consumed in this frame
|
UsedStateGas int64 // signed net state-gas consumed in this frame
|
||||||
|
|
||||||
|
// StateGasFromGasLeft tracks how much of this frame's regular gas (gas_left)
|
||||||
|
// has been borrowed to cover state-gas charges that exceeded the reservoir
|
||||||
|
// (the "spillover").
|
||||||
|
StateGasFromGasLeft uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewGasBudget initializes a fresh GasBudget for execution / forwarding,
|
// NewGasBudget initializes a fresh GasBudget for execution / forwarding,
|
||||||
|
|
@ -82,7 +87,7 @@ func (g GasBudget) Used(initial GasBudget) uint64 {
|
||||||
|
|
||||||
// String returns a visual representation of the budget.
|
// String returns a visual representation of the budget.
|
||||||
func (g GasBudget) String() string {
|
func (g GasBudget) String() string {
|
||||||
return fmt.Sprintf("<%v,%v,used=<%v,%v>>", g.RegularGas, g.StateGas, g.UsedRegularGas, g.UsedStateGas)
|
return fmt.Sprintf("<%v,%v,used=<%v,%v>,borrowed=%v>", g.RegularGas, g.StateGas, g.UsedRegularGas, g.UsedStateGas, g.StateGasFromGasLeft)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanAfford reports whether the running balance can cover the given cost.
|
// CanAfford reports whether the running balance can cover the given cost.
|
||||||
|
|
@ -117,6 +122,10 @@ func (g *GasBudget) Charge(cost GasCosts) (GasBudget, bool) {
|
||||||
spillover := cost.StateGas - g.StateGas
|
spillover := cost.StateGas - g.StateGas
|
||||||
g.StateGas = 0
|
g.StateGas = 0
|
||||||
g.RegularGas -= spillover
|
g.RegularGas -= spillover
|
||||||
|
|
||||||
|
// Record the regular gas borrowed to cover the overflowing state gas
|
||||||
|
// so a later inline refund can repay it before topping up the reservoir.
|
||||||
|
g.StateGasFromGasLeft += spillover
|
||||||
} else {
|
} else {
|
||||||
g.StateGas -= cost.StateGas
|
g.StateGas -= cost.StateGas
|
||||||
}
|
}
|
||||||
|
|
@ -146,14 +155,27 @@ func (g *GasBudget) IsZero() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefundState applies an inline state-gas refund (e.g., SSTORE 0->A->0).
|
// RefundState applies an inline state-gas refund (e.g., SSTORE 0->A->0).
|
||||||
// The reservoir is credited and the signed usage counter is decremented
|
|
||||||
// in lockstep, preserving the per-frame invariant:
|
|
||||||
//
|
//
|
||||||
// StateGas + UsedStateGas == initialStateGas + spillover_so_far
|
// Per EIP-8037, the refund repays the regular gas previously borrowed for
|
||||||
|
// state-gas spillover (tracked by StateGasFromGasLeft) before crediting the
|
||||||
|
// reservoir: it is returned to RegularGas up to the outstanding borrowed
|
||||||
|
// amount, and only the remainder tops up StateGas. This keeps the regular and
|
||||||
|
// state pools from drifting into one another.
|
||||||
//
|
//
|
||||||
// which the revert path relies on for the correct gross refund.
|
// The signed usage counter is decremented by the full refund regardless of the
|
||||||
|
// split, preserving the per-frame invariant:
|
||||||
|
//
|
||||||
|
// StateGas + UsedStateGas == initialStateGas + StateGasFromGasLeft
|
||||||
|
//
|
||||||
|
// which the revert and halt paths rely on for the correct gross refund.
|
||||||
func (g *GasBudget) RefundState(s uint64) {
|
func (g *GasBudget) RefundState(s uint64) {
|
||||||
g.StateGas += s
|
// Repay the borrowed regular gas first, capped at the outstanding amount.
|
||||||
|
repay := min(s, g.StateGasFromGasLeft)
|
||||||
|
g.RegularGas += repay
|
||||||
|
g.StateGasFromGasLeft -= repay
|
||||||
|
|
||||||
|
// Whatever is left tops up the reservoir.
|
||||||
|
g.StateGas += s - repay
|
||||||
g.UsedStateGas -= int64(s)
|
g.UsedStateGas -= int64(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -201,53 +223,46 @@ func (g GasBudget) ExitSuccess() GasBudget {
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExitRevert produces the leftover for a REVERT exit. Per EIP-8037, all state
|
// ExitRevert produces the leftover for a REVERT exit. The frame's state
|
||||||
// gas charged by the reverted frame is refunded to the caller's reservoir:
|
// changes are discarded, so all state gas it charged is refilled to its origin
|
||||||
//
|
// (EIP-8037): up to StateGasFromGasLeft is returned to RegularGas (the regular
|
||||||
// leftover.StateGas = StateGas + UsedStateGas
|
// gas it borrowed), and the remainder restores the reservoir. Because the
|
||||||
//
|
// borrowed regular gas is repaid first, the reservoir is made whole back to its
|
||||||
// UsedStateGas is reset since the frame's state changes are discarded.
|
// start-of-frame value:
|
||||||
func (g GasBudget) ExitRevert() GasBudget {
|
func (g GasBudget) ExitRevert() GasBudget {
|
||||||
reservoir := int64(g.StateGas) + g.UsedStateGas
|
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.StateGasFromGasLeft)
|
||||||
if reservoir < 0 {
|
if reservoir < 0 {
|
||||||
// Reservoir should never be negative. By construction it equals
|
// Reservoir should never be negative. By construction it equals
|
||||||
// the initial state-gas allocation plus any spillover to regular
|
// the initial state-gas allocation.
|
||||||
// gas.
|
|
||||||
reservoir = 0
|
reservoir = 0
|
||||||
log.Warn("Negative reservoir at revert", "remaining", g.StateGas, "used", g.UsedStateGas)
|
log.Warn("Negative reservoir at revert", "remaining", g.StateGas, "used", g.UsedStateGas, "borrowed", g.StateGasFromGasLeft)
|
||||||
}
|
}
|
||||||
return GasBudget{
|
return GasBudget{
|
||||||
RegularGas: g.RegularGas,
|
RegularGas: g.RegularGas + g.StateGasFromGasLeft,
|
||||||
StateGas: uint64(reservoir),
|
StateGas: uint64(reservoir),
|
||||||
UsedRegularGas: g.UsedRegularGas,
|
UsedRegularGas: g.UsedRegularGas,
|
||||||
UsedStateGas: 0,
|
UsedStateGas: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExitHalt produces the leftover for an exceptional halt.
|
// ExitHalt produces the leftover for an exceptional halt. As with a revert, the
|
||||||
//
|
// frame's state changes are rolled back and its state gas is refilled to origin
|
||||||
// - state_gas_reservoir is reset back to its value at the start of the child frame
|
// (EIP-8037); the difference is that the frame's gas_left is consumed rather
|
||||||
// - the gas_left initially given to the child is consumed (set to zero)
|
// than returned. The portion refilled to RegularGas is therefore burned along
|
||||||
func (g GasBudget) ExitHalt(initStateReservoir uint64) GasBudget {
|
// with the rest of gas_left, leaving only the reservoir portion to survive,
|
||||||
reservoir := int64(g.StateGas) + g.UsedStateGas
|
// which equals the reservoir's value at the start of the frame.
|
||||||
|
func (g GasBudget) ExitHalt() GasBudget {
|
||||||
|
reservoir := int64(g.StateGas) + g.UsedStateGas - int64(g.StateGasFromGasLeft)
|
||||||
if reservoir < 0 {
|
if reservoir < 0 {
|
||||||
// Reservoir should never be negative. By construction it equals
|
// Reservoir should never be negative. By construction it equals
|
||||||
// the initial state-gas allocation plus any spillover to regular
|
// the initial state-gas allocation.
|
||||||
// gas.
|
|
||||||
reservoir = 0
|
reservoir = 0
|
||||||
log.Warn("Negative reservoir at halt", "remaining", g.StateGas, "used", g.UsedStateGas)
|
log.Warn("Negative reservoir at halt", "remaining", g.StateGas, "used", g.UsedStateGas, "borrowed", g.StateGasFromGasLeft)
|
||||||
}
|
|
||||||
// The portion of state gas charged from regular gas is also burned
|
|
||||||
// together with the regular gas, rather than being returned to the
|
|
||||||
// parent's state-gas reservoir.
|
|
||||||
var spilled uint64
|
|
||||||
if uint64(reservoir) > initStateReservoir {
|
|
||||||
spilled = uint64(reservoir) - initStateReservoir
|
|
||||||
}
|
}
|
||||||
return GasBudget{
|
return GasBudget{
|
||||||
RegularGas: 0,
|
RegularGas: 0,
|
||||||
StateGas: initStateReservoir,
|
StateGas: uint64(reservoir),
|
||||||
UsedRegularGas: g.UsedRegularGas + g.RegularGas + spilled,
|
UsedRegularGas: g.UsedRegularGas + g.RegularGas + g.StateGasFromGasLeft,
|
||||||
UsedStateGas: 0,
|
UsedStateGas: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -258,17 +273,14 @@ func (g GasBudget) ExitHalt(initStateReservoir uint64) GasBudget {
|
||||||
// - err == nil → ExitSuccess
|
// - err == nil → ExitSuccess
|
||||||
// - err == ErrExecutionReverted → ExitRevert
|
// - err == ErrExecutionReverted → ExitRevert
|
||||||
// - any other err → ExitHalt
|
// - any other err → ExitHalt
|
||||||
//
|
func (g GasBudget) Exit(err error) GasBudget {
|
||||||
// Soft validation failures (occurring BEFORE evm.Run) should call Preserved
|
|
||||||
// directly instead of going through this dispatcher.
|
|
||||||
func (g GasBudget) Exit(err error, initStateReservoir uint64) GasBudget {
|
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
return g.ExitSuccess()
|
return g.ExitSuccess()
|
||||||
case err == ErrExecutionReverted:
|
case err == ErrExecutionReverted:
|
||||||
return g.ExitRevert()
|
return g.ExitRevert()
|
||||||
default:
|
default:
|
||||||
return g.ExitHalt(initStateReservoir)
|
return g.ExitHalt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -276,18 +288,12 @@ func (g GasBudget) Exit(err error, initStateReservoir uint64) GasBudget {
|
||||||
// budget. Additionally, it does an EIP-8037 spillover correction:
|
// budget. Additionally, it does an EIP-8037 spillover correction:
|
||||||
// state-gas that spilled into the regular pool inside the child frame is
|
// state-gas that spilled into the regular pool inside the child frame is
|
||||||
// excluded from the UsedRegularGas.
|
// excluded from the UsedRegularGas.
|
||||||
//
|
func (g *GasBudget) Absorb(child GasBudget) {
|
||||||
// spillover = forwarded - child.RegularGas - child.UsedRegularGas
|
|
||||||
//
|
|
||||||
// forwarded is the regular-gas amount that was passed to the child at call
|
|
||||||
// entry (i.e., the regular initial of the child's GasBudget).
|
|
||||||
func (g *GasBudget) Absorb(child GasBudget, forwarded uint64) {
|
|
||||||
spillover := forwarded - child.RegularGas - child.UsedRegularGas
|
|
||||||
|
|
||||||
g.UsedRegularGas -= child.RegularGas
|
g.UsedRegularGas -= child.RegularGas
|
||||||
g.RegularGas += child.RegularGas
|
g.RegularGas += child.RegularGas
|
||||||
g.StateGas = child.StateGas
|
g.StateGas = child.StateGas
|
||||||
g.UsedStateGas += child.UsedStateGas
|
g.UsedStateGas += child.UsedStateGas
|
||||||
|
|
||||||
g.UsedRegularGas -= spillover
|
g.UsedRegularGas -= child.StateGasFromGasLeft
|
||||||
|
g.StateGasFromGasLeft += child.StateGasFromGasLeft
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -677,7 +677,7 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
|
|
||||||
// Refund the leftover gas back to current frame
|
// Refund the leftover gas back to current frame
|
||||||
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
// Refund the state gas of account-creation if creation doesn't happen
|
// Refund the state gas of account-creation if creation doesn't happen
|
||||||
if evm.GetRules().IsAmsterdam && !creation {
|
if evm.GetRules().IsAmsterdam && !creation {
|
||||||
|
|
@ -715,7 +715,7 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Stack.push(&stackvalue)
|
scope.Stack.push(&stackvalue)
|
||||||
|
|
||||||
// Refund the leftover gas back to current frame
|
// Refund the leftover gas back to current frame
|
||||||
scope.Contract.refundGas(result, forward, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
// Refund the state gas of account-creation if creation doesn't happen
|
// Refund the state gas of account-creation if creation doesn't happen
|
||||||
if evm.GetRules().IsAmsterdam && !creation {
|
if evm.GetRules().IsAmsterdam && !creation {
|
||||||
|
|
@ -764,7 +764,7 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
// If the call frame reverts or halts exceptionally, the charged state-gas
|
// If the call frame reverts or halts exceptionally, the charged state-gas
|
||||||
// is refilled back to the state reservoir in Amsterdam.
|
// is refilled back to the state reservoir in Amsterdam.
|
||||||
|
|
@ -805,7 +805,7 @@ func opCallCode(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -837,7 +837,7 @@ func opDelegateCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
if err == nil || err == ErrExecutionReverted {
|
if err == nil || err == ErrExecutionReverted {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
@ -870,7 +870,7 @@ func opStaticCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
||||||
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.Contract.refundGas(result, gas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||||
|
|
||||||
evm.returnData = ret
|
evm.returnData = ret
|
||||||
return ret, nil
|
return ret, nil
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue