From 6d6172fb575c3d91d7f31cef69cded7dc6a8c6c2 Mon Sep 17 00:00:00 2001 From: piersy Date: Fri, 2 Aug 2024 12:30:24 +0100 Subject: [PATCH] Re-instate correct eip1559 gas price calcualtions (#197) * Re-instate correct eip1559 gas price calcualtions In this commit dfd1d38015d5ce7a174c84d4a4092308f668571a Ponti mistakenly removed gas price calculation for eip1559 style transactions retreived via the RPC. This resulted in gasFeeCap being returned for all transactions except depost transactions. So this commit fixes that but also adds support for calculating gas price for our migrated transactions as well. Prior to the gingerbread hardfork celo stored the base fee in state as opposed to on the block, this PR does not add code to be able to retrieve the base fee from state, so for all pre-gingerbread eip1559 style transactions we have no way to calculate the gas price and therefore will return null. Post gingerbread celo added the base fee to the block, this enables us to calculate the correct gas price for transactions using the native currency. For transactions using alternative fee currencies we still cannot calculate the correct gas price since we would need to look up the base fee and exchange rate from the state and we havend added code to do this. Post cel2 for celo eip1559 style transactions the baseFee in fee currency is stored on the receipt which means that we can calcualte the correct base fee for all transactions. Co-authored-by: Paul Lange --- core/types/receipt.go | 16 +- internal/ethapi/api.go | 39 ++- internal/ethapi/celo_api_test.go | 538 +++++++++++++++++++++++++------ params/config.go | 2 + 4 files changed, 490 insertions(+), 105 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index 5344814249..6c46d55265 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -610,11 +610,17 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu rs[i].Type = txs[i].Type() rs[i].TxHash = txs[i].Hash() - // The post transition CeloDynamicFeeV2Txs set the baseFee in the receipt - if rs[i].BaseFee == nil { - rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee) - } else { - rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), rs[i].BaseFee) + // Pre-gingerbred the base fee was stored in state, but we don't try to recover it here, since A) we don't have + // access to the objects required to get the state and B) retrieving the base fee is quite code heavy and we + // don't want to bring that code across from the celo L1 to op-geth. In the celo L1 we would return a nil base + // fee if the state was not available, so that is what we do here. + if config.IsGingerbread(new(big.Int).SetUint64(number)) { + // The post transition CeloDynamicFeeV2Txs set the baseFee in the receipt + if rs[i].BaseFee == nil { + rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee) + } else { + rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), rs[i].BaseFee) + } } // EIP-4844 blob transaction fields diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index b297f2d6d9..d37bc5d3fa 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1589,7 +1589,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.ChainID = (*hexutil.Big)(tx.ChainId()) result.YParity = &yparity - case types.DynamicFeeTxType, types.CeloDynamicFeeTxV2Type, types.CeloDenominatedTxType: + case types.DynamicFeeTxType, types.CeloDynamicFeeTxType, types.CeloDynamicFeeTxV2Type, types.CeloDenominatedTxType: al := tx.AccessList() yparity := hexutil.Uint64(v.Sign()) result.Accesses = &al @@ -1597,13 +1597,34 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber result.YParity = &yparity result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap()) result.GasTipCap = (*hexutil.Big)(tx.GasTipCap()) - // if the transaction has been mined, compute the effective gas price - if receipt != nil { + + // Note that celo denominated txs always have the gas price denominated in celo (the native currency) + isNativeFeeCurrency := tx.FeeCurrency() == nil || tx.Type() == types.CeloDenominatedTxType + isGingerbread := config.IsGingerbread(new(big.Int).SetUint64(blockNumber)) + isCel2 := config.IsCel2(blockTime) + + if blockHash == (common.Hash{}) { + // This is a pending transaction, for pending transactions we set the gas price to gas fee cap. + result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + } else if isGingerbread && isNativeFeeCurrency { + // Post gingerbread mined transaction with a native fee currency, we can compute the effective gas price. + result.GasPrice = (*hexutil.Big)(effectiveGasPrice(tx, baseFee)) + } else if isCel2 && tx.Type() == types.CeloDynamicFeeTxV2Type { + // Mined post Cel2 celoDynamicFeeTxV2 transaction, we can get the gas price from the receipt + // Assert that we should have a receipt + if receipt == nil { + panic(fmt.Sprintf("no corresponding receipt provided for celoDynamicFeeTxV2 transaction %s", tx.Hash().Hex())) + } result.GasPrice = (*hexutil.Big)(receipt.EffectiveGasPrice) } else { - result.GasPrice = (*hexutil.Big)(tx.GasFeeCap()) + // Otherwise this is either a: + // - pre-gingerbread transaction + // - post-gingerbread native fee currency transaction but no base fee was provided + // - post-gingerbread pre-cel2 transaction with a non-native fee currency + // + // In these cases we can't calculate the gas price. + result.GasPrice = nil } - case types.BlobTxType: al := tx.AccessList() yparity := hexutil.Uint64(v.Sign()) @@ -1658,12 +1679,12 @@ func newRPCTransactionFromBlockIndex(ctx context.Context, b *types.Block, index return nil } tx := txs[index] - rcpt := depositTxReceipt(ctx, b.Hash(), index, backend, tx) + rcpt := txReceipt(ctx, b.Hash(), index, backend, tx) return newRPCTransaction(tx, b.Hash(), b.NumberU64(), b.Time(), index, b.BaseFee(), config, rcpt) } -func depositTxReceipt(ctx context.Context, blockHash common.Hash, index uint64, backend Backend, tx *types.Transaction) *types.Receipt { - if tx.Type() != types.DepositTxType { +func txReceipt(ctx context.Context, blockHash common.Hash, index uint64, backend Backend, tx *types.Transaction) *types.Receipt { + if tx.Type() != types.DepositTxType && tx.Type() != types.CeloDynamicFeeTxV2Type { return nil } receipts, err := backend.GetReceipts(ctx, blockHash) @@ -1917,7 +1938,7 @@ func (api *TransactionAPI) GetTransactionByHash(ctx context.Context, hash common if err != nil { return nil, err } - rcpt := depositTxReceipt(ctx, blockHash, index, api.b, tx) + rcpt := txReceipt(ctx, blockHash, index, api.b, tx) return newRPCTransaction(tx, blockHash, blockNumber, header.Time, index, header.BaseFee, api.b.ChainConfig(), rcpt), nil } diff --git a/internal/ethapi/celo_api_test.go b/internal/ethapi/celo_api_test.go index 92e14f3687..7cf030547e 100644 --- a/internal/ethapi/celo_api_test.go +++ b/internal/ethapi/celo_api_test.go @@ -28,29 +28,14 @@ var ( transactionIndex uint64 = 15 blockhash = common.HexToHash("0x6ba4a8c1bfe2619eb498e5296e81b1c393b13cba0198ed63dea0ee3aa619b073") blockNumber uint64 = 100 + blockTime uint64 = 100 ) func TestNewRPCTransactionLegacy(t *testing.T) { - // Enable all block based forks so we get the most recent upstream signer. + config := allEnabledChainConfig() + // Set cel2 time to 2000 so that we don't activate the cel2 fork. var cel2Time uint64 = 2000 - config := ¶ms.ChainConfig{ - ChainID: big.NewInt(44787), - EIP150Block: big.NewInt(0), - EIP155Block: big.NewInt(0), - EIP158Block: big.NewInt(0), - ByzantiumBlock: big.NewInt(0), - ConstantinopleBlock: big.NewInt(0), - PetersburgBlock: big.NewInt(0), - IstanbulBlock: big.NewInt(0), - MuirGlacierBlock: big.NewInt(0), - BerlinBlock: big.NewInt(0), - LondonBlock: big.NewInt(0), - Cel2Time: &cel2Time, - } - // Block time is set to before Cel2Time so we don't activate the cel2 fork. - // This gives us the celo legacy signer (legacy transactions are deprecated - // after cel2). - blockTime := uint64(1000) + config.Cel2Time = &cel2Time s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) key, err := crypto.GenerateKey() @@ -75,31 +60,8 @@ func TestNewRPCTransactionLegacy(t *testing.T) { signed, err := types.SignTx(tx, s, key) require.NoError(t, err) - rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) - - // check newRPCTransaction has the expected fields - // Ethereum fields - checkRPCTransactionFields( - t, - rpcTx, - to, - value, - gasLimit, - gasPrice, - nonce, - config.ChainID, - signed.Hash(), - blockhash, - blockNumber, - transactionIndex, - ) - // Celo fields - assert.Equal(t, feeCurrency, *rpcTx.FeeCurrency) - assert.Equal(t, (*hexutil.Big)(gatewayFee), rpcTx.GatewayFee) - assert.Equal(t, gatewayFeeRecipient, *rpcTx.GatewayFeeRecipient) - assert.Equal(t, false, *rpcTx.EthCompatible) - assert.Nil(t, rpcTx.MaxFeeInFeeCurrency) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, nil) }) t.Run("WithoutCeloFields", func(t *testing.T) { @@ -115,63 +77,457 @@ func TestNewRPCTransactionLegacy(t *testing.T) { signed, err := types.SignTx(tx, s, key) require.NoError(t, err) rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, nil) + }) +} - // check newRPCTransaction has the expected fields - // Ethereum fields - checkRPCTransactionFields( - t, - rpcTx, - to, - value, - gasLimit, - gasPrice, - nonce, - config.ChainID, - signed.Hash(), - blockhash, - blockNumber, - transactionIndex, - ) - // Celo fields - assert.Nil(t, rpcTx.FeeCurrency) - assert.Nil(t, rpcTx.GatewayFee) - assert.Nil(t, rpcTx.GatewayFeeRecipient) - assert.Nil(t, rpcTx.EthCompatible) - assert.Nil(t, rpcTx.MaxFeeInFeeCurrency) +func TestNewRPCTransactionDynamicFee(t *testing.T) { + key, err := crypto.GenerateKey() + require.NoError(t, err) + feeCap := big.NewInt(1000) + tipCap := big.NewInt(100) + + t.Run("PendingTransactions", func(t *testing.T) { + // For pending transactions we expect the gas price to be the gas fee cap. + gasFeeCap := func(t *testing.T, tx *types.Transaction, rpcTx *RPCTransaction) { + assert.Equal(t, (*hexutil.Big)(feeCap), rpcTx.GasPrice) + } + overrides := map[string]func(*testing.T, *types.Transaction, *RPCTransaction){"gasPrice": gasFeeCap} + config := allEnabledChainConfig() + s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) + + // An empty bockhash signals pending transactions (I.E no mined block) + blockhash := common.Hash{} + t.Run("DynamicFeeTx", func(t *testing.T) { + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + + t.Run("CeloDynamicFeeTxV2", func(t *testing.T) { + tx := types.NewTx(&types.CeloDynamicFeeTxV2{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + FeeCurrency: &feeCurrency, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + }) + + t.Run("PreGingerbreadMinedDynamicTxs", func(t *testing.T) { + nilGasPrice := func(t *testing.T, tx *types.Transaction, rpcTx *RPCTransaction) { + assert.Nil(t, rpcTx.GasPrice) + } + overrides := map[string]func(*testing.T, *types.Transaction, *RPCTransaction){"gasPrice": nilGasPrice} + // For a pre gingerbread mined dynamic txs we expect the gas price to be unset, because without the state we + // cannot retrieve the base fee, and we currently have no implementation in op-geth to handle retrieving the + // base fee from state. + config := allEnabledChainConfig() + config.GingerbreadBlock = big.NewInt(200) // Setup config so that gingerbread is not active. + cel2Time := uint64(1000) + config.Cel2Time = &cel2Time // also deactivate cel2 + s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) + + t.Run("DynamicFeeTx", func(t *testing.T) { + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + + t.Run("CeloDynamicFeeTx", func(t *testing.T) { + tx := types.NewTx(&types.CeloDynamicFeeTx{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + GatewayFee: gatewayFee, + GatewayFeeRecipient: &gatewayFeeRecipient, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + }) + + t.Run("PostGingerbreadMinedDynamicTxsWithNativeFeeCurrency", func(t *testing.T) { + // For a post gingerbread mined dynamic tx with a native fee currency we expect the gas price to be the + // effective gas price calculated with the base fee available on the block. + effectiveGasPrice := func(t *testing.T, tx *types.Transaction, rpcTx *RPCTransaction) { + assert.Equal(t, (*hexutil.Big)(effectiveGasPrice(tx, baseFee)), rpcTx.GasPrice) + } + overrides := map[string]func(*testing.T, *types.Transaction, *RPCTransaction){"gasPrice": effectiveGasPrice} + + config := allEnabledChainConfig() + s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) + + t.Run("DynamicFeeTx", func(t *testing.T) { + tx := types.NewTx(&types.DynamicFeeTx{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + + t.Run("CeloDynamicFeeTx", func(t *testing.T) { + // CeloDynamicFeeTxs are deprecated after cel2 so we need to ensure cel2time is not activated + config := allEnabledChainConfig() + cel2Time := uint64(1000) + config.Cel2Time = &cel2Time + s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) + + tx := types.NewTx(&types.CeloDynamicFeeTx{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + GatewayFee: gatewayFee, + GatewayFeeRecipient: &gatewayFeeRecipient, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + + t.Run("CeloDynamicFeeTxV2", func(t *testing.T) { + tx := types.NewTx(&types.CeloDynamicFeeTxV2{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + + // TODO unskip this when cip 66 txs are enabled currently they are not supporeted in the celo signer. + t.Run("CeloDenominatedTx", func(t *testing.T) { + t.Skip("CeloDenominatedTx is currently not supported in the celo signer") + tx := types.NewTx(&types.CeloDenominatedTx{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + FeeCurrency: &feeCurrency, + MaxFeeInFeeCurrency: big.NewInt(100000), + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + }) + + t.Run("PostGingerbreadPreCel2MinedDynamicTxsWithNonNativeFeeCurrency", func(t *testing.T) { + // For a post gingerbread mined dynamic txs with a non native fee currency we expect the gas price to be unset, + // because without the state we cannot retrieve the base fee, and we currently have no implementation in op-geth + // to handle retrieving the base fee from state. + + nilGasPrice := func(t *testing.T, tx *types.Transaction, rpcTx *RPCTransaction) { + assert.Nil(t, rpcTx.GasPrice) + } + overrides := map[string]func(*testing.T, *types.Transaction, *RPCTransaction){"gasPrice": nilGasPrice} + + config := allEnabledChainConfig() + cel2Time := uint64(1000) + config.Cel2Time = &cel2Time // Deactivate cel2 + s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) + + t.Run("CeloDynamicFeeTx", func(t *testing.T) { + // CeloDynamicFeeTxs are deprecated after cel2 so we need to ensure cel2time is not activated + config := allEnabledChainConfig() + cel2Time := uint64(1000) + config.Cel2Time = &cel2Time + s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) + + tx := types.NewTx(&types.CeloDynamicFeeTx{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + FeeCurrency: &feeCurrency, + GatewayFee: gatewayFee, + GatewayFeeRecipient: &gatewayFeeRecipient, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + + t.Run("CeloDynamicFeeTxV2", func(t *testing.T) { + tx := types.NewTx(&types.CeloDynamicFeeTxV2{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + FeeCurrency: &feeCurrency, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, nil) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) + }) + + t.Run("PostCel2MinedDynamicTxs", func(t *testing.T) { + receipt := &types.Receipt{} + receipt.EffectiveGasPrice = big.NewInt(1234) + effectiveGasPrice := func(t *testing.T, tx *types.Transaction, rpcTx *RPCTransaction) { + assert.Equal(t, (*hexutil.Big)(receipt.EffectiveGasPrice), rpcTx.GasPrice) + } + overrides := map[string]func(*testing.T, *types.Transaction, *RPCTransaction){"gasPrice": effectiveGasPrice} + + config := allEnabledChainConfig() + s := types.MakeSigner(config, new(big.Int).SetUint64(blockNumber), blockTime) + + t.Run("CeloDynamicFeeTxV2", func(t *testing.T) { + // For a pre gingerbread mined dynamic fee tx we expect the gas price to be unset. + tx := types.NewTx(&types.CeloDynamicFeeTxV2{ + ChainID: config.ChainID, + Nonce: nonce, + Gas: gasLimit, + GasFeeCap: feeCap, + GasTipCap: tipCap, + FeeCurrency: &feeCurrency, + + To: &to, + Value: value, + Data: []byte{}, + }) + + signed, err := types.SignTx(tx, s, key) + require.NoError(t, err) + + rpcTx := newRPCTransaction(signed, blockhash, blockNumber, blockTime, transactionIndex, baseFee, config, receipt) + checkTxFields(t, signed, rpcTx, s, blockhash, blockNumber, transactionIndex, overrides) + }) }) } -func checkRPCTransactionFields( +func allEnabledChainConfig() *params.ChainConfig { + zeroTime := uint64(0) + return ¶ms.ChainConfig{ + ChainID: big.NewInt(44787), + HomesteadBlock: big.NewInt(0), + EIP150Block: big.NewInt(0), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: big.NewInt(0), + MuirGlacierBlock: big.NewInt(0), + BerlinBlock: big.NewInt(0), + LondonBlock: big.NewInt(0), + ArrowGlacierBlock: big.NewInt(0), + GrayGlacierBlock: big.NewInt(0), + ShanghaiTime: &zeroTime, + CancunTime: &zeroTime, + RegolithTime: &zeroTime, + CanyonTime: &zeroTime, + EcotoneTime: &zeroTime, + FjordTime: &zeroTime, + Cel2Time: &zeroTime, + GingerbreadBlock: big.NewInt(0), + } +} + +// checkTxFields for the most part checks that the fields of the rpcTx match those of the provided tx, it allows for +// overriding some checks by providing a map of fieldName -> overrideFunc. +func checkTxFields( t *testing.T, + tx *types.Transaction, rpcTx *RPCTransaction, - to common.Address, - value *big.Int, - gasLimit uint64, - gasPrice *big.Int, - nonce uint64, - chainID *big.Int, - hash common.Hash, + signer types.Signer, blockhash common.Hash, blockNumber uint64, transactionIndex uint64, + overrides map[string]func(*testing.T, *types.Transaction, *RPCTransaction), ) { - assert.Equal(t, to, *rpcTx.To) - assert.Equal(t, (*hexutil.Big)(value), rpcTx.Value) - assert.Equal(t, hexutil.Bytes{}, rpcTx.Input) - assert.Equal(t, hexutil.Uint64(gasLimit), rpcTx.Gas) - assert.Equal(t, (*hexutil.Big)(gasPrice), rpcTx.GasPrice) - assert.Equal(t, hash, rpcTx.Hash) - assert.Equal(t, hexutil.Uint64(nonce), rpcTx.Nonce) - assert.Equal(t, (*hexutil.Big)(chainID), rpcTx.ChainID) - assert.Equal(t, hexutil.Uint64(types.LegacyTxType), rpcTx.Type) - assert.Nil(t, rpcTx.Accesses) - assert.Nil(t, rpcTx.GasFeeCap) - assert.Nil(t, rpcTx.GasTipCap) - assert.Nil(t, rpcTx.MaxFeePerBlobGas) - assert.Equal(t, []common.Hash(nil), rpcTx.BlobVersionedHashes) - // Added fields (not part of the transaction type) - assert.Equal(t, &blockhash, rpcTx.BlockHash) - assert.Equal(t, (*hexutil.Big)(big.NewInt(int64(blockNumber))), rpcTx.BlockNumber) - assert.Equal(t, hexutil.Uint64(transactionIndex), *rpcTx.TransactionIndex) + // + // If blockhash is empty it signifies a pending tx and for pending txs the block hash, block number and tx index are + // not set on the rpcTx. on the result. + if blockhash == (common.Hash{}) { + assert.Nil(t, rpcTx.BlockHash) + assert.Nil(t, rpcTx.BlockNumber) + assert.Nil(t, rpcTx.TransactionIndex) + } else { + assert.Equal(t, &blockhash, rpcTx.BlockHash) + assert.Equal(t, (*hexutil.Big)(big.NewInt(int64(blockNumber))), rpcTx.BlockNumber) + assert.Equal(t, hexutil.Uint64(transactionIndex), *rpcTx.TransactionIndex) + } + + from, err := types.Sender(signer, tx) + require.NoError(t, err) + + assert.Equal(t, from, rpcTx.From) + assert.Equal(t, hexutil.Uint64(tx.Gas()), rpcTx.Gas) + assert.Equal(t, tx.To(), rpcTx.To) + override, ok := overrides["gasPrice"] + if ok { + override(t, tx, rpcTx) + } else { + assert.Equal(t, (*hexutil.Big)(tx.GasPrice()), rpcTx.GasPrice) + } + switch tx.Type() { + case types.DynamicFeeTxType, types.CeloDynamicFeeTxType, types.CeloDynamicFeeTxV2Type, types.CeloDenominatedTxType: + assert.Equal(t, (*hexutil.Big)(tx.GasFeeCap()), rpcTx.GasFeeCap) + assert.Equal(t, (*hexutil.Big)(tx.GasTipCap()), rpcTx.GasTipCap) + default: + assert.Nil(t, rpcTx.GasFeeCap) + assert.Nil(t, rpcTx.GasTipCap) + } + assert.Equal(t, (*hexutil.Big)(tx.BlobGasFeeCap()), rpcTx.MaxFeePerBlobGas) + assert.Equal(t, tx.Hash(), rpcTx.Hash) + assert.Equal(t, (hexutil.Bytes)(tx.Data()), rpcTx.Input) + assert.Equal(t, hexutil.Uint64(tx.Nonce()), rpcTx.Nonce) + assert.Equal(t, tx.To(), rpcTx.To) + assert.Equal(t, (*hexutil.Big)(tx.Value()), rpcTx.Value) + assert.Equal(t, hexutil.Uint64(tx.Type()), rpcTx.Type) + switch tx.Type() { + case types.AccessListTxType, types.DynamicFeeTxType, types.CeloDynamicFeeTxType, types.CeloDynamicFeeTxV2Type, types.CeloDenominatedTxType, types.BlobTxType: + assert.Equal(t, tx.AccessList(), *rpcTx.Accesses) + default: + assert.Nil(t, rpcTx.Accesses) + } + + assert.Equal(t, (*hexutil.Big)(tx.ChainId()), rpcTx.ChainID) + assert.Equal(t, tx.BlobHashes(), rpcTx.BlobVersionedHashes) + + v, r, s := tx.RawSignatureValues() + assert.Equal(t, (*hexutil.Big)(v), rpcTx.V) + assert.Equal(t, (*hexutil.Big)(r), rpcTx.R) + assert.Equal(t, (*hexutil.Big)(s), rpcTx.S) + + switch tx.Type() { + case types.AccessListTxType, types.DynamicFeeTxType, types.CeloDynamicFeeTxType, types.CeloDynamicFeeTxV2Type, types.CeloDenominatedTxType, types.BlobTxType: + yparity := (hexutil.Uint64)(v.Sign()) + assert.Equal(t, &yparity, rpcTx.YParity) + default: + assert.Nil(t, rpcTx.YParity) + } + + // optimism fields + switch tx.Type() { + case types.DepositTxType: + assert.Equal(t, tx.SourceHash(), rpcTx.SourceHash) + assert.Equal(t, tx.Mint(), rpcTx.Mint) + assert.Equal(t, tx.IsSystemTx(), rpcTx.IsSystemTx) + default: + assert.Nil(t, rpcTx.SourceHash) + assert.Nil(t, rpcTx.Mint) + assert.Nil(t, rpcTx.IsSystemTx) + } + + assert.Nil(t, rpcTx.DepositReceiptVersion) + + // celo fields + assert.Equal(t, tx.FeeCurrency(), rpcTx.FeeCurrency) + assert.Equal(t, (*hexutil.Big)(tx.MaxFeeInFeeCurrency()), rpcTx.MaxFeeInFeeCurrency) + if tx.Type() == types.LegacyTxType && tx.IsCeloLegacy() { + assert.Equal(t, false, *rpcTx.EthCompatible) + } else { + assert.Nil(t, rpcTx.EthCompatible) + } + assert.Equal(t, (*hexutil.Big)(tx.GatewayFee()), rpcTx.GatewayFee) + assert.Equal(t, tx.GatewayFeeRecipient(), rpcTx.GatewayFeeRecipient) } diff --git a/params/config.go b/params/config.go index bef0b6b0f3..094147eb7b 100644 --- a/params/config.go +++ b/params/config.go @@ -270,6 +270,7 @@ var ( CancunTime: nil, PragueTime: nil, VerkleTime: nil, + GingerbreadBlock: big.NewInt(0), Cel2Time: newUint64(0), TerminalTotalDifficulty: nil, TerminalTotalDifficultyPassed: false, @@ -328,6 +329,7 @@ var ( ArrowGlacierBlock: big.NewInt(0), GrayGlacierBlock: big.NewInt(0), MergeNetsplitBlock: big.NewInt(0), + GingerbreadBlock: big.NewInt(0), ShanghaiTime: newUint64(0), CancunTime: newUint64(0), PragueTime: nil,