From 90c234b86c614a2a503c74be9af0de5aac69ded3 Mon Sep 17 00:00:00 2001 From: Cedrick AHOUANGANSI Date: Sat, 1 Feb 2025 13:50:32 +0100 Subject: [PATCH 01/10] core/types: add method to compute transaction intrinsic gas --- core/types/transaction.go | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/core/types/transaction.go b/core/types/transaction.go index a2f4104635..3dd02c76de 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. @@ -581,6 +584,75 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e return &Transaction{inner: cpy, time: tx.time}, nil } +// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. +func (tx *Transaction) IntrinsicGas(rules *params.Rules) (uint64, error) { + var ( + data = tx.Data() + accessList = tx.AccessList() + authList = tx.SetCodeAuthorizations() + isContractCreation = tx.To() == nil + ) + + // Set the starting gas for the raw transaction + var gas uint64 + if isContractCreation && rules.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 + var nz uint64 + for _, byt := range data { + if byt != 0 { + nz++ + } + } + // 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 + + z := dataLen - nz + if (math.MaxUint64-gas)/params.TxDataZeroGas < z { + return 0, ErrGasUintOverflow + } + gas += z * params.TxDataZeroGas + + if isContractCreation && rules.IsShanghai { + 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 +} + +// 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 From 51753b5648e26e42db7801ff7628f53511dba586 Mon Sep 17 00:00:00 2001 From: Cedrick AHOUANGANSI Date: Fri, 7 Feb 2025 20:31:33 +0100 Subject: [PATCH 02/10] core: replace core.IntrinsicGas() with types.IntrinsicGas() pick 9798c84af core/types: add method to compute transaction intrinsic gas pick 2c620ab77 core: replace core.IntrinsicGas() with types.IntrinsicGas() fixup 0aef175eb removed unused commented code pick 989d56082 core/types: add IntrinsicGas() function to compute tx intrinsic gas --- cmd/evm/internal/t8ntool/transaction.go | 4 +- core/bench_test.go | 11 ++++- core/state_transition.go | 59 ++++--------------------- core/txpool/validation.go | 2 +- core/verkle_witness_test.go | 5 ++- tests/transaction_test_util.go | 2 +- 6 files changed, 26 insertions(+), 57 deletions(-) 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/core/bench_test.go b/core/bench_test.go index 155fa6c3b5..141ce90d12 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -90,12 +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 } + rules := params.TestChainConfig.Rules(common.Big0, true, 0) + tx0 := types.NewTx(&types.LegacyTx{ + Nonce: gen.TxNonce(benchRootAddr), + To: &toaddr, + Value: big.NewInt(1), + Gas: params.TxGas, + Data: data, + GasPrice: gasPrice, + }) + gas, _ := tx0.IntrinsicGas(&rules) tx, _ := types.SignNewTx(benchRootKey, signer, &types.LegacyTx{ Nonce: gen.TxNonce(benchRootAddr), To: &toaddr, diff --git a/core/state_transition.go b/core/state_transition.go index 0f9ee9eea5..5ce6d89b60 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 ( @@ -428,8 +379,16 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { floorDataGas uint64 ) + tx := types.NewTx(&types.LegacyTx{ + Nonce: msg.Nonce, + To: msg.To, + Value: msg.Value, + Gas: msg.GasLimit, + Data: msg.Data, + GasPrice: msg.GasPrice, + }) // 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) + gas, err := tx.IntrinsicGas(&rules) if err != nil { return nil, err } diff --git a/core/txpool/validation.go b/core/txpool/validation.go index 8747724247..abcc3f454a 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/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/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 } From 45c2c0bd3d3c0d7a92bd29eab21f95a88a12ff61 Mon Sep 17 00:00:00 2001 From: Cedrick AHOUANGANSI Date: Sat, 8 Feb 2025 20:25:45 +0100 Subject: [PATCH 03/10] core/types: add IntrinsicGas() function to compute tx intrinsic gas --- core/bench_test.go | 16 +++++----------- core/state_transition.go | 9 --------- core/types/transaction.go | 22 ++++++++++++---------- core/types/tx_access_list.go | 3 +++ core/types/tx_blob.go | 3 +++ core/types/tx_dynamic_fee.go | 3 +++ core/types/tx_legacy.go | 3 +++ core/types/tx_setcode.go | 3 +++ 8 files changed, 32 insertions(+), 30 deletions(-) diff --git a/core/bench_test.go b/core/bench_test.go index 141ce90d12..534b51acf8 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -96,23 +96,17 @@ func genValueTx(nbytes int) func(int, *BlockGen) { gasPrice = gen.header.BaseFee } rules := params.TestChainConfig.Rules(common.Big0, true, 0) - tx0 := types.NewTx(&types.LegacyTx{ + txdata := &types.LegacyTx{ Nonce: gen.TxNonce(benchRootAddr), To: &toaddr, Value: big.NewInt(1), Gas: params.TxGas, Data: data, GasPrice: gasPrice, - }) - gas, _ := tx0.IntrinsicGas(&rules) - tx, _ := types.SignNewTx(benchRootKey, signer, &types.LegacyTx{ - Nonce: gen.TxNonce(benchRootAddr), - To: &toaddr, - Value: big.NewInt(1), - Gas: gas, - Data: data, - GasPrice: gasPrice, - }) + } + gas, _ := types.IntrinsicGas(txdata, &rules) + txdata.Gas = gas + tx, _ := types.SignNewTx(benchRootKey, signer, txdata) gen.AddTx(tx) } } diff --git a/core/state_transition.go b/core/state_transition.go index 5ce6d89b60..2544fc0ece 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -82,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 { diff --git a/core/types/transaction.go b/core/types/transaction.go index 3dd02c76de..82ca0d6f1a 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -89,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) @@ -482,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 @@ -584,13 +581,18 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e return &Transaction{inner: cpy, time: tx.time}, nil } -// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. +// IntrinsicGas returns the 'intrinsic gas' computed for a message with the given data. func (tx *Transaction) IntrinsicGas(rules *params.Rules) (uint64, error) { + return IntrinsicGas(tx.inner, rules) +} + +// IntrinsicGas computes the 'intrinsic gas' for a message with the given data. +func IntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) { var ( - data = tx.Data() - accessList = tx.AccessList() - authList = tx.SetCodeAuthorizations() - isContractCreation = tx.To() == nil + data = txdata.data() + accessList = txdata.accessList() + authList = txdata.setCodeAuthorizations() + isContractCreation = txdata.to() == nil ) // Set the starting gas for the raw 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 { From 47daf83bb669431b37bcbf3dd29c5cba35f88346 Mon Sep 17 00:00:00 2001 From: Cedrick AHOUANGANSI Date: Wed, 19 Feb 2025 07:12:37 +0100 Subject: [PATCH 04/10] core, cores/types: added Gas field in Message, precomputed it and updated computation bytes count algo --- core/state_processor.go | 7 +++++++ core/state_transition.go | 15 +++------------ core/types/transaction.go | 33 +++++++++++---------------------- 3 files changed, 21 insertions(+), 34 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index 9241d091ad..1531f01586 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -133,6 +133,13 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { + rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time) + // Intrinsic gas + gas, err := tx.IntrinsicGas(&rules) + if err != nil { + return nil, err + } + msg.Gas = gas if hooks := evm.Config.Tracer; hooks != nil { if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) diff --git a/core/state_transition.go b/core/state_transition.go index 2544fc0ece..22c77c5816 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -93,6 +93,7 @@ type Message struct { GasPrice *big.Int GasFeeCap *big.Int GasTipCap *big.Int + Gas uint64 Data []byte AccessList types.AccessList BlobGasFeeCap *big.Int @@ -370,24 +371,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { floorDataGas uint64 ) - tx := types.NewTx(&types.LegacyTx{ - Nonce: msg.Nonce, - To: msg.To, - Value: msg.Value, - Gas: msg.GasLimit, - Data: msg.Data, - GasPrice: msg.GasPrice, - }) + gas := msg.Gas // Check clauses 4-5, subtract intrinsic gas if everything is correct - gas, err := tx.IntrinsicGas(&rules) - if err != nil { - return nil, err - } if st.gasRemaining < gas { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) } // Gas limit suffices for the floor data cost (EIP-7623) if rules.IsPrague { + var err error floorDataGas, err = FloorDataGas(msg.Data) if err != nil { return nil, err diff --git a/core/types/transaction.go b/core/types/transaction.go index 82ca0d6f1a..f489f3cce3 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -588,30 +588,20 @@ func (tx *Transaction) IntrinsicGas(rules *params.Rules) (uint64, error) { // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. func IntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) { - var ( - data = txdata.data() - accessList = txdata.accessList() - authList = txdata.setCodeAuthorizations() - isContractCreation = txdata.to() == nil - ) - // Set the starting gas for the raw transaction var gas uint64 - if isContractCreation && rules.IsHomestead { + if txdata.to() == nil && rules.IsHomestead { gas = params.TxGasContractCreation } else { gas = params.TxGas } - dataLen := uint64(len(data)) + 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 - var nz uint64 - for _, byt := range data { - if byt != 0 { - nz++ - } - } + 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 { @@ -622,13 +612,12 @@ func IntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) { } gas += nz * nonZeroGas - z := dataLen - nz if (math.MaxUint64-gas)/params.TxDataZeroGas < z { return 0, ErrGasUintOverflow } gas += z * params.TxDataZeroGas - if isContractCreation && rules.IsShanghai { + if txdata.to() == nil && rules.IsShanghai { lenWords := toWordSize(dataLen) if (math.MaxUint64-gas)/params.InitCodeWordGas < lenWords { return 0, ErrGasUintOverflow @@ -636,12 +625,12 @@ func IntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) { gas += lenWords * params.InitCodeWordGas } } - if accessList != nil { - gas += uint64(len(accessList)) * params.TxAccessListAddressGas - gas += uint64(accessList.StorageKeys()) * params.TxAccessListStorageKeyGas + if txdata.accessList() != nil { + gas += uint64(len(txdata.accessList())) * params.TxAccessListAddressGas + gas += uint64(txdata.accessList().StorageKeys()) * params.TxAccessListStorageKeyGas } - if authList != nil { - gas += uint64(len(authList)) * params.CallNewAccountGas + if txdata.setCodeAuthorizations() != nil { + gas += uint64(len(txdata.setCodeAuthorizations())) * params.CallNewAccountGas } return gas, nil } From 7dc804397be3659aec9e924f831335adb5ae94d8 Mon Sep 17 00:00:00 2001 From: Sina Mahmoodi Date: Wed, 12 Feb 2025 17:58:07 +0100 Subject: [PATCH 05/10] fix transaction tests --- tests/transaction_test_util.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index ec214bb1d5..116707d8ac 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -153,3 +153,35 @@ func (tt *TransactionTest) Run() error { } return nil } + +func getRules(config *params.ChainConfig, fork string) (params.Rules, error) { + switch fork { + case "Frontier": + return config.Rules(new(big.Int), false, 0), nil + case "Homestead": + return config.Rules(config.HomesteadBlock, false, 0), nil + case "EIP150": + return config.Rules(config.EIP150Block, false, 0), nil + case "EIP158": + return config.Rules(config.EIP158Block, false, 0), nil + case "Byzantium": + return config.Rules(config.ByzantiumBlock, false, 0), nil + case "Constantinople": + return config.Rules(config.ConstantinopleBlock, false, 0), nil + case "Istanbul": + return config.Rules(config.IstanbulBlock, false, 0), nil + case "Berlin": + return config.Rules(config.BerlinBlock, false, 0), nil + case "London": + return config.Rules(config.LondonBlock, false, 0), nil + case "Paris": + return config.Rules(config.LondonBlock, true, 0), nil + case "Shanghai": + return config.Rules(config.LondonBlock, true, *config.ShanghaiTime), nil + case "Cancun": + return config.Rules(config.LondonBlock, true, *config.CancunTime), nil + case "Prague": + return config.Rules(config.LondonBlock, true, *config.PragueTime), nil + } + return params.Rules{}, UnsupportedForkError{Name: fork} +} From f1ded868732d18849989ec7aca5a25c6459b0deb Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 12 Mar 2025 12:17:14 -0600 Subject: [PATCH 06/10] core: compute intrinsic gas in TransactionToMessage(..) --- cmd/evm/internal/t8ntool/execution.go | 3 ++- core/bench_test.go | 2 +- core/state_prefetcher.go | 3 ++- core/state_processor.go | 25 +++++++----------- core/state_transition.go | 11 ++++++-- core/types/transaction.go | 13 ++++++++-- core/vm/evm.go | 5 ++++ eth/state_accessor.go | 11 +++++--- eth/tracers/api.go | 26 ++++++++++++------- .../internal/tracetest/calltrace_test.go | 22 ++++++++-------- .../internal/tracetest/flat_calltrace_test.go | 4 +-- .../internal/tracetest/prestate_test.go | 4 +-- eth/tracers/tracers_test.go | 2 +- tests/state_test_util.go | 18 +++++++++++-- 14 files changed, 94 insertions(+), 55 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 7de1eb6949..7917fbc040 100644 --- a/cmd/evm/internal/t8ntool/execution.go +++ b/cmd/evm/internal/t8ntool/execution.go @@ -233,7 +233,8 @@ 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) + rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Difficulty.BitLen() == 0, evm.Context.Time) + 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/core/bench_test.go b/core/bench_test.go index 534b51acf8..1b8b47545c 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -104,7 +104,7 @@ func genValueTx(nbytes int) func(int, *BlockGen) { Data: data, GasPrice: gasPrice, } - gas, _ := types.IntrinsicGas(txdata, &rules) + gas := types.IntrinsicGas(txdata, &rules) txdata.Gas = gas 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 1531f01586..625efd2217 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) } @@ -133,13 +133,6 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg // and uses the input parameters for its environment similar to ApplyTransaction. However, // this method takes an already created EVM instance as input. func ApplyTransactionWithEVM(msg *Message, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (receipt *types.Receipt, err error) { - rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time) - // Intrinsic gas - gas, err := tx.IntrinsicGas(&rules) - if err != nil { - return nil, err - } - msg.Gas = gas if hooks := evm.Config.Tracer; hooks != nil { if hooks.OnTxStart != nil { hooks.OnTxStart(evm.GetVMContext(), tx, msg.From) @@ -208,7 +201,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 22c77c5816..ed6d5d2e0a 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -110,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(), @@ -134,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. + gas, err := tx.IntrinsicGas(rules) + if err != nil { + return nil, err + } + msg.Gas = gas + + // Recover sender. msg.From, err = types.Sender(s, tx) return msg, err } diff --git a/core/types/transaction.go b/core/types/transaction.go index f489f3cce3..645cfedd60 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -583,11 +583,20 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e // IntrinsicGas returns the 'intrinsic gas' computed for a message with the given data. func (tx *Transaction) IntrinsicGas(rules *params.Rules) (uint64, error) { - return IntrinsicGas(tx.inner, rules) + return calcIntrinsicGas(tx.inner, rules) } // IntrinsicGas computes the 'intrinsic gas' for a message with the given data. -func IntrinsicGas(txdata TxData, rules *params.Rules) (uint64, error) { +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 { diff --git a/core/vm/evm.go b/core/vm/evm.go index c28dcb2554..74609ed339 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -591,6 +591,11 @@ 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 } +func (evm *EVM) Rules() *params.Rules { + rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Difficulty.BitLen() == 0, evm.Context.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 627dd487fc..206c582184 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -269,10 +269,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(), task.block.Difficulty().BitLen() == 0, 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(), @@ -540,14 +541,15 @@ func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } - if chainConfig.IsPrague(block.Number(), block.Time()) { + rules := evm.Rules() + 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) @@ -604,7 +606,8 @@ func (api *API) traceBlock(ctx context.Context, block *types.Block, config *Trac if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } - if api.backend.ChainConfig().IsPrague(block.Number(), block.Time()) { + rules := evm.Rules() + if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } @@ -625,7 +628,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(), @@ -652,6 +655,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().BitLen() == 0, block.Time()) ) threads := runtime.NumCPU() if threads > len(txs) { @@ -664,7 +668,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(), @@ -703,7 +707,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 @@ -781,13 +785,14 @@ func (api *API) standardTraceBlockToFile(ctx context.Context, block *types.Block if beaconRoot := block.BeaconRoot(); beaconRoot != nil { core.ProcessBeaconBlockRoot(*beaconRoot, evm) } - if chainConfig.IsPrague(block.Number(), block.Time()) { + rules := evm.Rules() + 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 @@ -883,7 +888,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(), block.Difficulty().BitLen() == 0, 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 } 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/tests/state_test_util.go b/tests/state_test_util.go index a22e470ad8..aa84852797 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, + Gas: gas, GasLimit: gasLimit, GasPrice: gasPrice, GasFeeCap: tx.MaxFeePerGas, From c0ca97f96af7484ed9d3d4c77b1e66899d10a946 Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 12 Mar 2025 14:35:51 -0600 Subject: [PATCH 07/10] all: rename Gas to IntrinsicGas in message, fixup uses of Message --- cmd/evm/internal/t8ntool/execution.go | 6 +++--- core/state_transition.go | 6 +++--- core/vm/evm.go | 2 +- eth/tracers/api.go | 3 ++- eth/tracers/api_test.go | 3 ++- internal/ethapi/api.go | 14 ++++++++------ internal/ethapi/simulate.go | 5 +++-- internal/ethapi/transaction_args.go | 16 +++++++++++++++- tests/state_test.go | 2 +- tests/state_test_util.go | 2 +- 10 files changed, 39 insertions(+), 20 deletions(-) diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go index 7917fbc040..295f207647 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,8 +234,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig, rejectedTxs = append(rejectedTxs, &rejectedTx{i, errMsg}) continue } - rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Difficulty.BitLen() == 0, evm.Context.Time) - msg, err := core.TransactionToMessage(tx, signer, pre.Env.BaseFee, &rules) + 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/core/state_transition.go b/core/state_transition.go index ed6d5d2e0a..b724af4527 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -93,7 +93,7 @@ type Message struct { GasPrice *big.Int GasFeeCap *big.Int GasTipCap *big.Int - Gas uint64 + IntrinsicGas uint64 Data []byte AccessList types.AccessList BlobGasFeeCap *big.Int @@ -139,7 +139,7 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In if err != nil { return nil, err } - msg.Gas = gas + msg.IntrinsicGas = gas // Recover sender. msg.From, err = types.Sender(s, tx) @@ -378,7 +378,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) { floorDataGas uint64 ) - gas := msg.Gas + gas := msg.IntrinsicGas // Check clauses 4-5, subtract intrinsic gas if everything is correct if st.gasRemaining < gas { return nil, fmt.Errorf("%w: have %d, want %d", ErrIntrinsicGas, st.gasRemaining, gas) diff --git a/core/vm/evm.go b/core/vm/evm.go index 74609ed339..73a9b770f8 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -592,7 +592,7 @@ func (evm *EVM) resolveCodeHash(addr common.Address) common.Hash { func (evm *EVM) ChainConfig() *params.ChainConfig { return evm.chainConfig } func (evm *EVM) Rules() *params.Rules { - rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Difficulty.BitLen() == 0, evm.Context.Time) + rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time) return &rules } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 206c582184..40a5d592d8 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -969,7 +969,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(), block.Difficulty().BitLen() == 0, 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 529448e397..4ca40042ba 100644 --- a/eth/tracers/api_test.go +++ b/eth/tracers/api_test.go @@ -174,11 +174,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/internal/ethapi/api.go b/internal/ethapi/api.go index f3975d35a0..e2e830bcfb 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -693,7 +693,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, header.Difficulty.BitLen() == 0, 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 { @@ -837,7 +838,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.BitLen() == 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) @@ -1180,15 +1182,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, args.from(), to, precompiles) 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 097de8b0b0..e6d3441074 100644 --- a/internal/ethapi/simulate.go +++ b/internal/ethapi/simulate.go @@ -201,12 +201,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) } var allLogs []*types.Log @@ -225,7 +226,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 aa84852797..95455a5cad 100644 --- a/tests/state_test_util.go +++ b/tests/state_test_util.go @@ -481,7 +481,7 @@ func (tx *stTransaction) toMessage(ps stPostState, baseFee *big.Int, rules *para To: to, Nonce: tx.Nonce, Value: value, - Gas: gas, + IntrinsicGas: gas, GasLimit: gasLimit, GasPrice: gasPrice, GasFeeCap: tx.MaxFeePerGas, From 93de27d9d4231d5a984629766d480eebf2044664 Mon Sep 17 00:00:00 2001 From: lightclient Date: Wed, 12 Mar 2025 14:59:42 -0600 Subject: [PATCH 08/10] core: few final cleanups --- core/bench_test.go | 4 +--- core/state_transition.go | 11 +++++------ core/types/transaction.go | 4 ++-- core/vm/evm.go | 4 +++- eth/tracers/api.go | 18 +++++++++++------- 5 files changed, 22 insertions(+), 19 deletions(-) diff --git a/core/bench_test.go b/core/bench_test.go index 1b8b47545c..5bff3299ab 100644 --- a/core/bench_test.go +++ b/core/bench_test.go @@ -100,12 +100,10 @@ func genValueTx(nbytes int) func(int, *BlockGen) { Nonce: gen.TxNonce(benchRootAddr), To: &toaddr, Value: big.NewInt(1), - Gas: params.TxGas, Data: data, GasPrice: gasPrice, } - gas := types.IntrinsicGas(txdata, &rules) - txdata.Gas = gas + txdata.Gas = types.IntrinsicGas(txdata, &rules) tx, _ := types.SignNewTx(benchRootKey, signer, txdata) gen.AddTx(tx) } diff --git a/core/state_transition.go b/core/state_transition.go index b724af4527..c325df8fa2 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -376,16 +376,15 @@ 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 ) - gas := msg.IntrinsicGas // Check clauses 4-5, subtract intrinsic gas if everything is correct - 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 { - var err error floorDataGas, err = FloorDataGas(msg.Data) if err != nil { return nil, err @@ -395,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/types/transaction.go b/core/types/transaction.go index 645cfedd60..9d196e3ab6 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -581,12 +581,12 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e return &Transaction{inner: cpy, time: tx.time}, nil } -// IntrinsicGas returns the 'intrinsic gas' computed for a message with the given data. +// 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 computes the 'intrinsic gas' for a message with the given data. +// 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 { diff --git a/core/vm/evm.go b/core/vm/evm.go index 73a9b770f8..89d81e1bb7 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -591,8 +591,10 @@ 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 { - rules := evm.ChainConfig().Rules(evm.Context.BlockNumber, evm.Context.Random != nil, evm.Context.Time) + bctx := evm.Context + rules := evm.ChainConfig().Rules(bctx.BlockNumber, bctx.Random != nil, bctx.Time) return &rules } diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 40a5d592d8..783aac1023 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -536,12 +536,12 @@ 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) } - rules := evm.Rules() if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } @@ -601,12 +601,14 @@ 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) } - rules := evm.Rules() if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } @@ -781,11 +783,13 @@ 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) } - rules := evm.Rules() if rules.IsPrague { core.ProcessParentBlockHash(block.ParentHash(), evm) } From 0f2807038396b2e2f9aa4a88e77bb970efc97bb7 Mon Sep 17 00:00:00 2001 From: lightclient Date: Thu, 13 Mar 2025 09:24:13 -0600 Subject: [PATCH 09/10] all: review feedback from marius --- cmd/evm/internal/t8ntool/transition.go | 2 +- core/state_transition.go | 4 ++-- eth/tracers/api.go | 8 ++++---- internal/ethapi/api.go | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) 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/state_transition.go b/core/state_transition.go index c325df8fa2..0829e6ff71 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -135,11 +135,11 @@ func TransactionToMessage(tx *types.Transaction, s types.Signer, baseFee *big.In } } // Fill in intrinsic gas. - gas, err := tx.IntrinsicGas(rules) + intrinsicGas, err := tx.IntrinsicGas(rules) if err != nil { return nil, err } - msg.IntrinsicGas = gas + msg.IntrinsicGas = intrinsicGas // Recover sender. msg.From, err = types.Sender(s, tx) diff --git a/eth/tracers/api.go b/eth/tracers/api.go index 783aac1023..f8b7e70d85 100644 --- a/eth/tracers/api.go +++ b/eth/tracers/api.go @@ -269,7 +269,7 @@ 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(), task.block.Difficulty().BitLen() == 0, task.block.Time()) + 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() { @@ -657,7 +657,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().BitLen() == 0, block.Time()) + rules = api.backend.ChainConfig().Rules(block.Number(), block.Difficulty().Sign() == 0, block.Time()) ) threads := runtime.NumCPU() if threads > len(txs) { @@ -892,7 +892,7 @@ func (api *API) TraceTransaction(ctx context.Context, hash common.Hash, config * return nil, err } defer release() - rules := api.backend.ChainConfig().Rules(block.Number(), block.Difficulty().BitLen() == 0, block.Time()) + 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 @@ -973,7 +973,7 @@ func (api *API) TraceCall(ctx context.Context, args ethapi.TransactionArgs, bloc return nil, err } var ( - rules = api.backend.ChainConfig().Rules(block.Number(), block.Difficulty().BitLen() == 0, block.Time()) + 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/internal/ethapi/api.go b/internal/ethapi/api.go index e2e830bcfb..b990aa03fd 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -693,7 +693,7 @@ 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 } - rules := b.ChainConfig().Rules(header.Number, header.Difficulty.BitLen() == 0, header.Time) + 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). @@ -838,7 +838,7 @@ 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 } - rules := b.ChainConfig().Rules(header.Number, header.Difficulty.BitLen() == 0, header.Time) + 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 From 81d5d79109b3a814bc29fc615eb34000e739d22d Mon Sep 17 00:00:00 2001 From: lightclient Date: Sat, 22 Mar 2025 08:37:08 -0500 Subject: [PATCH 10/10] tests: remove getRules --- tests/transaction_test_util.go | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/tests/transaction_test_util.go b/tests/transaction_test_util.go index 116707d8ac..ec214bb1d5 100644 --- a/tests/transaction_test_util.go +++ b/tests/transaction_test_util.go @@ -153,35 +153,3 @@ func (tt *TransactionTest) Run() error { } return nil } - -func getRules(config *params.ChainConfig, fork string) (params.Rules, error) { - switch fork { - case "Frontier": - return config.Rules(new(big.Int), false, 0), nil - case "Homestead": - return config.Rules(config.HomesteadBlock, false, 0), nil - case "EIP150": - return config.Rules(config.EIP150Block, false, 0), nil - case "EIP158": - return config.Rules(config.EIP158Block, false, 0), nil - case "Byzantium": - return config.Rules(config.ByzantiumBlock, false, 0), nil - case "Constantinople": - return config.Rules(config.ConstantinopleBlock, false, 0), nil - case "Istanbul": - return config.Rules(config.IstanbulBlock, false, 0), nil - case "Berlin": - return config.Rules(config.BerlinBlock, false, 0), nil - case "London": - return config.Rules(config.LondonBlock, false, 0), nil - case "Paris": - return config.Rules(config.LondonBlock, true, 0), nil - case "Shanghai": - return config.Rules(config.LondonBlock, true, *config.ShanghaiTime), nil - case "Cancun": - return config.Rules(config.LondonBlock, true, *config.CancunTime), nil - case "Prague": - return config.Rules(config.LondonBlock, true, *config.PragueTime), nil - } - return params.Rules{}, UnsupportedForkError{Name: fork} -}