Skip to content
2 changes: 1 addition & 1 deletion cmd/evm/internal/t8ntool/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func Transaction(ctx *cli.Context) error {
}
// Check intrinsic gas
rules := chainConfig.Rules(common.Big0, true, 0)
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai, rules.IsAmsterdam)
cost, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules, params.CostPerStateByte)
if err != nil {
r.Error = err
results = append(results, r)
Expand Down
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
data := make([]byte, nbytes)
return func(i int, gen *BlockGen) {
toaddr := common.Address{}
cost, _ := IntrinsicGas(data, nil, nil, false, false, false, false, false)
cost, _ := IntrinsicGas(data, nil, nil, false, params.Rules{}, params.CostPerStateByte)
signer := gen.Signer()
gasPrice := big.NewInt(0)
if gen.header.BaseFee != nil {
Expand Down
6 changes: 3 additions & 3 deletions core/bintrie_witness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ var (
func TestProcessUBT(t *testing.T) {
var (
code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`)
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true, false)
intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, params.Rules{IsHomestead: true, IsIstanbul: true, IsShanghai: true}, 0)
// A contract creation that calls EXTCODECOPY in the constructor. Used to ensure that the witness
// will not contain that copied data.
// Source: https://gist.github.com/gballet/a23db1e1cb4ed105616b5920feb75985
codeWithExtCodeCopy = common.FromHex(`0x60806040526040516100109061017b565b604051809103906000f08015801561002c573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555034801561007857600080fd5b5060008067ffffffffffffffff8111156100955761009461024a565b5b6040519080825280601f01601f1916602001820160405280156100c75781602001600182028036833780820191505090505b50905060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690506020600083833c81610101906101e3565b60405161010d90610187565b61011791906101a3565b604051809103906000f080158015610133573d6000803e3d6000fd5b50600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505061029b565b60d58061046783390190565b6102068061053c83390190565b61019d816101d9565b82525050565b60006020820190506101b86000830184610194565b92915050565b6000819050602082019050919050565b600081519050919050565b6000819050919050565b60006101ee826101ce565b826101f8846101be565b905061020381610279565b925060208210156102435761023e7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8360200360080261028e565b831692505b5050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600061028582516101d9565b80915050919050565b600082821b905092915050565b6101bd806102aa6000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f566852414610030575b600080fd5b61003861004e565b6040516100459190610146565b60405180910390f35b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166381ca91d36040518163ffffffff1660e01b815260040160206040518083038186803b1580156100b857600080fd5b505afa1580156100cc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100f0919061010a565b905090565b60008151905061010481610170565b92915050565b6000602082840312156101205761011f61016b565b5b600061012e848285016100f5565b91505092915050565b61014081610161565b82525050565b600060208201905061015b6000830184610137565b92915050565b6000819050919050565b600080fd5b61017981610161565b811461018457600080fd5b5056fea2646970667358221220a6a0e11af79f176f9c421b7b12f441356b25f6489b83d38cc828a701720b41f164736f6c63430008070033608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063ab5ed15014602d575b600080fd5b60336047565b604051603e9190605d565b60405180910390f35b60006001905090565b6057816076565b82525050565b6000602082019050607060008301846050565b92915050565b600081905091905056fea26469706673582212203a14eb0d5cd07c277d3e24912f110ddda3e553245a99afc4eeefb2fbae5327aa64736f6c63430008070033608060405234801561001057600080fd5b5060405161020638038061020683398181016040528101906100329190610063565b60018160001c6100429190610090565b60008190555050610145565b60008151905061005d8161012e565b92915050565b60006020828403121561007957610078610129565b5b60006100878482850161004e565b91505092915050565b600061009b826100f0565b91506100a6836100f0565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156100db576100da6100fa565b5b828201905092915050565b6000819050919050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610137816100e6565b811461014257600080fd5b50565b60b3806101536000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806381ca91d314602d575b600080fd5b60336047565b604051603e9190605a565b60405180910390f35b60005481565b6054816073565b82525050565b6000602082019050606d6000830184604d565b92915050565b600081905091905056fea26469706673582212209bff7098a2f526de1ad499866f27d6d0d6f17b74a413036d6063ca6a0998ca4264736f6c63430008070033`)
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, true, true, true, false)
intrinsicCodeWithExtCodeCopyGas, _ = IntrinsicGas(codeWithExtCodeCopy, nil, nil, true, params.Rules{IsHomestead: true, IsIstanbul: true, IsShanghai: true}, 0)
signer = types.LatestSigner(testUBTChainConfig)
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain
Expand Down Expand Up @@ -201,7 +201,7 @@ func TestProcessParentBlockHash(t *testing.T) {
if isUBT {
chainConfig = testUBTChainConfig
}
vmContext := NewEVMBlockContext(header, nil, new(common.Address))
vmContext := NewEVMBlockContext(header, &BlockChain{chainConfig: chainConfig}, new(common.Address))
evm := vm.NewEVM(vmContext, statedb, chainConfig, vm.Config{})
ProcessParentBlockHash(header.ParentHash, evm, bal.NewConstructionBlockAccessList())
}
Expand Down
6 changes: 6 additions & 0 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -2301,6 +2301,12 @@ func (bc *BlockChain) ProcessBlock(ctx context.Context, parentRoot common.Hash,
stats.Validation = vtime - (statedb.AccountHashes + statedb.AccountUpdates + statedb.StorageUpdates) // The time spent on block validation
stats.CrossValidation = xvtime // The time spent on stateless cross validation

// Attach the computed block access list so it gets persisted alongside the
// block. The validator has already verified the hash matches the header.
if res.Bal != nil && block.AccessList() == nil {
block = block.WithAccessListUnsafe(res.Bal.ToEncodingObj())
}

// Write the block to the chain and get the status.
var status WriteStatus
if config.WriteState {
Expand Down
25 changes: 13 additions & 12 deletions core/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,19 @@ func NewEVMBlockContext(header *types.Header, chain ChainContext, author *common
}

return vm.BlockContext{
CanTransfer: CanTransfer,
Transfer: Transfer,
GetHash: GetHashFn(header, chain),
Coinbase: beneficiary,
BlockNumber: new(big.Int).Set(header.Number),
Time: header.Time,
Difficulty: new(big.Int).Set(header.Difficulty),
BaseFee: baseFee,
BlobBaseFee: blobBaseFee,
GasLimit: header.GasLimit,
Random: random,
SlotNum: slotNum,
CanTransfer: CanTransfer,
Transfer: Transfer,
GetHash: GetHashFn(header, chain),
Coinbase: beneficiary,
BlockNumber: new(big.Int).Set(header.Number),
Time: header.Time,
Difficulty: new(big.Int).Set(header.Difficulty),
BaseFee: baseFee,
BlobBaseFee: blobBaseFee,
GasLimit: header.GasLimit,
Random: random,
SlotNum: slotNum,
CostPerStateByte: params.CostPerStateByte,
}
}

Expand Down
72 changes: 55 additions & 17 deletions core/gaspool.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ type GasPool struct {
remaining uint64
initial uint64
cumulativeUsed uint64

// After 8037 Block gas used is
// max(cumulativeRegular, cumulativeState).
cumulativeRegular uint64
cumulativeState uint64
}

// NewGasPool initializes the gasPool with the given amount.
Expand All @@ -37,61 +42,92 @@ func NewGasPool(amount uint64) *GasPool {
}
}

// SubGas deducts the given amount from the pool if enough gas is
// CheckGasLegacy deducts the given amount from the pool if enough gas is
// available and returns an error otherwise.
func (gp *GasPool) SubGas(amount uint64) error {
func (gp *GasPool) CheckGasLegacy(amount uint64) error {
if gp.remaining < amount {
return ErrGasLimitReached
}
gp.remaining -= amount
return nil
}

// ReturnGas adds the refunded gas back to the pool and updates
// CheckGasAmsterdam performs the EIP-8037 per-tx 2D block-inclusion check:
// the worst-case regular contribution must fit in the regular dimension and
// the worst-case state contribution must fit in the state dimension
func (gp *GasPool) CheckGasAmsterdam(regularReservation, stateReservation uint64) error {
if gp.initial-gp.cumulativeRegular < regularReservation {
return ErrGasLimitReached
}
if gp.initial-gp.cumulativeState < stateReservation {
return ErrGasLimitReached
}
return nil
}

// ChargeGasLegacy adds the refunded gas back to the pool and updates
// the cumulative gas usage accordingly.
func (gp *GasPool) ReturnGas(returned uint64, gasUsed uint64) error {
func (gp *GasPool) ChargeGasLegacy(returned uint64, gasUsed uint64) error {
if gp.remaining > math.MaxUint64-returned {
return fmt.Errorf("%w: remaining: %d, returned: %d", ErrGasLimitOverflow, gp.remaining, returned)
}
// The returned gas calculation differs across forks.
//
// - Pre-Amsterdam:
// returned = purchased - remaining (refund included)
//
// - Post-Amsterdam:
// returned = purchased - gasUsed (refund excluded)
// returned = purchased - remaining (refund included)
gp.remaining += returned

// gasUsed = max(txGasUsed - gasRefund, calldataFloorGasCost)
// regardless of Amsterdam is activated or not.
gp.cumulativeUsed += gasUsed
return nil
}

// ChargeGasAmsterdam calculates the new remaining gas in the pool after the
// execution of a message. Previously we subtracted and re-added gas to the
// gaspool. After Amsterdam we only check if we can include the transaction
// and charge the gaspool at the end.
func (gp *GasPool) ChargeGasAmsterdam(txRegular, txState, receiptGasUsed uint64) error {
gp.cumulativeRegular += txRegular
gp.cumulativeState += txState
gp.cumulativeUsed += receiptGasUsed

blockUsed := max(gp.cumulativeRegular, gp.cumulativeState)
if gp.initial < blockUsed {
return fmt.Errorf("%w: block gas overflow: initial %d, used %d (regular: %d, state: %d)",
ErrGasLimitReached, gp.initial, blockUsed, gp.cumulativeRegular, gp.cumulativeState)
}
// For tx inclusion, we only check if the regular dimension fits.
gp.remaining = gp.initial - gp.cumulativeRegular
return nil
}

// Gas returns the amount of gas remaining in the pool.
func (gp *GasPool) Gas() uint64 {
return gp.remaining
}

// CumulativeUsed returns the amount of cumulative consumed gas (refunded included).
// CumulativeUsed returns the cumulative gas consumed for receipt tracking.
func (gp *GasPool) CumulativeUsed() uint64 {
return gp.cumulativeUsed
}

// Used returns the amount of consumed gas.
func (gp *GasPool) Used() uint64 {
if gp.cumulativeRegular > 0 || gp.cumulativeState > 0 {
// After 8037 we return max(sum_regular, sum_state)
return max(gp.cumulativeRegular, gp.cumulativeState)
}
if gp.initial < gp.remaining {
panic("gas used underflow")
panic(fmt.Sprintf("gas used underflow: %v %v", gp.initial, gp.remaining))
}
return gp.initial - gp.remaining
}

// Snapshot returns the deep-copied object as the snapshot.
func (gp *GasPool) Snapshot() *GasPool {
return &GasPool{
initial: gp.initial,
remaining: gp.remaining,
cumulativeUsed: gp.cumulativeUsed,
initial: gp.initial,
remaining: gp.remaining,
cumulativeUsed: gp.cumulativeUsed,
cumulativeRegular: gp.cumulativeRegular,
cumulativeState: gp.cumulativeState,
}
}

Expand All @@ -100,6 +136,8 @@ func (gp *GasPool) Set(other *GasPool) {
gp.initial = other.initial
gp.remaining = other.remaining
gp.cumulativeUsed = other.cumulativeUsed
gp.cumulativeRegular = other.cumulativeRegular
gp.cumulativeState = other.cumulativeState
}

func (gp *GasPool) String() string {
Expand Down
24 changes: 24 additions & 0 deletions core/state/state_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,30 @@ func (s *stateObject) Code() []byte {
return code
}

// GetCommittedCode returns the contract code committed at the start of the
// current execution, ignoring any intra-execution SetCode modifications.
// Used by EIP-7702 authorization application to make refund decisions
// relative to the originally-committed delegation, matching the spirit of
// GetCommittedState for storage slots.
func (s *stateObject) GetCommittedCode() []byte {
// The account did not exist at the start of the current execution.
if s.origin == nil {
return nil
}
hash := common.BytesToHash(s.origin.CodeHash)
if hash == types.EmptyCodeHash {
return nil
}
// If the code has not been touched in this execution, the live cache
// already holds the committed code.
if !s.dirtyCode {
return s.Code()
}
// Code was modified within the current execution. Reach for the on-disk
// blob keyed by the origin hash.
return s.db.reader.Code(s.address, hash)
}

// CodeSize returns the size of the contract code associated with this object,
// or zero if none. This method is an almost mirror of Code, but uses a cache
// inside the database to avoid loading codes seen recently.
Expand Down
30 changes: 24 additions & 6 deletions core/state_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,15 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM, blockAccessList
defer tracer.OnSystemCallEnd()
}
}
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
// new storage slots a single system call is expected to write.
//
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
// largest per-block bound across the existing system contracts.
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
msg := &Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasLimit: 30_000_000 + stateBudget,
GasPrice: uint256.NewInt(0),
GasFeeCap: uint256.NewInt(0),
GasTipCap: uint256.NewInt(0),
Expand All @@ -290,7 +296,7 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM, blockAccessList
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
evm.StateDB.AddAddressToAccessList(params.BeaconRootsAddress)
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
_, _, _ = evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
}
Expand All @@ -306,9 +312,15 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, blockAccessList *
defer tracer.OnSystemCallEnd()
}
}
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
// new storage slots a single system call is expected to write.
//
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
// largest per-block bound across the existing system contracts.
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
msg := &Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasLimit: 30_000_000 + stateBudget,
GasPrice: uint256.NewInt(0),
GasFeeCap: uint256.NewInt(0),
GasTipCap: uint256.NewInt(0),
Expand All @@ -319,7 +331,7 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM, blockAccessList *
evm.StateDB.Prepare(evm.GetRules(), common.Address{}, common.Address{}, nil, nil, nil)
evm.StateDB.SetTxContext(common.Hash{}, 0, 0)
evm.StateDB.AddAddressToAccessList(params.HistoryStorageAddress)
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
_, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -348,9 +360,15 @@ func processRequestsSystemCall(requests *[][]byte, rules params.Rules, evm *vm.E
defer tracer.OnSystemCallEnd()
}
}
// SYSTEM_MAX_SSTORES_PER_CALL = 16 is the upper bound on the number of
// new storage slots a single system call is expected to write.
//
// This value matches MAX_WITHDRAWAL_REQUESTS_PER_BLOCK (EIP-7002), the
// largest per-block bound across the existing system contracts.
stateBudget := params.SystemMaxSStoresPerCall * evm.Context.CostPerStateByte * params.StorageCreationSize
msg := &Message{
From: params.SystemAddress,
GasLimit: 30_000_000,
GasLimit: 30_000_000 + stateBudget,
GasPrice: uint256.NewInt(0),
GasFeeCap: uint256.NewInt(0),
GasTipCap: uint256.NewInt(0),
Expand All @@ -360,7 +378,7 @@ func processRequestsSystemCall(requests *[][]byte, rules params.Rules, evm *vm.E
evm.StateDB.Prepare(rules, common.Address{}, common.Address{}, nil, nil, nil)
evm.StateDB.SetTxContext(common.Hash{}, 0, blockAccessIndex)
evm.StateDB.AddAddressToAccessList(addr)
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000), common.U2560)
ret, _, err := evm.Call(msg.From, *msg.To, msg.Data, vm.NewGasBudget(30_000_000, stateBudget), common.U2560)
if evm.StateDB.AccessEvents() != nil {
evm.StateDB.AccessEvents().Merge(evm.AccessEvents)
}
Expand Down
Loading
Loading