core: arena allocator improvements and GC safety fixes

Multi-slab BumpAllocator (8 MiB per slab, 1 GiB cap) with per-block
usage logging (used, peak, slab count, total capacity).

Fix GC corruption: only flat types (uint256.Int) are safe in the
[]byte arena slab. Pointer-containing types (Contract, Memory,
stateTransition, ExecutionResult) use standard heap allocation.

Convert Message fields from *big.Int to uint256.Int value types,
allowing Message and Stack to remain arena-allocated without GC
issues. The buyGas() function still uses big.Int internally for
balance overflow detection.

Arena hardening: clear() for optimized zeroing, Stack pre-alloc
1024 capacity, prefetcher isolation (Allocator=nil).
This commit is contained in:
tellabg 2026-02-19 00:33:02 +01:00 committed by Guillaume Ballet
parent 3f5b5dc3a3
commit 0eb659b003
No known key found for this signature in database
11 changed files with 127 additions and 89 deletions

View file

@ -101,7 +101,7 @@ func NewEVMTxContext(msg *Message) vm.TxContext {
func NewEVMTxContextWithAlloc(msg *Message, alloc arena.Allocator) vm.TxContext { func NewEVMTxContextWithAlloc(msg *Message, alloc arena.Allocator) vm.TxContext {
ctx := vm.TxContext{ ctx := vm.TxContext{
Origin: msg.From, Origin: msg.From,
GasPrice: uint256.MustFromBig(msg.GasPrice), GasPrice: new(uint256.Int).Set(&msg.GasPrice),
BlobHashes: msg.BlobHashes, BlobHashes: msg.BlobHashes,
} }
return ctx return ctx

View file

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/internal/telemetry" "github.com/ethereum/go-ethereum/internal/telemetry"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
) )
// StateProcessor is a basic Processor, which takes care of transitioning // StateProcessor is a basic Processor, which takes care of transitioning
@ -256,9 +257,9 @@ func ProcessBeaconBlockRoot(beaconRoot common.Hash, evm *vm.EVM) {
msg := &Message{ msg := &Message{
From: params.SystemAddress, From: params.SystemAddress,
GasLimit: 30_000_000, GasLimit: 30_000_000,
GasPrice: common.Big0, GasPrice: uint256.Int{},
GasFeeCap: common.Big0, GasFeeCap: uint256.Int{},
GasTipCap: common.Big0, GasTipCap: uint256.Int{},
To: &params.BeaconRootsAddress, To: &params.BeaconRootsAddress,
Data: beaconRoot[:], Data: beaconRoot[:],
} }
@ -280,9 +281,9 @@ func ProcessParentBlockHash(prevHash common.Hash, evm *vm.EVM) {
msg := &Message{ msg := &Message{
From: params.SystemAddress, From: params.SystemAddress,
GasLimit: 30_000_000, GasLimit: 30_000_000,
GasPrice: common.Big0, GasPrice: uint256.Int{},
GasFeeCap: common.Big0, GasFeeCap: uint256.Int{},
GasTipCap: common.Big0, GasTipCap: uint256.Int{},
To: &params.HistoryStorageAddress, To: &params.HistoryStorageAddress,
Data: prevHash.Bytes(), Data: prevHash.Bytes(),
} }
@ -320,9 +321,9 @@ func processRequestsSystemCall(requests *[][]byte, evm *vm.EVM, requestType byte
msg := &Message{ msg := &Message{
From: params.SystemAddress, From: params.SystemAddress,
GasLimit: 30_000_000, GasLimit: 30_000_000,
GasPrice: common.Big0, GasPrice: uint256.Int{},
GasFeeCap: common.Big0, GasFeeCap: uint256.Int{},
GasTipCap: common.Big0, GasTipCap: uint256.Int{},
To: &addr, To: &addr,
} }
evm.SetTxContext(NewEVMTxContext(msg)) evm.SetTxContext(NewEVMTxContext(msg))

View file

@ -147,14 +147,14 @@ type Message struct {
To *common.Address To *common.Address
From common.Address From common.Address
Nonce uint64 Nonce uint64
Value *big.Int Value uint256.Int
GasLimit uint64 GasLimit uint64
GasPrice *big.Int GasPrice uint256.Int
GasFeeCap *big.Int GasFeeCap uint256.Int
GasTipCap *big.Int GasTipCap uint256.Int
Data []byte Data []byte
AccessList types.AccessList AccessList types.AccessList
BlobGasFeeCap *big.Int BlobGasFeeCap uint256.Int
BlobHashes []common.Hash BlobHashes []common.Hash
SetCodeAuthorizations []types.SetCodeAuthorization SetCodeAuthorizations []types.SetCodeAuthorization
@ -181,31 +181,62 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In
// TransactionToMessageWithAlloc converts a transaction into a Message, using // TransactionToMessageWithAlloc converts a transaction into a Message, using
// the provided allocator for the Message struct and its transient big.Int fields. // the provided allocator for the Message struct and its transient big.Int fields.
func TransactionToMessageWithAlloc(tx *types.Transaction, s types.Signer, baseFee *big.Int, alloc arena.Allocator) (*Message, error) { func TransactionToMessageWithAlloc(tx *types.Transaction, s types.Signer, baseFee *big.Int, alloc arena.Allocator) (*Message, error) {
msg := new(Message) msg := arena.New[Message](alloc)
var err error
msg.From, err = types.Sender(s, tx)
if err != nil {
return nil, err
}
msg.Nonce = tx.Nonce() msg.Nonce = tx.Nonce()
msg.GasLimit = tx.Gas() msg.GasLimit = tx.Gas()
msg.GasPrice = tx.GasPrice()
msg.GasFeeCap = tx.GasFeeCap() var (
msg.GasTipCap = tx.GasTipCap() v *uint256.Int
overflow bool
)
if v, overflow = uint256.FromBig(tx.GasPrice()); overflow {
return nil, fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From.Hex(), tx.GasPrice().BitLen())
}
msg.GasPrice = *v
if v, overflow = uint256.FromBig(tx.GasFeeCap()); overflow {
return nil, fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From.Hex(), tx.GasFeeCap().BitLen())
}
msg.GasFeeCap = *v
if v, overflow = uint256.FromBig(tx.GasTipCap()); overflow {
return nil, fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
msg.From.Hex(), tx.GasTipCap().BitLen())
}
msg.GasTipCap = *v
msg.To = tx.To() msg.To = tx.To()
msg.Value = tx.Value() if v, overflow = uint256.FromBig(tx.Value()); overflow {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
msg.Value = *v
msg.Data = tx.Data() msg.Data = tx.Data()
msg.AccessList = tx.AccessList() msg.AccessList = tx.AccessList()
msg.SetCodeAuthorizations = tx.SetCodeAuthorizations() msg.SetCodeAuthorizations = tx.SetCodeAuthorizations()
msg.BlobHashes = tx.BlobHashes() msg.BlobHashes = tx.BlobHashes()
msg.BlobGasFeeCap = tx.BlobGasFeeCap() if tx.BlobGasFeeCap() != nil {
if v, overflow = uint256.FromBig(tx.BlobGasFeeCap()); overflow {
return nil, fmt.Errorf("%w: blobGasFeeCap exceeds 256 bits", ErrBlobFeeCapTooLow)
}
msg.BlobGasFeeCap = *v
}
// If baseFee provided, set gasPrice to effectiveGasPrice. // If baseFee provided, set gasPrice to effectiveGasPrice.
if baseFee != nil { if baseFee != nil {
msg.GasPrice = msg.GasPrice.Add(msg.GasTipCap, baseFee) baseFeeU256 := uint256.MustFromBig(baseFee)
if msg.GasPrice.Cmp(msg.GasFeeCap) > 0 { msg.GasPrice.Add(&msg.GasTipCap, baseFeeU256)
msg.GasPrice = msg.GasFeeCap if msg.GasPrice.Cmp(&msg.GasFeeCap) > 0 {
msg.GasPrice.Set(&msg.GasFeeCap)
} }
} }
var err error return msg, nil
msg.From, err = types.Sender(s, tx)
return msg, err
} }
// ApplyMessage computes the new state by applying the given message // ApplyMessage computes the new state by applying the given message
@ -274,22 +305,21 @@ func (st *stateTransition) to() common.Address {
} }
func (st *stateTransition) buyGas() error { func (st *stateTransition) buyGas() error {
// Compute mgval = gasLimit * gasPrice (the gas cost to deduct from sender). // Use big.Int for balance check to detect >256-bit overflow correctly.
// Use big.Int for balanceCheck to detect >256-bit overflow correctly.
mgval := new(big.Int).SetUint64(st.msg.GasLimit) mgval := new(big.Int).SetUint64(st.msg.GasLimit)
mgval.Mul(mgval, st.msg.GasPrice) mgval.Mul(mgval, st.msg.GasPrice.ToBig())
balanceCheck := new(big.Int).Set(mgval) balanceCheck := new(big.Int).Set(mgval)
if st.msg.GasFeeCap != nil { if !st.msg.GasFeeCap.IsZero() {
balanceCheck.SetUint64(st.msg.GasLimit) balanceCheck.SetUint64(st.msg.GasLimit)
balanceCheck = balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap) balanceCheck.Mul(balanceCheck, st.msg.GasFeeCap.ToBig())
} }
balanceCheck.Add(balanceCheck, st.msg.Value) balanceCheck.Add(balanceCheck, st.msg.Value.ToBig())
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) { if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
if blobGas := st.blobGasUsed(); blobGas > 0 { if blobGas := st.blobGasUsed(); blobGas > 0 {
// Check that the user has enough funds to cover blobGasUsed * tx.BlobGasFeeCap // Check that the user has enough funds to cover blobGasUsed * tx.BlobGasFeeCap
blobBalanceCheck := new(big.Int).SetUint64(blobGas) blobBalanceCheck := new(big.Int).SetUint64(blobGas)
blobBalanceCheck.Mul(blobBalanceCheck, st.msg.BlobGasFeeCap) blobBalanceCheck.Mul(blobBalanceCheck, st.msg.BlobGasFeeCap.ToBig())
balanceCheck.Add(balanceCheck, blobBalanceCheck) balanceCheck.Add(balanceCheck, blobBalanceCheck)
// Pay for blobGasUsed * actual blob fee // Pay for blobGasUsed * actual blob fee
blobFee := new(big.Int).SetUint64(blobGas) blobFee := new(big.Int).SetUint64(blobGas)
@ -352,25 +382,18 @@ func (st *stateTransition) preCheck() error {
// Make sure that transaction gasFeeCap is greater than the baseFee (post london) // Make sure that transaction gasFeeCap is greater than the baseFee (post london)
if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) { if st.evm.ChainConfig().IsLondon(st.evm.Context.BlockNumber) {
// Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call) // Skip the checks if gas fields are zero and baseFee was explicitly disabled (eth_call)
skipCheck := st.evm.Config.NoBaseFee && msg.GasFeeCap.BitLen() == 0 && msg.GasTipCap.BitLen() == 0 skipCheck := st.evm.Config.NoBaseFee && msg.GasFeeCap.IsZero() && msg.GasTipCap.IsZero()
if !skipCheck { if !skipCheck {
if l := msg.GasFeeCap.BitLen(); l > 256 { if msg.GasFeeCap.Cmp(&msg.GasTipCap) < 0 {
return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
msg.From.Hex(), l)
}
if l := msg.GasTipCap.BitLen(); l > 256 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
msg.From.Hex(), l)
}
if msg.GasFeeCap.Cmp(msg.GasTipCap) < 0 {
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap, return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
msg.From.Hex(), msg.GasTipCap, msg.GasFeeCap) msg.From.Hex(), &msg.GasTipCap, &msg.GasFeeCap)
} }
// This will panic if baseFee is nil, but basefee presence is verified // This will panic if baseFee is nil, but basefee presence is verified
// as part of header validation. // as part of header validation.
if msg.GasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 { baseFeeU256 := uint256.MustFromBig(st.evm.Context.BaseFee)
if msg.GasFeeCap.Cmp(baseFeeU256) < 0 {
return fmt.Errorf("%w: address %v, maxFeePerGas: %s, baseFee: %s", ErrFeeCapTooLow, return fmt.Errorf("%w: address %v, maxFeePerGas: %s, baseFee: %s", ErrFeeCapTooLow,
msg.From.Hex(), msg.GasFeeCap, st.evm.Context.BaseFee) msg.From.Hex(), &msg.GasFeeCap, st.evm.Context.BaseFee)
} }
} }
} }
@ -398,11 +421,12 @@ func (st *stateTransition) preCheck() error {
if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) { if st.evm.ChainConfig().IsCancun(st.evm.Context.BlockNumber, st.evm.Context.Time) {
if st.blobGasUsed() > 0 { if st.blobGasUsed() > 0 {
// Skip the checks if gas fields are zero and blobBaseFee was explicitly disabled (eth_call) // Skip the checks if gas fields are zero and blobBaseFee was explicitly disabled (eth_call)
skipCheck := st.evm.Config.NoBaseFee && msg.BlobGasFeeCap.BitLen() == 0 skipCheck := st.evm.Config.NoBaseFee && msg.BlobGasFeeCap.IsZero()
if !skipCheck { if !skipCheck {
// This will panic if blobBaseFee is nil, but blobBaseFee presence // This will panic if blobBaseFee is nil, but blobBaseFee presence
// is verified as part of header validation. // is verified as part of header validation.
if msg.BlobGasFeeCap.Cmp(st.evm.Context.BlobBaseFee) < 0 { blobBaseFeeU256 := uint256.MustFromBig(st.evm.Context.BlobBaseFee)
if msg.BlobGasFeeCap.Cmp(blobBaseFeeU256) < 0 {
return fmt.Errorf("%w: address %v blobGasFeeCap: %v, blobBaseFee: %v", ErrBlobFeeCapTooLow, return fmt.Errorf("%w: address %v blobGasFeeCap: %v, blobBaseFee: %v", ErrBlobFeeCapTooLow,
msg.From.Hex(), msg.BlobGasFeeCap, st.evm.Context.BlobBaseFee) msg.From.Hex(), msg.BlobGasFeeCap, st.evm.Context.BlobBaseFee)
} }
@ -486,11 +510,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
// Check clause 6 // Check clause 6
value, overflow := uint256.FromBig(msg.Value) if !msg.Value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, &msg.Value) {
if overflow {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
}
if !value.IsZero() && !st.evm.Context.CanTransfer(st.state, msg.From, value) {
return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex()) return nil, fmt.Errorf("%w: address %v", ErrInsufficientFundsForTransfer, msg.From.Hex())
} }
@ -509,7 +529,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
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
) )
if contractCreation { if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, value) ret, _, st.gasRemaining, vmerr = st.evm.Create(msg.From, msg.Data, st.gasRemaining, &msg.Value)
} else { } else {
// Increment the nonce for the next transaction. // Increment the nonce for the next transaction.
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall) st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1, tracing.NonceChangeEoACall)
@ -532,7 +552,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
// Execute the transaction's call. // Execute the transaction's call.
ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value) ret, st.gasRemaining, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, &msg.Value)
} }
// Record the gas used excluding gas refunds. This value represents the actual // Record the gas used excluding gas refunds. This value represents the actual
@ -556,13 +576,13 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
} }
st.returnGas() st.returnGas()
effectiveTipU256, _ := uint256.FromBig(msg.GasPrice) effectiveTipU256 := new(uint256.Int).Set(&msg.GasPrice)
if rules.IsLondon { if rules.IsLondon {
baseFee, _ := uint256.FromBig(st.evm.Context.BaseFee) baseFee := uint256.MustFromBig(st.evm.Context.BaseFee)
effectiveTipU256 = new(uint256.Int).Sub(effectiveTipU256, baseFee) effectiveTipU256.Sub(effectiveTipU256, baseFee)
} }
if st.evm.Config.NoBaseFee && msg.GasFeeCap.Sign() == 0 && msg.GasTipCap.Sign() == 0 { if st.evm.Config.NoBaseFee && msg.GasFeeCap.IsZero() && msg.GasTipCap.IsZero() {
// Skip fee payment when NoBaseFee is set and the fee fields // Skip fee payment when NoBaseFee is set and the fee fields
// are 0. This avoids a negative effectiveTip being applied to // are 0. This avoids a negative effectiveTip being applied to
// the coinbase when simulating calls. // the coinbase when simulating calls.
@ -666,7 +686,7 @@ func (st *stateTransition) calcRefund() uint64 {
// exchanged at the original rate. // exchanged at the original rate.
func (st *stateTransition) returnGas() { func (st *stateTransition) returnGas() {
remaining := uint256.NewInt(st.gasRemaining) remaining := uint256.NewInt(st.gasRemaining)
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice)) remaining.Mul(remaining, &st.msg.GasPrice)
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn) st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 { if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil && st.gasRemaining > 0 {

View file

@ -47,7 +47,7 @@ func newstack() *Stack {
// falls back to the pool. // falls back to the pool.
func newstackWithAlloc(alloc arena.Allocator) *Stack { func newstackWithAlloc(alloc arena.Allocator) *Stack {
if _, ok := alloc.(*arena.BumpAllocator); ok { if _, ok := alloc.(*arena.BumpAllocator); ok {
s := new(Stack) s := arena.New[Stack](alloc)
s.data = arena.MakeSlice[uint256.Int](alloc, 0, 1024) s.data = arena.MakeSlice[uint256.Int](alloc, 0, 1024)
s.arenaAlloc = true s.arenaAlloc = true
return s return s

View file

@ -55,6 +55,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
EnablePreimageRecording bool EnablePreimageRecording bool
EnableWitnessStats bool EnableWitnessStats bool
StatelessSelfValidation bool StatelessSelfValidation bool
EnableArenaAlloc bool
EnableStateSizeTracking bool EnableStateSizeTracking bool
VMTrace string VMTrace string
VMTraceJsonConfig string VMTraceJsonConfig string
@ -108,6 +109,7 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.EnablePreimageRecording = c.EnablePreimageRecording enc.EnablePreimageRecording = c.EnablePreimageRecording
enc.EnableWitnessStats = c.EnableWitnessStats enc.EnableWitnessStats = c.EnableWitnessStats
enc.StatelessSelfValidation = c.StatelessSelfValidation enc.StatelessSelfValidation = c.StatelessSelfValidation
enc.EnableArenaAlloc = c.EnableArenaAlloc
enc.EnableStateSizeTracking = c.EnableStateSizeTracking enc.EnableStateSizeTracking = c.EnableStateSizeTracking
enc.VMTrace = c.VMTrace enc.VMTrace = c.VMTrace
enc.VMTraceJsonConfig = c.VMTraceJsonConfig enc.VMTraceJsonConfig = c.VMTraceJsonConfig
@ -165,6 +167,7 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
EnablePreimageRecording *bool EnablePreimageRecording *bool
EnableWitnessStats *bool EnableWitnessStats *bool
StatelessSelfValidation *bool StatelessSelfValidation *bool
EnableArenaAlloc *bool
EnableStateSizeTracking *bool EnableStateSizeTracking *bool
VMTrace *string VMTrace *string
VMTraceJsonConfig *string VMTraceJsonConfig *string
@ -297,6 +300,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.StatelessSelfValidation != nil { if dec.StatelessSelfValidation != nil {
c.StatelessSelfValidation = *dec.StatelessSelfValidation c.StatelessSelfValidation = *dec.StatelessSelfValidation
} }
if dec.EnableArenaAlloc != nil {
c.EnableArenaAlloc = *dec.EnableArenaAlloc
}
if dec.EnableStateSizeTracking != nil { if dec.EnableStateSizeTracking != nil {
c.EnableStateSizeTracking = *dec.EnableStateSizeTracking c.EnableStateSizeTracking = *dec.EnableStateSizeTracking
} }

View file

@ -81,10 +81,10 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
// Normalize the max fee per gas the call is willing to spend. // Normalize the max fee per gas the call is willing to spend.
var feeCap *big.Int var feeCap *big.Int
if call.GasFeeCap != nil { if !call.GasFeeCap.IsZero() {
feeCap = call.GasFeeCap feeCap = call.GasFeeCap.ToBig()
} else if call.GasPrice != nil { } else if !call.GasPrice.IsZero() {
feeCap = call.GasPrice feeCap = call.GasPrice.ToBig()
} else { } else {
feeCap = common.Big0 feeCap = common.Big0
} }
@ -93,17 +93,18 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
balance := opts.State.GetBalance(call.From).ToBig() balance := opts.State.GetBalance(call.From).ToBig()
available := balance available := balance
if call.Value != nil { if !call.Value.IsZero() {
if call.Value.Cmp(available) >= 0 { valueBig := call.Value.ToBig()
if valueBig.Cmp(available) >= 0 {
return 0, nil, core.ErrInsufficientFundsForTransfer return 0, nil, core.ErrInsufficientFundsForTransfer
} }
available.Sub(available, call.Value) available.Sub(available, valueBig)
} }
if opts.Config.IsCancun(opts.Header.Number, opts.Header.Time) && len(call.BlobHashes) > 0 { if opts.Config.IsCancun(opts.Header.Number, opts.Header.Time) && len(call.BlobHashes) > 0 {
blobGasPerBlob := new(big.Int).SetInt64(params.BlobTxBlobGasPerBlob) blobGasPerBlob := new(big.Int).SetInt64(params.BlobTxBlobGasPerBlob)
blobBalanceUsage := new(big.Int).SetInt64(int64(len(call.BlobHashes))) blobBalanceUsage := new(big.Int).SetInt64(int64(len(call.BlobHashes)))
blobBalanceUsage.Mul(blobBalanceUsage, blobGasPerBlob) blobBalanceUsage.Mul(blobBalanceUsage, blobGasPerBlob)
blobBalanceUsage.Mul(blobBalanceUsage, call.BlobGasFeeCap) blobBalanceUsage.Mul(blobBalanceUsage, call.BlobGasFeeCap.ToBig())
if blobBalanceUsage.Cmp(available) >= 0 { if blobBalanceUsage.Cmp(available) >= 0 {
return 0, nil, core.ErrInsufficientFunds return 0, nil, core.ErrInsufficientFunds
} }
@ -113,9 +114,9 @@ func Estimate(ctx context.Context, call *core.Message, opts *Options, gasCap uin
// If the allowance is larger than maximum uint64, skip checking // If the allowance is larger than maximum uint64, skip checking
if allowance.IsUint64() && hi > allowance.Uint64() { if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value transfer := new(big.Int)
if transfer == nil { if !call.Value.IsZero() {
transfer = new(big.Int) transfer = call.Value.ToBig()
} }
log.Debug("Gas estimation capped by limited funds", "original", hi, "balance", balance, log.Debug("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "maxFeePerGas", feeCap, "fundable", allowance) "sent", transfer, "maxFeePerGas", feeCap, "fundable", allowance)
@ -252,7 +253,7 @@ func run(ctx context.Context, call *core.Message, opts *Options) (*core.Executio
if call.GasPrice.Sign() == 0 { if call.GasPrice.Sign() == 0 {
evmContext.BaseFee = new(big.Int) evmContext.BaseFee = new(big.Int)
} }
if call.BlobGasFeeCap != nil && call.BlobGasFeeCap.BitLen() == 0 { if call.BlobGasFeeCap.IsZero() {
evmContext.BlobBaseFee = new(big.Int) evmContext.BlobBaseFee = new(big.Int)
} }
evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true}) evm := vm.NewEVM(evmContext, dirtyState, opts.Config, vm.Config{NoBaseFee: true})

View file

@ -994,7 +994,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc
if msg.GasPrice.Sign() == 0 { if msg.GasPrice.Sign() == 0 {
blockContext.BaseFee = new(big.Int) blockContext.BaseFee = new(big.Int)
} }
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { if !msg.BlobGasFeeCap.IsZero() && msg.BlobGasFeeCap.BitLen() == 0 {
blockContext.BlobBaseFee = new(big.Int) blockContext.BlobBaseFee = new(big.Int)
} }
if config != nil { if config != nil {

View file

@ -761,7 +761,7 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s
if msg.GasPrice.Sign() == 0 { if msg.GasPrice.Sign() == 0 {
blockContext.BaseFee = new(big.Int) blockContext.BaseFee = new(big.Int)
} }
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { if !msg.BlobGasFeeCap.IsZero() && msg.BlobGasFeeCap.BitLen() == 0 {
blockContext.BlobBaseFee = new(big.Int) blockContext.BlobBaseFee = new(big.Int)
} }
evm := b.GetEVM(ctx, state, header, vmConfig, blockContext) evm := b.GetEVM(ctx, state, header, vmConfig, blockContext)
@ -1366,7 +1366,7 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH
if msg.GasPrice.Sign() == 0 { if msg.GasPrice.Sign() == 0 {
evm.Context.BaseFee = new(big.Int) evm.Context.BaseFee = new(big.Int)
} }
if msg.BlobGasFeeCap != nil && msg.BlobGasFeeCap.BitLen() == 0 { if !msg.BlobGasFeeCap.IsZero() && msg.BlobGasFeeCap.BitLen() == 0 {
evm.Context.BlobBaseFee = new(big.Int) evm.Context.BlobBaseFee = new(big.Int)
} }
res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)) res, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit))

View file

@ -477,18 +477,25 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck bool) *c
if args.AccessList != nil { if args.AccessList != nil {
accessList = *args.AccessList accessList = *args.AccessList
} }
bigToU256 := func(b *big.Int) uint256.Int {
if b == nil {
return uint256.Int{}
}
v, _ := uint256.FromBig(b)
return *v
}
return &core.Message{ return &core.Message{
From: args.from(), From: args.from(),
To: args.To, To: args.To,
Value: (*big.Int)(args.Value), Value: bigToU256((*big.Int)(args.Value)),
Nonce: uint64(*args.Nonce), Nonce: uint64(*args.Nonce),
GasLimit: uint64(*args.Gas), GasLimit: uint64(*args.Gas),
GasPrice: gasPrice, GasPrice: bigToU256(gasPrice),
GasFeeCap: gasFeeCap, GasFeeCap: bigToU256(gasFeeCap),
GasTipCap: gasTipCap, GasTipCap: bigToU256(gasTipCap),
Data: args.data(), Data: args.data(),
AccessList: accessList, AccessList: accessList,
BlobGasFeeCap: (*big.Int)(args.BlobFeeCap), BlobGasFeeCap: bigToU256((*big.Int)(args.BlobFeeCap)),
BlobHashes: args.BlobHashes, BlobHashes: args.BlobHashes,
SetCodeAuthorizations: args.AuthorizationList, SetCodeAuthorizations: args.AuthorizationList,
SkipNonceChecks: skipNonceCheck, SkipNonceChecks: skipNonceCheck,

View file

@ -35,7 +35,6 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/tracers/logger" "github.com/ethereum/go-ethereum/eth/tracers/logger"
"github.com/holiman/uint256"
) )
func initMatcher(st *testMatcher) { func initMatcher(st *testMatcher) {
@ -316,7 +315,7 @@ func runBenchmark(b *testing.B, t *StateTest) {
start := time.Now() start := time.Now()
// Execute the message. // Execute the message.
_, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, msg.GasLimit, uint256.MustFromBig(msg.Value)) _, leftOverGas, err := evm.Call(sender.Address(), *msg.To, msg.Data, msg.GasLimit, &msg.Value)
if err != nil { if err != nil {
b.Error(err) b.Error(err)
return return

View file

@ -477,19 +477,23 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess
} }
} }
var blobGasFeeCap uint256.Int
if tx.BlobGasFeeCap != nil {
blobGasFeeCap = *uint256.MustFromBig(tx.BlobGasFeeCap)
}
msg := &core.Message{ msg := &core.Message{
From: from, From: from,
To: to, To: to,
Nonce: tx.Nonce, Nonce: tx.Nonce,
Value: value, Value: *uint256.MustFromBig(value),
GasLimit: gasLimit, GasLimit: gasLimit,
GasPrice: gasPrice, GasPrice: *uint256.MustFromBig(gasPrice),
GasFeeCap: tx.MaxFeePerGas, GasFeeCap: *uint256.MustFromBig(tx.MaxFeePerGas),
GasTipCap: tx.MaxPriorityFeePerGas, GasTipCap: *uint256.MustFromBig(tx.MaxPriorityFeePerGas),
Data: data, Data: data,
AccessList: accessList, AccessList: accessList,
BlobHashes: tx.BlobVersionedHashes, BlobHashes: tx.BlobVersionedHashes,
BlobGasFeeCap: tx.BlobGasFeeCap, BlobGasFeeCap: blobGasFeeCap,
SetCodeAuthorizations: authList, SetCodeAuthorizations: authList,
} }
return msg, nil return msg, nil