diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index b2e5f70714..a9bb982119 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -213,7 +213,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, if beaconRoot := pre.Env.ParentBeaconBlockRoot; beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } - if pre.Env.BlockHashes != nil && chainConfig.IsPrague(new(big.Int).SetUint64(pre.Env.Number), pre.Env.Timestamp) { + rules := evm.Rules() + if pre.Env.BlockHashes != nil && rules.IsPrague { var ( prevNumber = pre.Env.Number - 1 prevHash = pre.Env.BlockHashes[math.HexOrDecimal64(prevNumber)] @@ -233,7 +234,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg}) continue } - msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee) + msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee, rules) if err != nil { log.Warn("rejected tx", "index", i, "hash", tx.Hash(), "error", err) rejectedTxs = append(rejectedTxs, &rejectedTx{i, err.Error()}) diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go index 2bc4f73b60..e1f3dca4c7 100644 --- a/cmd/evm/internal/t8ntool/transaction.go +++ b/cmd/evm/internal/t8ntool/transaction.go @@ -132,9 +132,9 @@ func Transaction(ctx *cli.Context) error { } else { r.Address = sender } - // Check intrinsic gas rules := chainConfig.Rules(common.Big0, true, 0) - gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) + // Check intrinsic gas + gas, err := tx.IntrinsicGas(&rules) if err != nil { r.Error = err results = append(results, r) diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go index e946ccddd5..0c7cebd9fe 100644 --- a/cmd/evm/internal/t8ntool/transition.go +++ b/cmd/evm/internal/t8ntool/transition.go @@ -245,7 +245,7 @@ func applyMergeChecks(env *stEnv, chainConfig *params.ChainConfig) error { switch { case env.Random == nil: return NewError(ErrorConfig, errors.New("post-merge requires currentRandom to be defined in env")) - case env.Difficulty != nil && env.Difficulty.BitLen() != 0: + case env.Difficulty != nil && env.Difficulty.Sign() != 0: return NewError(ErrorConfig, errors.New("post-merge difficulty must be zero (or omitted) in env")) } env.Difficulty = nil diff --git a/core/bench_test.go b/core/bench_test.go index 00f924076a..a9b4f558c6 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -90,20 +90,21 @@ func genValueTx(nbytes int) func(int, *BlockGen) { data := make([]byte, nbytes) return func(i int, gen *BlockGen) { toaddr := common.Address{} - gas, _ := IntrinsicGas(data, nil, nil, false, false, false, false) signer := gen.Signer() gasPrice := big.NewInt(0) if gen.header.BaseFee != nil { gasPrice = gen.header.BaseFee } - tx, _ := types.SignNewTx(benchRootKey, signer, &types.LegacyTx{ + rules := params.TestChainConfig.Rules(common.Big0, true, 0) + txdata := &types.LegacyTx{ Nonce: gen.TxNonce(benchRootAddr), To: &toaddr, Value: big.NewInt(1), - Gas: gas, Data: data, GasPrice: gasPrice, - }) + } + txdata.Gas = types.IntrinsicGas(txdata, &rules) + tx, _ := types.SignNewTx(benchRootKey, signer, txdata) gen.AddTx(tx) } } diff --git a/core/state_prefetcher.go b/core/state_prefetcher.go index 805df5ef62..193144a704 100644 --- a/core/state_prefetcher.go +++ b/core/state_prefetcher.go @@ -51,6 +51,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c blockContext = NewEVMBlockContext(header, p.chain, nil) evm = vm.NewEVM(blockContext, statedb, p.config, cfg) signer = types.MakeSigner(p.config, header.Number, header.Time) + rules = evm.Rules() ) // Iterate over and process the individual transactions byzantium := p.config.IsByzantium(block.Number()) @@ -60,7 +61,7 @@ func (p *statePrefetcher) Prefetch(block *types.Block, statedb *state.StateDB, c return } // Convert the transaction into an executable message and pre-cache its sender - msg, err := TransactionToMessage(tx, signer, header.BaseFee) + msg, err := TransactionToMessage(tx, signer, header.BaseFee, rules) if err != nil { return // Also invalid block, bail out } diff --git a/core/state_processor.go b/core/state_processor.go index 322bd24f41..560f420f8e 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -69,29 +69,29 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { misc.ApplyDAOHardFork(statedb) } - var ( - context vm.BlockContext - signer = types.MakeSigner(p.config, header.Number, header.Time) - ) // Apply pre-execution system calls. var tracingStateDB = vm.StateDB(statedb) if hooks := cfg.Tracer; hooks != nil { tracingStateDB = state.NewHookedState(statedb, hooks) } - context = NewEVMBlockContext(header, p.chain, nil) - evm := vm.NewEVM(context, tracingStateDB, p.config, cfg) + var ( + context = NewEVMBlockContext(header, p.chain, nil) + evm = vm.NewEVM(context, tracingStateDB, p.config, cfg) + rules = evm.Rules() + signer = types.MakeSigner(p.config, header.Number, header.Time) + ) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { ProcessBeaconBlockRoot(*beaconRoot, evm) } - if p.config.IsPrague(block.Number(), block.Time()) || p.config.IsVerkle(block.Number(), block.Time()) { + if rules.IsPrague || rules.IsVerkle { ProcessParentBlockHash(block.ParentHash(), evm) } // Iterate over and process the individual transactions for i, tx := range block.Transactions() { - msg, err := TransactionToMessage(tx, signer, header.BaseFee) + msg, err := TransactionToMessage(tx, signer, header.BaseFee, rules) if err != nil { return nil, fmt.Errorf("could not apply tx %d [%v]: %w", i, tx.Hash().Hex(), err) } @@ -205,7 +205,7 @@ func MakeReceipt(evm *vm.EVM, result *ExecutionResult, statedb *state.StateDB, b // for the transaction, gas used and an error if the transaction failed, // indicating the block was invalid. func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { - msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee) + msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee, evm.Rules()) if err != nil { return nil, err } diff --git a/core/state_transition.go b/core/state_transition.go index f9c9a2ab5a..b1150f8e99 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -67,55 +67,6 @@ func (result *ExecutionResult) Revert() []byte { return common.CopyBytes(result.ReturnData) } -// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.SetCodeAuthorization, isContractCreation, isHomestead, isEIP2028, isEIP3860 bool) (uint64, error) { - // Set the starting gas for the raw transaction - var gas uint64 - if isContractCreation && isHomestead { - gas = params.TxGasContractCreation - } else { - gas = params.TxGas - } - dataLen := uint64(len(data)) - // Bump the required gas by the amount of transactional data - if dataLen > 0 { - // Zero and non-zero bytes are priced differently - z := uint64(bytes.Count(data, []byte{0})) - nz := dataLen - z - - // Make sure we don't exceed uint64 for all data combinations - nonZeroGas := params.TxDataNonZeroGasFrontier - if isEIP2028 { - nonZeroGas = params.TxDataNonZeroGasEIP2028 - } - if (math.MaxUint64-gas)/nonZeroGas < nz { - return 0, ErrGasUintOverflow - } - gas += nz * nonZeroGas - - if (math.MaxUint64-gas)/params.TxDataZeroGas < z { - return 0, ErrGasUintOverflow - } - gas += z * params.TxDataZeroGas - - if isContractCreation && isEIP3860 { - lenWords := toWordSize(dataLen) - if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { - return 0, ErrGasUintOverflow - } - gas += lenWords * params.InitCodeWordGas - } - } - if accessList != nil { - gas += uint64(len(accessList)) * params.TxAccessListAddressGas - gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas - } - if authList != nil { - gas += uint64(len(authList)) * params.CallNewAccountGas - } - return gas, nil -} - // FloorDataGas computes the minimum gas required for a transaction based on its data tokens (EIP-7623). func FloorDataGas(data []byte) (uint64, error) { var ( @@ -131,15 +82,6 @@ func FloorDataGas(data []byte) (uint64, error) { return params.TxGas + tokens*params.TxCostFloorPerToken, nil } -// toWordSize returns the ceiled word size required for init code payment calculation. -func toWordSize(size uint64) uint64 { - if size > math.MaxUint64-31 { - return math.MaxUint64/32 + 1 - } - - return (size + 31) / 32 -} - // A Message contains the data derived from a single transaction that is relevant to state // processing. type Message struct { @@ -151,6 +93,7 @@ type Message struct { GasPrice *big.Int GasFeeCap *big.Int GasTipCap *big.Int + IntrinsicGas uint64 Data []byte AccessList types.AccessList BlobGasFeeCap *big.Int @@ -167,7 +110,7 @@ type Message struct { } // TransactionToMessage converts a transaction into a Message. -func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int) (*Message, error) { +func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.Int, rules *params.Rules) (*Message, error) { msg := &Message{ Nonce: tx.Nonce(), GasLimit: tx.Gas(), @@ -191,7 +134,14 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In msg.GasPrice = msg.GasFeeCap } } - var err error + // Fill in intrinsic gas. + intrinsicGas, err := tx.IntrinsicGas(rules) + if err != nil { + return nil, err + } + msg.IntrinsicGas = intrinsicGas + + // Recover sender. msg.From, err = types.Sender(s, tx) return msg, err } @@ -426,15 +376,12 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { rules = st.evm.ChainConfig().Rules(st.evm.Context.BlockNumber, st.evm.Context.Random != nil, st.evm.Context.Time) contractCreation = msg.To == nil floorDataGas uint64 + err error ) // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) - if err != nil { - return nil, err - } - if st.gasRemaining < gas { - return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) + if st.gasRemaining < msg.IntrinsicGas { + return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, msg.IntrinsicGas) } // Gas limit suffices for the floor data cost (EIP-7623) if rules.IsPrague { @@ -447,9 +394,9 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { } } if t := st.evm.Config.Tracer; t != nil && t.OnGasChange != nil { - t.OnGasChange(st.gasRemaining, st.gasRemaining-gas, tracing.GasChangeTxIntrinsicGas) + t.OnGasChange(st.gasRemaining, st.gasRemaining-msg.IntrinsicGas, tracing.GasChangeTxIntrinsicGas) } - st.gasRemaining -= gas + st.gasRemaining -= msg.IntrinsicGas if rules.IsEIP4762 { st.evm.AccessEvents.AddTxOrigin(msg.From) diff --git a/core/txpool/validation.go b/core/txpool/validation.go index e370f2ce84..f25abc0e6e 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -112,7 +112,7 @@ func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types } // Ensure the transaction has more gas than the bare minimum needed to cover // the transaction metadata - intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsIstanbul, rules.IsShanghai) + intrGas, err := tx.IntrinsicGas(&rules) if err != nil { return err } diff --git a/core/types/transaction.go b/core/types/transaction.go index a2f4104635..9d196e3ab6 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -21,12 +21,14 @@ import ( "errors" "fmt" "io" + "math" "math/big" "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" ) @@ -40,6 +42,7 @@ var ( errInvalidYParity = errors.New("'yParity' field must be 0 or 1") errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match") errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction") + ErrGasUintOverflow = errors.New("gas uint64 overflow") ) // Transaction types. @@ -86,6 +89,7 @@ type TxData interface { value() *big.Int nonce() uint64 to() *common.Address + setCodeAuthorizations() []SetCodeAuthorization rawSignatureValues() (v, r, s *big.Int) setSignatureValues(chainID, v, r, s *big.Int) @@ -479,11 +483,7 @@ func (tx *Transaction) WithBlobTxSidecar(sideCar *BlobTxSidecar) *Transaction { // SetCodeAuthorizations returns the authorizations list of the transaction. func (tx *Transaction) SetCodeAuthorizations() []SetCodeAuthorization { - setcodetx, ok := tx.inner.(*SetCodeTx) - if !ok { - return nil - } - return setcodetx.AuthList + return tx.inner.setCodeAuthorizations() } // SetCodeAuthorities returns a list of unique authorities from the @@ -581,6 +581,78 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e return &Transaction{inner: cpy, time: tx.time}, nil } +// IntrinsicGas returns the calculated intrinsic gas for the given transaction. +func (tx *Transaction) IntrinsicGas(rules *params.Rules) (uint64, error) { + return calcIntrinsicGas(tx.inner, rules) +} + +// IntrinsicGas returns the calculated intrinsic gas for the given transaction. +func IntrinsicGas(txdata TxData, rules *params.Rules) uint64 { + gas, err := calcIntrinsicGas(txdata, rules) + if err != nil { + panic(err) + } + return gas +} + +// calcIntrinsicGas is an internal function that drives exported versions of the function. +func calcIntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) { + // Set the starting gas for the raw transaction + var gas uint64 + if txdata.to() == nil && rules.IsHomestead { + gas = params.TxGasContractCreation + } else { + gas = params.TxGas + } + dataLen := uint64(len(txdata.data())) + // Bump the required gas by the amount of transactional data + if dataLen > 0 { + // Zero and non-zero bytes are priced differently + z := uint64(bytes.Count(txdata.data(), []byte{0})) + nz := dataLen - z + + // Make sure we don't exceed uint64 for all data combinations + nonZeroGas := params.TxDataNonZeroGasFrontier + if rules.IsIstanbul { + nonZeroGas = params.TxDataNonZeroGasEIP2028 + } + if (math.MaxUint64-gas)/nonZeroGas < nz { + return 0, ErrGasUintOverflow + } + gas += nz * nonZeroGas + + if (math.MaxUint64-gas)/params.TxDataZeroGas < z { + return 0, ErrGasUintOverflow + } + gas += z * params.TxDataZeroGas + + if txdata.to() == nil && rules.IsShanghai { + lenWords := toWordSize(dataLen) + if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { + return 0, ErrGasUintOverflow + } + gas += lenWords * params.InitCodeWordGas + } + } + if txdata.accessList() != nil { + gas += uint64(len(txdata.accessList())) * params.TxAccessListAddressGas + gas += uint64(txdata.accessList().StorageKeys()) * params.TxAccessListStorageKeyGas + } + if txdata.setCodeAuthorizations() != nil { + gas += uint64(len(txdata.setCodeAuthorizations())) * params.CallNewAccountGas + } + return gas, nil +} + +// toWordSize returns the ceiled word size required for init code payment calculation. +func toWordSize(size uint64) uint64 { + if size > math.MaxUint64-31 { + return math.MaxUint64/32 + 1 + } + + return (size + 31) / 32 +} + // Transactions implements DerivableList for transactions. type Transactions []*Transaction diff --git a/core/types/tx_access_list.go b/core/types/tx_access_list.go index 915de9a8ab..6955461a0a 100644 --- a/core/types/tx_access_list.go +++ b/core/types/tx_access_list.go @@ -107,6 +107,9 @@ func (tx *AccessListTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *AccessListTx) value() *big.Int { return tx.Value } func (tx *AccessListTx) nonce() uint64 { return tx.Nonce } func (tx *AccessListTx) to() *common.Address { return tx.To } +func (tx *AccessListTx) setCodeAuthorizations() []SetCodeAuthorization { + return nil +} func (tx *AccessListTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { return dst.Set(tx.GasPrice) diff --git a/core/types/tx_blob.go b/core/types/tx_blob.go index 9b1d53958f..823a4facdf 100644 --- a/core/types/tx_blob.go +++ b/core/types/tx_blob.go @@ -179,6 +179,9 @@ func (tx *BlobTx) value() *big.Int { return tx.Value.ToBig() } func (tx *BlobTx) nonce() uint64 { return tx.Nonce } func (tx *BlobTx) to() *common.Address { tmp := tx.To; return &tmp } func (tx *BlobTx) blobGas() uint64 { return params.BlobTxBlobGasPerBlob * uint64(len(tx.BlobHashes)) } +func (tx *BlobTx) setCodeAuthorizations() []SetCodeAuthorization { + return nil +} func (tx *BlobTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { if baseFee == nil { diff --git a/core/types/tx_dynamic_fee.go b/core/types/tx_dynamic_fee.go index bba81464f8..3ff0bd3846 100644 --- a/core/types/tx_dynamic_fee.go +++ b/core/types/tx_dynamic_fee.go @@ -96,6 +96,9 @@ func (tx *DynamicFeeTx) gasPrice() *big.Int { return tx.GasFeeCap } func (tx *DynamicFeeTx) value() *big.Int { return tx.Value } func (tx *DynamicFeeTx) nonce() uint64 { return tx.Nonce } func (tx *DynamicFeeTx) to() *common.Address { return tx.To } +func (tx *DynamicFeeTx) setCodeAuthorizations() []SetCodeAuthorization { + return nil +} func (tx *DynamicFeeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { if baseFee == nil { diff --git a/core/types/tx_legacy.go b/core/types/tx_legacy.go index 49f0a98809..26f02aeed0 100644 --- a/core/types/tx_legacy.go +++ b/core/types/tx_legacy.go @@ -103,6 +103,9 @@ func (tx *LegacyTx) gasFeeCap() *big.Int { return tx.GasPrice } func (tx *LegacyTx) value() *big.Int { return tx.Value } func (tx *LegacyTx) nonce() uint64 { return tx.Nonce } func (tx *LegacyTx) to() *common.Address { return tx.To } +func (tx *LegacyTx) setCodeAuthorizations() []SetCodeAuthorization { + return nil +} func (tx *LegacyTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { return dst.Set(tx.GasPrice) diff --git a/core/types/tx_setcode.go b/core/types/tx_setcode.go index b8e38ef1f7..fefc697b7a 100644 --- a/core/types/tx_setcode.go +++ b/core/types/tx_setcode.go @@ -193,6 +193,9 @@ func (tx *SetCodeTx) gasPrice() *big.Int { return tx.GasFeeCap.ToBig() } func (tx *SetCodeTx) value() *big.Int { return tx.Value.ToBig() } func (tx *SetCodeTx) nonce() uint64 { return tx.Nonce } func (tx *SetCodeTx) to() *common.Address { tmp := tx.To; return &tmp } +func (tx *SetCodeTx) setCodeAuthorizations() []SetCodeAuthorization { + return tx.AuthList +} func (tx *SetCodeTx) effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int { if baseFee == nil { diff --git a/core/verkle_witness_test.go b/core/verkle_witness_test.go index de2280ced1..7d829464b3 100644 --- a/core/verkle_witness_test.go +++ b/core/verkle_witness_test.go @@ -92,12 +92,13 @@ var ( func TestProcessVerkle(t *testing.T) { var ( code = common.FromHex(`6060604052600a8060106000396000f360606040526008565b00`) - intrinsicContractCreationGas, _ = IntrinsicGas(code, nil, nil, true, true, true, true) + rules = testVerkleChainConfig.Rules(common.Big0, true, 0) + intrinsicContractCreationGas, _ = types.NewContractCreation(0, big.NewInt(999), params.TxGas, big.NewInt(875000000), code).IntrinsicGas(&rules) // 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) + intrinsicCodeWithExtCodeCopyGas, _ = types.NewContractCreation(0, big.NewInt(999), params.TxGas, big.NewInt(875000000), codeWithExtCodeCopy).IntrinsicGas(&rules) signer = types.LatestSigner(testVerkleChainConfig) testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") bcdb = rawdb.NewMemoryDatabase() // Database for the blockchain diff --git a/core/vm/evm.go b/core/vm/evm.go index c28dcb2554..89d81e1bb7 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -591,6 +591,13 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash { // ChainConfig returns the environment's chain configuration func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } +// Rules returns the rules for the EVM given the current block's context. +func (evm *EVM) Rules() *params.Rules { + bctx := evm.Context + rules := evm.ChainConfig().Rules(bctx.BlockNumber, bctx.Random != nil, bctx.Time) + return &rules +} + func (evm *EVM) captureBegin(depth int, typ OpCode, from common.Address, to common.Address, input []byte, startGas uint64, value *big.Int) { tracer := evm.Config.Tracer if tracer.OnEnter != nil { diff --git a/eth/state_accessor.go b/eth/state_accessor.go index 99ed28d96a..1c6dd18483 100644 --- a/eth/state_accessor.go +++ b/eth/state_accessor.go @@ -235,13 +235,16 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, return nil, vm.BlockContext{}, nil, nil, err } // Insert parent beacon block root in the state as per EIP-4788. - context := core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) - evm := vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{}) + var ( + context = core.NewEVMBlockContext(block.Header(), eth.blockchain, nil) + evm = vm.NewEVM(context, statedb, eth.blockchain.Config(), vm.Config{}) + rules = evm.Rules() + ) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } // If prague hardfork, insert parent block hash in the state as per EIP-2935. - if eth.blockchain.Config().IsPrague(block.Number(), block.Time()) { + if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } if txIndex == 0 && len(block.Transactions()) == 0 { @@ -254,7 +257,7 @@ func (eth *Ethereum) stateAtTransaction(ctx context.Context, block *types.Block, return tx, context, statedb, release, nil } // Assemble the transaction call message and return if the requested offset - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), rules) // Not yet the searched for transaction, execute on top of the current state statedb.SetTxContext(tx.Hash(), idx) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 17a0ad687a..182f9a7f08 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -270,10 +270,11 @@ func (api *API) traceChain(start, end *types.Block, config *TraceConfig, closed var ( signer = types.MakeSigner(api.backend.ChainConfig(), task.block.Number(), task.block.Time()) blockCtx = core.NewEVMBlockContext(task.block.Header(), api.chainContext(ctx), nil) + rules = api.backend.ChainConfig().Rules(task.block.Number(), blockCtx.Random != nil, task.block.Time()) ) // Trace all the transactions contained within for i, tx := range task.block.Transactions() { - msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, task.block.BaseFee(), &rules) txctx := &Context{ BlockHash: task.block.Hash(), BlockNumber: task.block.Number(), @@ -536,19 +537,20 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config chainConfig = api.backend.ChainConfig() vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) deleteEmptyObjects = chainConfig.IsEIP158(block.Number()) + evm = vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) + rules = evm.Rules() ) - evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } - if chainConfig.IsPrague(block.Number(), block.Time()) { + if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } for i, tx := range block.Transactions() { if err := ctx.Err(); err != nil { return nil, err } - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), rules) statedb.SetTxContext(tx.Hash(), i) if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err) @@ -600,12 +602,15 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac } defer release() - blockCtx := core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) - evm := vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{}) + var ( + blockCtx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil) + evm = vm.NewEVM(blockCtx, statedb, api.backend.ChainConfig(), vm.Config{}) + rules = evm.Rules() + ) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } - if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) { + if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } @@ -626,7 +631,7 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac ) for i, tx := range txs { // Generate the next state snapshot fast without tracing - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), rules) txctx := &Context{ BlockHash: blockHash, BlockNumber: block.Number(), @@ -653,6 +658,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat signer = types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()) results = make([]*txTraceResult, len(txs)) pend sync.WaitGroup + rules = api.backend.ChainConfig().Rules(block.Number(), block.Difficulty().Sign() == 0, block.Time()) ) threads := runtime.NumCPU() if threads > len(txs) { @@ -665,7 +671,7 @@ func (api *API) traceBlockParallel(ctx context.Context, block *types.Block, stat defer pend.Done() // Fetch and execute the next transaction trace tasks for task := range jobs { - msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(txs[task.index], signer, block.BaseFee(), &rules) txctx := &Context{ BlockHash: blockHash, BlockNumber: block.Number(), @@ -704,7 +710,7 @@ txloop: } // Generate the next state snapshot fast without tracing - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), &rules) statedb.SetTxContext(tx.Hash(), i) if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(msg.GasLimit)); err != nil { failed = err @@ -778,17 +784,20 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block // Note: This copies the config, to not screw up the main config chainConfig, canon = overrideConfig(chainConfig, config.Overrides) } - evm := vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) + var ( + evm = vm.NewEVM(vmctx, statedb, chainConfig, vm.Config{}) + rules = evm.Rules() + ) if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } - if chainConfig.IsPrague(block.Number(), block.Time()) { + if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } for i, tx := range block.Transactions() { // Prepare the transaction for un-traced execution var ( - msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ = core.TransactionToMessage(tx, signer, block.BaseFee(), rules) vmConf vm.Config dump *os.File writer *bufio.Writer @@ -885,7 +894,8 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * return nil, err } defer release() - msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee()) + rules := api.backend.ChainConfig().Rules(block.Number(), vmctx.Random != nil, block.Time()) + msg, err := core.TransactionToMessage(tx, types.MakeSigner(api.backend.ChainConfig(), block.Number(), block.Time()), block.BaseFee(), &rules) if err != nil { return nil, err } @@ -967,7 +977,8 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc return nil, err } var ( - msg = args.ToMessage(vmctx.BaseFee, true, true) + rules = api.backend.ChainConfig().Rules(block.Number(), vmctx.Random != nil, block.Time()) + msg = args.ToMessage(&rules, vmctx.BaseFee, true, true) tx = args.ToTransaction(types.LegacyTxType) traceConfig *TraceConfig ) diff --git a/eth/tracers/api_test.go b/eth/tracers/api_test.go index fa39187694..cc5f49da93 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -178,11 +178,12 @@ func (b *testBackend) StateAtTransaction(ctx context.Context, block *types.Block signer := types.MakeSigner(b.chainConfig, block.Number(), block.Time()) context := core.NewEVMBlockContext(block.Header(), b.chain, nil) evm := vm.NewEVM(context, statedb, b.chainConfig, vm.Config{}) + rules := evm.Rules() for idx, tx := range block.Transactions() { if idx == txIndex { return tx, context, statedb, release, nil } - msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee()) + msg, _ := core.TransactionToMessage(tx, signer, block.BaseFee(), rules) if _, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())); err != nil { return nil, vm.BlockContext{}, nil, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err) } diff --git a/eth/tracers/internal/tracetest/calltrace_test.go b/eth/tracers/internal/tracetest/calltrace_test.go index b2486661e4..004557a4c9 100644 --- a/eth/tracers/internal/tracetest/calltrace_test.go +++ b/eth/tracers/internal/tracetest/calltrace_test.go @@ -125,11 +125,11 @@ func testCallTracer(tracerName string, dirPath string, t *testing.T) { if tracer.Hooks != nil { logState = state.NewHookedState(st.StateDB, tracer.Hooks) } - msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) + evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules()) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, logState, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { @@ -201,20 +201,20 @@ func benchTracer(tracerName string, test *callTracerTest, b *testing.B) { if err := tx.UnmarshalBinary(common.FromHex(test.Input)); err != nil { b.Fatalf("failed to parse testcase input: %v", err) } - signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) - context := test.Context.toBlockContext(test.Genesis) - msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) - if err != nil { - b.Fatalf("failed to prepare transaction for tracing: %v", err) - } state := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false, rawdb.HashScheme) defer state.Close() + signer := types.MakeSigner(test.Genesis.Config, new(big.Int).SetUint64(uint64(test.Context.Number)), uint64(test.Context.Time)) + context := test.Context.toBlockContext(test.Genesis) + evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{}) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules()) + if err != nil { + b.Fatalf("failed to prepare transaction for tracing: %v", err) + } + b.ReportAllocs() b.ResetTimer() - evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{}) - for i := 0; i < b.N; i++ { snap := state.StateDB.Snapshot() tracer, err := tracers.DefaultDirectory.New(tracerName, new(tracers.Context), nil, test.Genesis.Config) @@ -370,7 +370,7 @@ func TestInternals(t *testing.T) { t.Fatalf("test %v: failed to sign transaction: %v", tc.name, err) } evm := vm.NewEVM(context, logState, config, vm.Config{Tracer: tc.tracer.Hooks}) - msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0)) + msg, err := core.TransactionToMessage(tx, signer, big.NewInt(0), evm.Rules()) if err != nil { t.Fatalf("test %v: failed to create message: %v", tc.name, err) } diff --git a/eth/tracers/internal/tracetest/flat_calltrace_test.go b/eth/tracers/internal/tracetest/flat_calltrace_test.go index d1fa44e9d8..8d0532ed46 100644 --- a/eth/tracers/internal/tracetest/flat_calltrace_test.go +++ b/eth/tracers/internal/tracetest/flat_calltrace_test.go @@ -107,11 +107,11 @@ func flatCallTracerTestRunner(tracerName string, filename string, dirPath string return fmt.Errorf("failed to create call tracer: %v", err) } - msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) + evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules()) if err != nil { return fmt.Errorf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { diff --git a/eth/tracers/internal/tracetest/prestate_test.go b/eth/tracers/internal/tracetest/prestate_test.go index 29c2834ba2..20cf1af32e 100644 --- a/eth/tracers/internal/tracetest/prestate_test.go +++ b/eth/tracers/internal/tracetest/prestate_test.go @@ -99,11 +99,11 @@ func testPrestateTracer(tracerName string, dirPath string, t *testing.T) { t.Fatalf("failed to create call tracer: %v", err) } - msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) + evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules()) if err != nil { t.Fatalf("failed to prepare transaction for tracing: %v", err) } - evm := vm.NewEVM(context, state.StateDB, test.Genesis.Config, vm.Config{Tracer: tracer.Hooks}) tracer.OnTxStart(evm.GetVMContext(), tx, msg.From) vmRet, err := core.ApplyMessage(evm, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go index a72dbf6ee6..8ad177716b 100644 --- a/eth/tracers/tracers_test.go +++ b/eth/tracers/tracers_test.go @@ -80,7 +80,7 @@ func BenchmarkTransactionTraceV2(b *testing.B) { evm := vm.NewEVM(context, state.StateDB, params.AllEthashProtocolChanges, vm.Config{}) - msg, err := core.TransactionToMessage(tx, signer, context.BaseFee) + msg, err := core.TransactionToMessage(tx, signer, context.BaseFee, evm.Rules()) if err != nil { b.Fatalf("failed to prepare transaction for tracing: %v", err) } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 8f736226c7..d7f16e22af 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -695,7 +695,8 @@ func applyMessage(ctx context.Context, b Backend, args TransactionArgs, state *s if err := args.CallDefaults(gp.Gas(), blockContext.BaseFee, b.ChainConfig().ChainID); err != nil { return nil, err } - msg := args.ToMessage(header.BaseFee, skipChecks, skipChecks) + rules := b.ChainConfig().Rules(header.Number, blockContext.Random != nil, header.Time) + msg := args.ToMessage(&rules, header.BaseFee, skipChecks, skipChecks) // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { @@ -838,7 +839,8 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr if err := args.CallDefaults(gasCap, header.BaseFee, b.ChainConfig().ChainID); err != nil { return 0, err } - call := args.ToMessage(header.BaseFee, true, true) + rules := b.ChainConfig().Rules(header.Number, header.Difficulty.Sign() == 0, header.Time) + call := args.ToMessage(&rules, header.BaseFee, true, true) // Run the gas estimation and wrap any revertals into a custom return estimate, revert, err := gasestimator.Estimate(ctx, call, opts, gasCap) @@ -1214,15 +1216,15 @@ func AccessList(ctx context.Context, b Backend, blockNrOrHash rpc.BlockNumberOrH // Copy the original db so we don't modify it statedb := db.Copy() - // Set the accesslist to the last al - args.AccessList = &accessList - msg := args.ToMessage(header.BaseFee, true, true) - // Apply the transaction with the access list tracer tracer := logger.NewAccessListTracer(accessList, addressesToExclude) config := vm.Config{Tracer: tracer.Hooks(), NoBaseFee: true} evm := b.GetEVM(ctx, statedb, header, &config, nil) + // Set the accesslist to the last al + args.AccessList = &accessList + msg := args.ToMessage(evm.Rules(), header.BaseFee, true, true) + // Lower the basefee to 0 to avoid breaking EVM // invariants (basefee < feecap). if msg.GasPrice.Sign() == 0 { diff --git a/internal/ethapi/simulate.go b/internal/ethapi/simulate.go index b997cf297b..f782ea306e 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -259,12 +259,13 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tracingStateDB = state.NewHookedState(sim.state, hooks) } evm := vm.NewEVM(blockContext, tracingStateDB, sim.chainConfig, *vmConfig) + rules := evm.Rules() // It is possible to override precompiles with EVM bytecode, or // move them to another address. if precompiles != nil { evm.SetPrecompiles(precompiles) } - if sim.chainConfig.IsPrague(header.Number, header.Time) || sim.chainConfig.IsVerkle(header.Number, header.Time) { + if rules.IsPrague || rules.IsVerkle { core.ProcessParentBlockHash(header.ParentHash, evm) } if header.ParentBeaconRoot != nil { @@ -287,7 +288,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header, tracer.reset(txHash, uint(i)) sim.state.SetTxContext(txHash, i) // EoA check is always skipped, even in validation mode. - msg := call.ToMessage(header.BaseFee, !sim.validate, true) + msg := call.ToMessage(rules, header.BaseFee, !sim.validate, true) result, err := applyMessageWithEVM(ctx, evm, msg, timeout, sim.gp) if err != nil { txErr := txValidationError(err) diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 7a7d63c535..5eac4502e9 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -414,7 +414,7 @@ func (args *TransactionArgs) CallDefaults(globalGasCap uint64, baseFee *big.Int, // core evm. This method is used in calls and traces that do not require a real // live transaction. // Assumes that fields are not nil, i.e. setDefaults or CallDefaults has been called. -func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoACheck bool) *core.Message { +func (args *TransactionArgs) ToMessage(rules *params.Rules, baseFee *big.Int, skipNonceCheck, skipEoACheck bool) *core.Message { var ( gasPrice *big.Int gasFeeCap *big.Int @@ -447,11 +447,25 @@ func (args *TransactionArgs) ToMessage(baseFee *big.Int, skipNonceCheck, skipEoA if args.AccessList != nil { accessList = *args.AccessList } + + // Compute the intrinsic gas for stTransaction. Since the test file doesn't + // specify the transaction type we need to infer. This is hacky but we can use + // dynamic fee tx to represent all txs (with respect to instrinsic gas calc at + // least) types except the set code tx type. + var txdata types.TxData + if args.To == nil { + txdata = &types.DynamicFeeTx{To: args.To, Data: args.data(), AccessList: accessList} + } else { + txdata = &types.SetCodeTx{To: *args.To, Data: args.data(), AccessList: accessList, AuthList: args.AuthorizationList} + } + intrinsicGas := types.IntrinsicGas(txdata, rules) + return &core.Message{ From: args.from(), To: args.To, Value: (*big.Int)(args.Value), Nonce: uint64(*args.Nonce), + IntrinsicGas: intrinsicGas, GasLimit: uint64(*args.Gas), GasPrice: gasPrice, GasFeeCap: gasFeeCap, diff --git a/tests/state_test.go b/tests/state_test.go index 301bc3a7a9..3bea14744f 100644 --- a/tests/state_test.go +++ b/tests/state_test.go @@ -271,7 +271,7 @@ func runBenchmark(b *testing.B, t *StateTest) { } } post := t.json.Post[subtest.Fork][subtest.Index] - msg, err := t.json.Tx.toMessage(post, baseFee) + msg, err := t.json.Tx.toMessage(post, baseFee, &rules) if err != nil { b.Error(err) return diff --git a/tests/state_test_util.go b/tests/state_test_util.go index a22e470ad8..95455a5cad 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -257,6 +257,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh return st, common.Hash{}, 0, UnsupportedForkError{subtest.Fork} } vmconfig.ExtraEips = eips + rules := config.Rules(new(big.Int).SetUint64(t.json.Env.Number), t.json.Env.Random != nil, t.json.Env.Timestamp) block := t.genesis(config).ToBlock() st = MakePreState(rawdb.NewMemoryDatabase(), t.json.Pre, snapshotter, scheme) @@ -271,7 +272,7 @@ func (t *StateTest) RunNoVerify(subtest StateSubtest, vmconfig vm.Config, snapsh } } post := t.json.Post[subtest.Fork][subtest.Index] - msg, err := t.json.Tx.toMessage(post, baseFee) + msg, err := t.json.Tx.toMessage(post, baseFee, &rules) if err != nil { return st, common.Hash{}, 0, err } @@ -376,7 +377,7 @@ func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis { return genesis } -func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Message, error) { +func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int, rules *params.Rules) (*core.Message, error) { var from common.Address // If 'sender' field is present, use that if tx.Sender != nil { @@ -463,11 +464,24 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int) (*core.Mess } } + // Compute the intrinsic gas for stTransaction. Since the test file doesn't + // specify the transaction type we need to infer. This is hacky but we can use + // dynamic fee tx to represent all txs (with respect to instrinsic gas calc at + // least) types except the set code tx type. + var txdata types.TxData + if to == nil { + txdata = &types.DynamicFeeTx{To: to, Data: data, AccessList: accessList} + } else { + txdata = &types.SetCodeTx{To: *to, Data: data, AccessList: accessList, AuthList: authList} + } + gas := types.IntrinsicGas(txdata, rules) + msg := &core.Message{ From: from, To: to, Nonce: tx.Nonce, Value: value, + IntrinsicGas: gas, GasLimit: gasLimit, GasPrice: gasPrice, GasFeeCap: tx.MaxFeePerGas, diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index b2efabe82e..ec214bb1d5 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -80,7 +80,7 @@ func (tt *TransactionTest) Run() error { return } // Intrinsic gas - requiredGas, err = core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, rules.IsHomestead, rules.IsIstanbul, rules.IsShanghai) + requiredGas, err = tx.IntrinsicGas(rules) if err != nil { return }