From ec7a82834b611afbac53d7f07dcf77b0cdcf1986 Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 02:59:10 -0300 Subject: [PATCH 01/11] Add e2e test for celo denominated tx + bug fixes or marshalling --- core/types/celo_denominated_tx.go | 9 ++-- core/types/transaction_signing.go | 5 ++ e2e_test/e2e_test.go | 76 ++++++++++++++--------------- e2e_test/e2e_transfer_test.go | 22 +++++++-- internal/ethapi/api.go | 2 + internal/ethapi/transaction_args.go | 3 +- mycelo/genesis/base_config.go | 8 +-- mycelo/genesis/genesis.go | 9 ++-- test/node.go | 4 +- 9 files changed, 77 insertions(+), 61 deletions(-) diff --git a/core/types/celo_denominated_tx.go b/core/types/celo_denominated_tx.go index 140e7b8b65..66c9541f48 100644 --- a/core/types/celo_denominated_tx.go +++ b/core/types/celo_denominated_tx.go @@ -1,16 +1,11 @@ package types import ( - "errors" "math/big" "github.com/celo-org/celo-blockchain/common" ) -var ( - ErrNonCeloDenominated error = errors.New("Tx not CeloDenominated") -) - type CeloDenominatedTx struct { ChainID *big.Int Nonce uint64 @@ -48,6 +43,9 @@ func (tx *CeloDenominatedTx) copy() TxData { R: new(big.Int), S: new(big.Int), } + if tx.MaxFeeInFeeCurrency != nil { + cpy.MaxFeeInFeeCurrency = new(big.Int).Set(tx.MaxFeeInFeeCurrency) + } copy(cpy.AccessList, tx.AccessList) if tx.Value != nil { cpy.Value.Set(tx.Value) @@ -76,7 +74,6 @@ func (tx *CeloDenominatedTx) copy() TxData { // accessors for innerTx. func (tx *CeloDenominatedTx) txType() byte { return CeloDenominatedTxType } func (tx *CeloDenominatedTx) chainID() *big.Int { return tx.ChainID } -func (tx *CeloDenominatedTx) protected() bool { return true } func (tx *CeloDenominatedTx) accessList() AccessList { return tx.AccessList } func (tx *CeloDenominatedTx) data() []byte { return tx.Data } func (tx *CeloDenominatedTx) gas() uint64 { return tx.Gas } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 30151578cf..ea93d4ed27 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -40,6 +40,8 @@ type sigCache struct { func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { var signer Signer switch { + case config.IsHFork(blockNumber): + signer = NewHForkSigner(config.ChainID) case config.IsGingerbreadP2(blockNumber): signer = NewGingerbreadSigner(config.ChainID) case config.IsEspresso(blockNumber): @@ -63,6 +65,9 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { // have the current block number available, use MakeSigner instead. func LatestSigner(config *params.ChainConfig) Signer { if config.ChainID != nil { + if config.HForkBlock != nil { + return NewHForkSigner(config.ChainID) + } if config.GingerbreadP2Block != nil { return NewGingerbreadSigner(config.ChainID) } diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index fc73486ec6..adceb1fabb 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -44,8 +44,8 @@ func init() { // network to process the transaction. func TestSendCelo(t *testing.T) { ac := test.AccountConfig(3, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -70,8 +70,8 @@ func TestSendCelo(t *testing.T) { // and can be useful for debugging these traces. func TestTraceSendCeloViaGoldToken(t *testing.T) { ac := test.AccountConfig(3, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -107,8 +107,8 @@ func TestTraceSendCeloViaGoldToken(t *testing.T) { // Use the callTracer to trace a native CELO transfer. func TestCallTraceTransactionNativeTransfer(t *testing.T) { ac := test.AccountConfig(1, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -145,8 +145,8 @@ func TestCallTraceTransactionNativeTransfer(t *testing.T) { // Use the prestateTracer to trace a native CELO transfer. func TestPrestateTransactionNativeTransfer(t *testing.T) { ac := test.AccountConfig(1, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -183,8 +183,8 @@ func TestSingleNodeNetworkManyTxs(t *testing.T) { iterations := 5 txsPerIteration := 5 ac := test.AccountConfig(1, 1) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) gc.Istanbul.Epoch = uint64(iterations) * 50 // avoid the epoch for this test network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -209,8 +209,8 @@ func TestSingleNodeNetworkManyTxs(t *testing.T) { // We previously had an open bug for this https://github.com/celo-org/celo-blockchain/issues/1574 func TestEpochBlockMarshaling(t *testing.T) { accounts := test.AccountConfig(1, 0) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(accounts, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(accounts, hforkBlock) require.NoError(t, err) // Configure the shortest possible epoch, uptimeLookbackWindow minimum is 3 @@ -239,8 +239,8 @@ func TestEpochBlockMarshaling(t *testing.T) { // validators are shut down, when they restart the network is able to continue. func TestStartStopValidators(t *testing.T) { ac := test.AccountConfig(4, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, _, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -376,8 +376,8 @@ func TestStartStopValidators(t *testing.T) { // trace block code was the source of the concurrent map access. func TestBlockTracingConcurrentMapAccess(t *testing.T) { ac := test.AccountConfig(1, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -427,8 +427,8 @@ func TestBlockTracingConcurrentMapAccess(t *testing.T) { // Helpful for debugging issues in TestBlockTracingConcurrentMapAccess. func TestBlockTracingSequentialAccess(t *testing.T) { ac := test.AccountConfig(1, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -462,8 +462,8 @@ type rpcCustomTransaction struct { // price by the tx, which could be less than the feeCap (as in this example) func TestRPCDynamicTxGasPriceWithBigFeeCap(t *testing.T) { ac := test.AccountConfig(3, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -503,8 +503,8 @@ func TestRPCDynamicTxGasPriceWithBigFeeCap(t *testing.T) { // GasPriceMinimum contract func TestRPCDynamicTxGasPriceWithState(t *testing.T) { ac := test.AccountConfig(3, 2) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) ec.TxLookupLimit = 0 ec.NoPruning = true @@ -576,13 +576,13 @@ func TestRPCDynamicTxGasPriceWithoutStateForAlternativeCurrencyAfterGingerbread( } func testRPCDynamicTxGasPriceWithoutState(t *testing.T, afterGingerbread, alternativeCurrency bool) { - var gingerbreadBlock *big.Int + var hforkBlock *big.Int if afterGingerbread { - gingerbreadBlock = common.Big0 + hforkBlock = common.Big0 } cusdAddress := common.HexToAddress("0xd008") ac := test.AccountConfig(3, 2) - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, hforkBlock) ec.TrieDirtyCache = 5 require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -655,8 +655,8 @@ func pruneStateOfBlock(ctx context.Context, node *test.Node, blockNumber *big.In func runMochaTest(t *testing.T, add_args func(*env.AccountsConfig, *genesis.Config, test.Network) []string) { ac := test.AccountConfig(1, 1) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -701,8 +701,8 @@ func TestEthersJSCompatibility(t *testing.T) { // returning the 'gasLimit' and 'baseFeePerGas' fields on RPC blocks. func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) { ac := test.AccountConfig(1, 1) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) // Check fields present (compatibility set by default) @@ -749,8 +749,8 @@ func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) { // Gingerbread is relevant because it added the gasLimit to the header. func TestEthersJSCompatibilityDisableBeforeGingerbread(t *testing.T) { ac := test.AccountConfig(1, 1) - var gingerbreadBlock *big.Int = nil - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + var hforkBlock *big.Int = nil + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) // Check fields present (compatibility set by default) @@ -804,10 +804,10 @@ func TestEthCompatibilityFieldsOnGenesisBlock(t *testing.T) { ac := test.AccountConfig(1, 1) // Gingerbread needs to be disabled for this test to be meaningful (since // gingerbread added gasLimt & baseFee fields to the block) - var gingerbreadBlock *big.Int = nil + var hforkBlock *big.Int = nil // Fist we test without eth compatibility to ensure that the setting has an effect. - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err := test.BuildConfig(ac, hforkBlock) ec.RPCEthCompatibility = false require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -826,7 +826,7 @@ func TestEthCompatibilityFieldsOnGenesisBlock(t *testing.T) { // Now we with eth compatility enabled and see that gasLimit and baseFee // are returned on the block. - gc, ec, err = test.BuildConfig(ac, gingerbreadBlock) + gc, ec, err = test.BuildConfig(ac, hforkBlock) ec.RPCEthCompatibility = true require.NoError(t, err) network, shutdown, err = test.NewNetwork(ac, gc, ec) @@ -847,8 +847,8 @@ func TestSettingGingerbreadOnGenesisBlock(t *testing.T) { // Fist we test without gingerbread to ensure that setting the gingerbread // actually has an effect. - var gingerbreadBlock *big.Int = nil - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + var hforkBlock *big.Int = nil + gc, ec, err := test.BuildConfig(ac, hforkBlock) ec.RPCEthCompatibility = false require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) @@ -866,8 +866,8 @@ func TestSettingGingerbreadOnGenesisBlock(t *testing.T) { require.Equal(t, uint64(0), block.GasLimit()) // Now we check that setting the gingerbread block at genesis causes gasLimit and baseFee to be set on the block. - gingerbreadBlock = big.NewInt(0) - gc, ec, err = test.BuildConfig(ac, gingerbreadBlock) + hforkBlock = big.NewInt(0) + gc, ec, err = test.BuildConfig(ac, hforkBlock) ec.RPCEthCompatibility = false require.NoError(t, err) network, shutdown, err = test.NewNetwork(ac, gc, ec) diff --git a/e2e_test/e2e_transfer_test.go b/e2e_test/e2e_transfer_test.go index 4ebbbf929f..e04aa3e233 100644 --- a/e2e_test/e2e_transfer_test.go +++ b/e2e_test/e2e_transfer_test.go @@ -36,8 +36,8 @@ const ( // - validator account has tip fee added. func TestTransferCELO(t *testing.T) { ac := test.AccountConfig(1, 3) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -258,8 +258,8 @@ func prepareTransaction(txArgs ethapi.TransactionArgs, senderKey *ecdsa.PrivateK func TestTransferERC20(t *testing.T) { ac := test.AccountConfig(1, 3) - gingerbreadBlock := common.Big0 - gc, ec, err := test.BuildConfig(ac, gingerbreadBlock) + hforkBlock := common.Big0 + gc, ec, err := test.BuildConfig(ac, hforkBlock) require.NoError(t, err) network, shutdown, err := test.NewNetwork(ac, gc, ec) require.NoError(t, err) @@ -278,6 +278,7 @@ func TestTransferERC20(t *testing.T) { header, err := network[0].WsClient.HeaderByNumber(ctx, nil) require.NoError(t, err) datum := header.BaseFee + maxRate, _ := new(big.Int).SetString("1000000000000000000000000", 10) stableTokenAddress := env.MustProxyAddressFor("StableToken") intrinsicGas := hexutil.Uint64(config.IntrinsicGasForAlternativeFeeCurrency + 21000) @@ -298,6 +299,19 @@ func TestTransferERC20(t *testing.T) { }, expectedErr: nil, }, + { + name: "Celo denominated transfer with fee currency", + txArgs: ðapi.TransactionArgs{ + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), + MaxFeePerGas: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + MaxPriorityFeePerGas: (*hexutil.Big)(datum), + Gas: &intrinsicGas, + FeeCurrency: &stableTokenAddress, + MaxFeeInFeeCurrency: (*hexutil.Big)(new(big.Int).Mul(new(big.Int).Mul(datum, big.NewInt(int64(intrinsicGas))), maxRate)), + }, + expectedErr: nil, + }, } // Get feeHandlerAddress diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 6d5346144e..297ecc302c 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -1289,6 +1289,7 @@ type RPCTransaction struct { GasFeeCap *hexutil.Big `json:"maxFeePerGas,omitempty"` GasTipCap *hexutil.Big `json:"maxPriorityFeePerGas,omitempty"` FeeCurrency *common.Address `json:"feeCurrency"` + MaxFeeInFeeCurrency *hexutil.Big `json:"maxFeeInFeeCurrency,omitempty"` GatewayFeeRecipient *common.Address `json:"gatewayFeeRecipient"` GatewayFee *hexutil.Big `json:"gatewayFee"` Hash common.Hash `json:"hash"` @@ -1331,6 +1332,7 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber Gas: hexutil.Uint64(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), FeeCurrency: tx.FeeCurrency(), + MaxFeeInFeeCurrency: (*hexutil.Big)(tx.MaxFeeInFeeCurrency()), GatewayFeeRecipient: tx.GatewayFeeRecipient(), GatewayFee: (*hexutil.Big)(tx.GatewayFee()), Hash: tx.Hash(), diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go index 2151e9591f..abd970c6c4 100644 --- a/internal/ethapi/transaction_args.go +++ b/internal/ethapi/transaction_args.go @@ -172,6 +172,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error { FeeCurrency: args.FeeCurrency, GatewayFee: args.GatewayFee, GatewayFeeRecipient: args.GatewayFeeRecipient, + MaxFeeInFeeCurrency: args.MaxFeeInFeeCurrency, Value: args.Value, Data: (*hexutil.Bytes)(&data), AccessList: args.AccessList, @@ -355,7 +356,7 @@ func (args *TransactionArgs) ToTransaction() *types.Transaction { func (args *TransactionArgs) checkEthCompatibility() error { // Reject if Celo-only fields set when EthCompatible is true - if args.EthCompatible && !(args.FeeCurrency == nil && args.GatewayFeeRecipient == nil && (args.GatewayFee == nil || args.GatewayFee.ToInt().Sign() == 0)) { + if args.EthCompatible && !(args.FeeCurrency == nil && args.GatewayFeeRecipient == nil && (args.GatewayFee == nil || args.GatewayFee.ToInt().Sign() == 0) && args.MaxFeeInFeeCurrency == nil) { return types.ErrEthCompatibleTransactionIsntCompatible } return nil diff --git a/mycelo/genesis/base_config.go b/mycelo/genesis/base_config.go index bed7742e9b..a10404c218 100644 --- a/mycelo/genesis/base_config.go +++ b/mycelo/genesis/base_config.go @@ -10,17 +10,13 @@ import ( // BaseConfig creates base parameters for celo // Callers must complete missing pieces -func BaseConfig(gingerbreadBlock *big.Int) *Config { - baseFeeOpCodeActivationBlock := gingerbreadBlock +func BaseConfig() *Config { + baseFeeOpCodeActivationBlock := big.NewInt(1) bigInt := big.NewInt bigIntStr := common.MustBigInt fixed := fixed.MustNew decimal := decimal.RequireFromString - if baseFeeOpCodeActivationBlock == nil { - baseFeeOpCodeActivationBlock = big.NewInt(0) // deactivated - } - return &Config{ SortedOracles: SortedOraclesParameters{ ReportExpirySeconds: 5 * Minute, diff --git a/mycelo/genesis/genesis.go b/mycelo/genesis/genesis.go index 9d50c58b09..30fef589ec 100644 --- a/mycelo/genesis/genesis.go +++ b/mycelo/genesis/genesis.go @@ -19,8 +19,8 @@ import ( var genesisMsgHash = common.HexToHash("ecc833a7747eaa8327335e8e0c6b6d8aa3a38d0063591e43ce116ccf5c89753e") // CreateCommonGenesisConfig generates a config starting point which templates can then customize further -func CreateCommonGenesisConfig(chainID *big.Int, adminAccountAddress common.Address, istanbulConfig params.IstanbulConfig, gingerbreadBlock *big.Int) (*Config, error) { - genesisConfig := BaseConfig(gingerbreadBlock) +func CreateCommonGenesisConfig(chainID *big.Int, adminAccountAddress common.Address, istanbulConfig params.IstanbulConfig, hforkBlock *big.Int) (*Config, error) { + genesisConfig := BaseConfig() genesisConfig.ChainID = chainID genesisConfig.GenesisTimestamp = uint64(time.Now().Unix()) genesisConfig.Istanbul = istanbulConfig @@ -28,8 +28,9 @@ func CreateCommonGenesisConfig(chainID *big.Int, adminAccountAddress common.Addr ChurritoBlock: common.Big0, DonutBlock: common.Big0, EspressoBlock: common.Big0, - GingerbreadBlock: gingerbreadBlock, - GingerbreadP2Block: gingerbreadBlock, + GingerbreadBlock: common.Big0, + GingerbreadP2Block: common.Big0, + HForkBlock: hforkBlock, } // Make admin account manager of Governance & Reserve diff --git a/test/node.go b/test/node.go index 74e3f9b879..3b8e65a9f4 100644 --- a/test/node.go +++ b/test/node.go @@ -344,12 +344,12 @@ func AccountConfig(numValidators, numExternal int) *env.AccountsConfig { // NOTE: Do not edit the Istanbul field of the returned genesis config it will // be overwritten with the corresponding config from the Istanbul field of the // returned eth config. -func BuildConfig(accounts *env.AccountsConfig, gingerbreadBlock *big.Int) (*genesis.Config, *ethconfig.Config, error) { +func BuildConfig(accounts *env.AccountsConfig, hforkBlock *big.Int) (*genesis.Config, *ethconfig.Config, error) { gc, err := genesis.CreateCommonGenesisConfig( big.NewInt(1), accounts.AdminAccount().Address, params.IstanbulConfig{}, - gingerbreadBlock, + hforkBlock, ) if err != nil { return nil, nil, err From 6ec207fa3dfa0557371df71583d899137b44bb15 Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 05:26:23 -0300 Subject: [PATCH 02/11] Add receipt fields feeInFeeCurrency (CIP-66) && extend current e2e test for CELO denominated txs to use it --- core/state_processor.go | 1 + core/state_transition.go | 47 +++++++++++++++++++--------------- core/types/gen_header_json.go | 3 --- core/types/gen_receipt_json.go | 6 +++++ core/types/receipt.go | 35 +++++++++++++++++-------- e2e_test/e2e_transfer_test.go | 18 +++++++++---- internal/ethapi/api.go | 1 + 7 files changed, 71 insertions(+), 40 deletions(-) diff --git a/core/state_processor.go b/core/state_processor.go index c0fa3663d5..37094e6b30 100644 --- a/core/state_processor.go +++ b/core/state_processor.go @@ -172,6 +172,7 @@ func applyTransaction(msg types.Message, config *params.ChainConfig, gp *GasPool } else { receipt.Status = types.ReceiptStatusSuccessful } + receipt.FeeInFeeCurrency = result.FeeInFeeCurrency receipt.TxHash = tx.Hash() receipt.GasUsed = result.UsedGas diff --git a/core/state_transition.go b/core/state_transition.go index 3441ac9b1a..2bc146c71f 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -60,21 +60,22 @@ import ( // 5. Run Script section // 6. Derive new state root type StateTransition struct { - gp *GasPool - msg Message - gas uint64 - gasPrice *big.Int - gasFeeCap *big.Int - gasTipCap *big.Int - initialGas uint64 - value *big.Int - data []byte - state vm.StateDB - evm *vm.EVM - vmRunner vm.EVMRunner - gasPriceMinimum *big.Int - sysCtx *SysContractCallCtx - erc20FeeDebited *big.Int + gp *GasPool + msg Message + gas uint64 + gasPrice *big.Int + gasFeeCap *big.Int + gasTipCap *big.Int + initialGas uint64 + value *big.Int + data []byte + state vm.StateDB + evm *vm.EVM + vmRunner vm.EVMRunner + gasPriceMinimum *big.Int + sysCtx *SysContractCallCtx + erc20FeeDebited *big.Int // Total debited in erc20 gas currencies + feeInFeeCurrency *big.Int // total fee paid (debited - credited) in fee currency for CELO deno txs } // Message represents a message sent to a contract. @@ -118,9 +119,10 @@ func CheckEthCompatibility(msg Message) error { // ExecutionResult includes all output after executing given evm // message no matter the execution itself is successful or not. type ExecutionResult struct { - UsedGas uint64 // Total used gas but include the refunded gas - Err error // Any error encountered during the execution(listed in core/vm/errors.go) - ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + UsedGas uint64 // Total used gas but include the refunded gas + Err error // Any error encountered during the execution(listed in core/vm/errors.go) + ReturnData []byte // Returned data from evm(function result or data supplied with revert opcode) + FeeInFeeCurrency *big.Int // Fee paid (debit - credit) on CELO denominated txs } // Unwrap returns the internal evm error which allows us for further @@ -558,9 +560,10 @@ func (st *StateTransition) TransitionDb() (*ExecutionResult, error) { return nil, err } return &ExecutionResult{ - UsedGas: st.gasUsed(), - Err: vmerr, - ReturnData: ret, + UsedGas: st.gasUsed(), + Err: vmerr, + ReturnData: ret, + FeeInFeeCurrency: st.feeInFeeCurrency, }, nil } @@ -602,6 +605,8 @@ func (st *StateTransition) creditTxFees(feeCurrencyRate *currency.ExchangeRate) refund.Sub(st.erc20FeeDebited, totalTxFee) // refund = debited - tip - basefee // No need to exchange gateway fee since it's it's deprecated on G fork, // and MaxFeeInFeeCurrency can only be present in H fork (which implies G fork) + // Set receipt field + st.feeInFeeCurrency = totalTxFee } feeCurrency := st.msg.FeeCurrency() diff --git a/core/types/gen_header_json.go b/core/types/gen_header_json.go index 9bae389b60..c7cd552e7b 100644 --- a/core/types/gen_header_json.go +++ b/core/types/gen_header_json.go @@ -137,8 +137,5 @@ func (h *Header) UnmarshalJSON(input []byte) error { if dec.BaseFee != nil { h.BaseFee = (*big.Int)(dec.BaseFee) } - if dec.BaseFee != nil { - h.BaseFee = (*big.Int)(dec.BaseFee) - } return nil } diff --git a/core/types/gen_receipt_json.go b/core/types/gen_receipt_json.go index a0bcef2a62..abc394d0f0 100644 --- a/core/types/gen_receipt_json.go +++ b/core/types/gen_receipt_json.go @@ -22,6 +22,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Logs []*Log `json:"logs" gencodec:"required"` + FeeInFeeCurrency *hexutil.Big `json:"feeInFeeCurrency"` TxHash common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress common.Address `json:"contractAddress"` GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` @@ -36,6 +37,7 @@ func (r Receipt) MarshalJSON() ([]byte, error) { enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed) enc.Bloom = r.Bloom enc.Logs = r.Logs + enc.FeeInFeeCurrency = (*hexutil.Big)(r.FeeInFeeCurrency) enc.TxHash = r.TxHash enc.ContractAddress = r.ContractAddress enc.GasUsed = hexutil.Uint64(r.GasUsed) @@ -54,6 +56,7 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` Bloom *Bloom `json:"logsBloom" gencodec:"required"` Logs []*Log `json:"logs" gencodec:"required"` + FeeInFeeCurrency *hexutil.Big `json:"feeInFeeCurrency"` TxHash *common.Hash `json:"transactionHash" gencodec:"required"` ContractAddress *common.Address `json:"contractAddress"` GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` @@ -86,6 +89,9 @@ func (r *Receipt) UnmarshalJSON(input []byte) error { return errors.New("missing required field 'logs' for Receipt") } r.Logs = dec.Logs + if dec.FeeInFeeCurrency != nil { + r.FeeInFeeCurrency = (*big.Int)(dec.FeeInFeeCurrency) + } if dec.TxHash == nil { return errors.New("missing required field 'transactionHash' for Receipt") } diff --git a/core/types/receipt.go b/core/types/receipt.go index 2560e1fb89..c784d7996e 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -52,12 +52,13 @@ const ( // Receipt represents the results of a transaction. type Receipt struct { // Consensus fields: These fields are defined by the Yellow Paper - Type uint8 `json:"type,omitempty"` - PostState []byte `json:"root"` - Status uint64 `json:"status"` - CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` - Bloom Bloom `json:"logsBloom" gencodec:"required"` - Logs []*Log `json:"logs" gencodec:"required"` + Type uint8 `json:"type,omitempty"` + PostState []byte `json:"root"` + Status uint64 `json:"status"` + CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` + FeeInFeeCurrency *big.Int `json:"feeInFeeCurrency,omitempty"` // Implementation fields: These fields are added by geth when processing a transaction. // They are stored in the chain database. @@ -80,6 +81,7 @@ type receiptMarshaling struct { GasUsed hexutil.Uint64 BlockNumber *hexutil.Big TransactionIndex hexutil.Uint + FeeInFeeCurrency *hexutil.Big } // receiptRLP is the consensus encoding of a receipt. @@ -88,6 +90,7 @@ type receiptRLP struct { CumulativeGasUsed uint64 Bloom Bloom Logs []*Log + FeeInFeeCurrency *big.Int } // storedReceiptRLP is the storage encoding of a receipt. @@ -95,6 +98,7 @@ type storedReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Logs []*LogForStorage + FeeInFeeCurrency *big.Int } // v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4. @@ -137,10 +141,13 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { // EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt // into an RLP stream. If no post state is present, byzantium fork is assumed. func (r *Receipt) EncodeRLP(w io.Writer) error { - data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, nil} if r.Type == LegacyTxType { return rlp.Encode(w, data) } + if r.Type == CeloDenominatedTxType { + data.FeeInFeeCurrency = r.FeeInFeeCurrency + } buf := encodeBufferPool.Get().(*bytes.Buffer) defer encodeBufferPool.Put(buf) buf.Reset() @@ -190,7 +197,7 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { } func (r *Receipt) setFromRLP(data receiptRLP) error { - r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs + r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency = data.CumulativeGasUsed, data.Bloom, data.Logs, data.FeeInFeeCurrency return r.setStatus(data.PostStateOrStatus) } @@ -226,6 +233,9 @@ func (r *Receipt) Size() common.StorageSize { for _, log := range r.Logs { size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data)) } + if r.FeeInFeeCurrency != nil { + size += common.StorageSize(r.FeeInFeeCurrency.BitLen() / 8) + } return size } @@ -240,6 +250,7 @@ func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { PostStateOrStatus: (*Receipt)(r).statusEncoding(), CumulativeGasUsed: r.CumulativeGasUsed, Logs: make([]*LogForStorage, len(r.Logs)), + FeeInFeeCurrency: r.FeeInFeeCurrency, } for i, log := range r.Logs { enc.Logs[i] = (*LogForStorage)(log) @@ -282,7 +293,10 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { r.Logs[i] = (*Log)(log) } r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) - + // rlp encodes the nil value as a zero instance + if stored.FeeInFeeCurrency != nil && stored.FeeInFeeCurrency.Cmp(common.Big0) != 0 { + r.FeeInFeeCurrency = stored.FeeInFeeCurrency + } return nil } @@ -336,7 +350,7 @@ func (rs Receipts) Len() int { return len(rs) } // EncodeIndex encodes the i'th receipt to w. func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { r := rs[i] - data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} switch r.Type { case LegacyTxType: rlp.Encode(w, data) @@ -366,7 +380,6 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { // data and contextual infos like containing block and transactions. func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error { signer := MakeSigner(config, new(big.Int).SetUint64(number)) - logIndex := uint(0) if len(txs) != len(r) { // The receipts may include an additional "block finalization" receipt (only IBFT) diff --git a/e2e_test/e2e_transfer_test.go b/e2e_test/e2e_transfer_test.go index e04aa3e233..2fc06da77a 100644 --- a/e2e_test/e2e_transfer_test.go +++ b/e2e_test/e2e_transfer_test.go @@ -278,14 +278,16 @@ func TestTransferERC20(t *testing.T) { header, err := network[0].WsClient.HeaderByNumber(ctx, nil) require.NoError(t, err) datum := header.BaseFee - maxRate, _ := new(big.Int).SetString("1000000000000000000000000", 10) + actualRate, _ := new(big.Int).SetString("1", 10) + maxRate, _ := new(big.Int).SetString("1", 10) stableTokenAddress := env.MustProxyAddressFor("StableToken") intrinsicGas := hexutil.Uint64(config.IntrinsicGasForAlternativeFeeCurrency + 21000) testCases := []struct { - name string - txArgs *ethapi.TransactionArgs - expectedErr error + name string + txArgs *ethapi.TransactionArgs + expectedFeeInFeeCurrency *big.Int + expectedErr error }{ { name: "Celo transfer with fee currency", @@ -310,7 +312,8 @@ func TestTransferERC20(t *testing.T) { FeeCurrency: &stableTokenAddress, MaxFeeInFeeCurrency: (*hexutil.Big)(new(big.Int).Mul(new(big.Int).Mul(datum, big.NewInt(int64(intrinsicGas))), maxRate)), }, - expectedErr: nil, + expectedFeeInFeeCurrency: new(big.Int).Mul(actualRate, new(big.Int).Mul(datum, big.NewInt(int64(intrinsicGas)))), + expectedErr: nil, }, } @@ -365,6 +368,11 @@ func TestTransferERC20(t *testing.T) { expected := tx.Value() actual := watcher.Delta(recipient.Address) assert.Equal(t, expected, actual, "Recipient's balance increase unexpected", "expected", expected.Int64(), "actual", actual.Int64()) + + // Check for expected FeeInFeeCurrency in receipt for CELO denominated txs + expectedFeeInReceipt := tc.expectedFeeInFeeCurrency + actualFeeInReceipt := receipt.FeeInFeeCurrency + assert.Equal(t, expectedFeeInReceipt, actualFeeInReceipt, "Receipt FeeInFeeCurrency unexpected", "expected", expectedFeeInReceipt.String(), "actual", actualFeeInReceipt.String()) }) } } diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 297ecc302c..1c2d4c9e68 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -2113,6 +2113,7 @@ func generateReceiptResponse(ctx context.Context, backend Backend, receipt *type "contractAddress": nil, "logs": receipt.Logs, "logsBloom": receipt.Bloom, + "feeInFeeCurrency": (*hexutil.Big)(receipt.FeeInFeeCurrency), "type": hexutil.Uint(receipt.Type), } From 80f296aebb7caeaa57dc3ca7fea9a42f9b782e08 Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 06:35:08 -0300 Subject: [PATCH 03/11] Fix coexistance of pre and post CIP66 receipt values --- core/types/receipt.go | 47 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index c784d7996e..1a2f369cd0 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -93,6 +93,13 @@ type receiptRLP struct { FeeInFeeCurrency *big.Int } +type preCIP66receiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + Bloom Bloom + Logs []*Log +} + // storedReceiptRLP is the storage encoding of a receipt. type storedReceiptRLP struct { PostStateOrStatus []byte @@ -101,6 +108,13 @@ type storedReceiptRLP struct { FeeInFeeCurrency *big.Int } +// storedPreCIP66ReceiptRLP is the storage encoding of a receipt before adding the FeeInFeeCurrency field +type storedPreCIP66ReceiptRLP struct { + PostStateOrStatus []byte + CumulativeGasUsed uint64 + Logs []*LogForStorage +} + // v4StoredReceiptRLP is the storage encoding of a receipt used in database version 4. type v4StoredReceiptRLP struct { PostStateOrStatus []byte @@ -183,7 +197,13 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return errEmptyTypedReceipt } r.Type = b[0] - if r.Type == AccessListTxType || r.Type == DynamicFeeTxType || r.Type == CeloDynamicFeeTxType || r.Type == CeloDynamicFeeTxV2Type || r.Type == CeloDenominatedTxType { + if r.Type == AccessListTxType || r.Type == DynamicFeeTxType || r.Type == CeloDynamicFeeTxType || r.Type == CeloDynamicFeeTxV2Type { + var dec preCIP66receiptRLP + if err := rlp.DecodeBytes(b[1:], &dec); err != nil { + return err + } + return r.setFromPreCIP66RLP(dec) + } else if r.Type == CeloDenominatedTxType { var dec receiptRLP if err := rlp.DecodeBytes(b[1:], &dec); err != nil { return err @@ -196,6 +216,11 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { } } +func (r *Receipt) setFromPreCIP66RLP(data preCIP66receiptRLP) error { + r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs + return r.setStatus(data.PostStateOrStatus) +} + func (r *Receipt) setFromRLP(data receiptRLP) error { r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency = data.CumulativeGasUsed, data.Bloom, data.Logs, data.FeeInFeeCurrency return r.setStatus(data.PostStateOrStatus) @@ -272,6 +297,9 @@ func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { if err := decodeStoredReceiptRLP(r, blob); err == nil { return nil } + if err := decodePreCIP66ReceiptRLP(r, blob); err == nil { + return nil + } if err := decodeV3StoredReceiptRLP(r, blob); err == nil { return nil } @@ -300,6 +328,23 @@ func decodeStoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { return nil } +func decodePreCIP66ReceiptRLP(r *ReceiptForStorage, blob []byte) error { + var stored storedPreCIP66ReceiptRLP + if err := rlp.DecodeBytes(blob, &stored); err != nil { + return err + } + if err := (*Receipt)(r).setStatus(stored.PostStateOrStatus); err != nil { + return err + } + r.CumulativeGasUsed = stored.CumulativeGasUsed + r.Logs = make([]*Log, len(stored.Logs)) + for i, log := range stored.Logs { + r.Logs[i] = (*Log)(log) + } + r.Bloom = CreateBloom(Receipts{(*Receipt)(r)}) + return nil +} + func decodeV4StoredReceiptRLP(r *ReceiptForStorage, blob []byte) error { var stored v4StoredReceiptRLP if err := rlp.DecodeBytes(blob, &stored); err != nil { From eede48f7c03d8961c836e18cdeac59c579391f26 Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 07:00:34 -0300 Subject: [PATCH 04/11] Remove old tests from previous fork --- e2e_test/e2e_test.go | 162 ++++--------------------------------------- 1 file changed, 12 insertions(+), 150 deletions(-) diff --git a/e2e_test/e2e_test.go b/e2e_test/e2e_test.go index adceb1fabb..7c24415297 100644 --- a/e2e_test/e2e_test.go +++ b/e2e_test/e2e_test.go @@ -559,25 +559,25 @@ func TestRPCDynamicTxGasPriceWithState(t *testing.T) { // TestRPCDynamicTxGasPriceWithoutState aims to test the scenario where a // an old dynamic tx is requested via rpc, to a full node that does not have // the state anymore. -func TestRPCDynamicTxGasPriceWithoutStateBeforeGingerbread(t *testing.T) { +func TestRPCDynamicTxGasPriceWithoutStateBeforeHFork(t *testing.T) { testRPCDynamicTxGasPriceWithoutState(t, false, false) } -func TestRPCDynamicTxGasPriceWithoutStateAfterGingerbread(t *testing.T) { +func TestRPCDynamicTxGasPriceWithoutStateAfterHFork(t *testing.T) { testRPCDynamicTxGasPriceWithoutState(t, true, false) } -func TestRPCDynamicTxGasPriceWithoutStateForAlternativeCurrencyBeforeGingerbread(t *testing.T) { +func TestRPCDynamicTxGasPriceWithoutStateForAlternativeCurrencyBeforeHFork(t *testing.T) { testRPCDynamicTxGasPriceWithoutState(t, false, true) } -func TestRPCDynamicTxGasPriceWithoutStateForAlternativeCurrencyAfterGingerbread(t *testing.T) { +func TestRPCDynamicTxGasPriceWithoutStateForAlternativeCurrencyAfterHFork(t *testing.T) { testRPCDynamicTxGasPriceWithoutState(t, true, true) } -func testRPCDynamicTxGasPriceWithoutState(t *testing.T, afterGingerbread, alternativeCurrency bool) { +func testRPCDynamicTxGasPriceWithoutState(t *testing.T, afterHfork, alternativeCurrency bool) { var hforkBlock *big.Int - if afterGingerbread { + if afterHfork { hforkBlock = common.Big0 } cusdAddress := common.HexToAddress("0xd008") @@ -634,7 +634,7 @@ func testRPCDynamicTxGasPriceWithoutState(t *testing.T, afterGingerbread, altern // Blocknumber != nil it means that it eas already processed require.NotNil(t, json2.BlockNumber) - if afterGingerbread && !alternativeCurrency { + if !alternativeCurrency { require.Equal(t, json.GasPrice, json2.GasPrice) } else { require.Nil(t, json2.GasPrice) @@ -699,7 +699,7 @@ func TestEthersJSCompatibility(t *testing.T) { // This test checks the functionality of the configuration to enable/disable // returning the 'gasLimit' and 'baseFeePerGas' fields on RPC blocks. -func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) { +func TestEthersJSCompatibilityDisable(t *testing.T) { ac := test.AccountConfig(1, 1) hforkBlock := common.Big0 gc, ec, err := test.BuildConfig(ac, hforkBlock) @@ -719,7 +719,7 @@ func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) { for _, field := range []string{"gasLimit", "baseFeePerGas", "sha3Uncles", "uncles", "nonce", "mixHash", "difficulty"} { _, ok := result[field] - assert.Truef(t, ok, "%s field should be present on RPC block after Gingerbread", field) + assert.Truef(t, ok, "%s field should be present on RPC block", field) } require.Equal(t, result["sha3Uncles"], "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347") @@ -736,150 +736,12 @@ func TestEthersJSCompatibilityDisableAfterGingerbread(t *testing.T) { err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "0x0", true) require.NoError(t, err) - // After Gingerbread, gasLimit should be returned directly from the header, even if + // gasLimit should be returned directly from the header, even if // RPCEthCompatibility is off, since it is now part of the header hash. _, ok := result["gasLimit"] - assert.True(t, ok, "gasLimit field must be present on RPC block after Gingerbread") + assert.True(t, ok, "gasLimit field must be present on RPC block") _, ok = result["baseFeePerGas"] - assert.True(t, ok, "baseFeePerGas field must be present on RPC block after Gingerbread") -} - -// This test checks the functionality of the configuration to enable/disable -// returning the 'gasLimit' and 'baseFeePerGas' fields on RPC blocks before the Gingerbread happened. -// Gingerbread is relevant because it added the gasLimit to the header. -func TestEthersJSCompatibilityDisableBeforeGingerbread(t *testing.T) { - ac := test.AccountConfig(1, 1) - var hforkBlock *big.Int = nil - gc, ec, err := test.BuildConfig(ac, hforkBlock) - require.NoError(t, err) - - // Check fields present (compatibility set by default) - network, shutdown, err := test.NewNetwork(ac, gc, ec) - require.NoError(t, err) - defer shutdown() - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) - defer cancel() - - result := make(map[string]interface{}) - err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) - require.NoError(t, err) - - for _, field := range []string{"gasLimit", "baseFeePerGas", "difficulty"} { - _, ok := result[field] - assert.Truef(t, ok, "%s field should be present on RPC block before Gingerbread", field) - } - for _, field := range []string{"sha3Uncles", "uncles", "nonce", "mixHash"} { - _, ok := result[field] - assert.Falsef(t, ok, "%s field should not be present on RPC block before Gingerbread", field) - } - - // Turn off compatibility and check fields are not present - ec.RPCEthCompatibility = false - network, shutdown, err = test.NewNetwork(ac, gc, ec) - require.NoError(t, err) - defer shutdown() - - ctx, cancel = context.WithTimeout(context.Background(), time.Second*20) - defer cancel() - - result = make(map[string]interface{}) - err = network[0].WsClient.GetRPCClient().CallContext(ctx, &result, "eth_getBlockByNumber", "latest", true) - require.NoError(t, err) - - for _, field := range []string{"gasLimit", "baseFeePerGas", "sha3Uncles", "uncles", "nonce", "mixHash", "difficulty"} { - _, ok := result[field] - assert.Falsef(t, ok, "%s field should not be present on RPC block before Gingerbread", field) - } -} - -// Initially we could not retrieve the eth compatibility fields (gasLimit & -// baseFee) on the genesis block because our logic dictated that the effective -// base fee and gas limit for a block were the ones set in the previous block -// (because those values are required by the vm before processing a block). -// There was no special case to handle the genesis block. We now have a special -// case to handle the genesis block which returns the values retrieved from the -// state at the genesis block. -func TestEthCompatibilityFieldsOnGenesisBlock(t *testing.T) { - ac := test.AccountConfig(1, 1) - // Gingerbread needs to be disabled for this test to be meaningful (since - // gingerbread added gasLimt & baseFee fields to the block) - var hforkBlock *big.Int = nil - - // Fist we test without eth compatibility to ensure that the setting has an effect. - gc, ec, err := test.BuildConfig(ac, hforkBlock) - ec.RPCEthCompatibility = false - require.NoError(t, err) - network, shutdown, err := test.NewNetwork(ac, gc, ec) - require.NoError(t, err) - defer shutdown() - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) - defer cancel() - - block, err := network[0].WsClient.BlockByNumber(ctx, big.NewInt(0)) - require.NoError(t, err) - - var nilBigInt *big.Int - require.Equal(t, nilBigInt, block.BaseFee()) - require.Equal(t, uint64(0), block.GasLimit()) - - // Now we with eth compatility enabled and see that gasLimit and baseFee - // are returned on the block. - gc, ec, err = test.BuildConfig(ac, hforkBlock) - ec.RPCEthCompatibility = true - require.NoError(t, err) - network, shutdown, err = test.NewNetwork(ac, gc, ec) - require.NoError(t, err) - defer shutdown() - - block, err = network[0].WsClient.BlockByNumber(ctx, big.NewInt(0)) - require.NoError(t, err) - - require.NotEqual(t, nilBigInt, block.BaseFee()) - require.Greater(t, block.BaseFee().Uint64(), uint64(0)) - require.Greater(t, block.GasLimit(), uint64(0)) -} - -// Initially we were not able to set the gingerbread activation block to be the genesis block, this test checks that it's possible. -func TestSettingGingerbreadOnGenesisBlock(t *testing.T) { - ac := test.AccountConfig(1, 1) - - // Fist we test without gingerbread to ensure that setting the gingerbread - // actually has an effect. - var hforkBlock *big.Int = nil - gc, ec, err := test.BuildConfig(ac, hforkBlock) - ec.RPCEthCompatibility = false - require.NoError(t, err) - network, shutdown, err := test.NewNetwork(ac, gc, ec) - require.NoError(t, err) - defer shutdown() - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) - defer cancel() - - block, err := network[0].WsClient.BlockByNumber(ctx, big.NewInt(0)) - require.NoError(t, err) - - var nilBigInt *big.Int - require.Equal(t, nilBigInt, block.BaseFee()) - require.Equal(t, uint64(0), block.GasLimit()) - - // Now we check that setting the gingerbread block at genesis causes gasLimit and baseFee to be set on the block. - hforkBlock = big.NewInt(0) - gc, ec, err = test.BuildConfig(ac, hforkBlock) - ec.RPCEthCompatibility = false - require.NoError(t, err) - network, shutdown, err = test.NewNetwork(ac, gc, ec) - require.NoError(t, err) - defer shutdown() - - block, err = network[0].WsClient.BlockByNumber(ctx, big.NewInt(0)) - require.NoError(t, err) - - require.NotEqual(t, nilBigInt, block.BaseFee()) - require.Greater(t, block.BaseFee().Uint64(), uint64(0)) - require.Greater(t, block.GasLimit(), uint64(0)) + assert.True(t, ok, "baseFeePerGas field must be present on RPC block") } // This test checks that retreiveing the "finalized" block results in the same response as retrieving the "latest" block. From 45cf06206612f147f2c7e0339fdafc4933d54493 Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 07:16:01 -0300 Subject: [PATCH 05/11] fix encode for CELOdenominated tx receipts --- core/types/receipt.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index 1a2f369cd0..0ebce05827 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -155,18 +155,21 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { // EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt // into an RLP stream. If no post state is present, byzantium fork is assumed. func (r *Receipt) EncodeRLP(w io.Writer) error { - data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, nil} + data := &preCIP66receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} if r.Type == LegacyTxType { return rlp.Encode(w, data) } - if r.Type == CeloDenominatedTxType { - data.FeeInFeeCurrency = r.FeeInFeeCurrency - } buf := encodeBufferPool.Get().(*bytes.Buffer) defer encodeBufferPool.Put(buf) buf.Reset() buf.WriteByte(r.Type) - if err := rlp.Encode(buf, data); err != nil { + var d interface{} + if r.Type == CeloDenominatedTxType { + d = &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} + } else { + d = data + } + if err := rlp.Encode(buf, d); err != nil { return err } return rlp.Encode(w, buf.Bytes()) From 90e09a66bf4bf6c69c4091150a4934733b7cf7ef Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 07:42:02 -0300 Subject: [PATCH 06/11] Fix ReadLogs not using the new receipt type --- core/rawdb/accessors_chain.go | 1 + core/types/receipt.go | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go index 13587331be..5e9be423fe 100644 --- a/core/rawdb/accessors_chain.go +++ b/core/rawdb/accessors_chain.go @@ -670,6 +670,7 @@ type storedReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Logs []*types.LogForStorage + FeeInFeeCurrency *big.Int } // ReceiptLogs is a barebone version of ReceiptForStorage which only keeps diff --git a/core/types/receipt.go b/core/types/receipt.go index 0ebce05827..9e744a0a03 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -52,13 +52,12 @@ const ( // Receipt represents the results of a transaction. type Receipt struct { // Consensus fields: These fields are defined by the Yellow Paper - Type uint8 `json:"type,omitempty"` - PostState []byte `json:"root"` - Status uint64 `json:"status"` - CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` - Bloom Bloom `json:"logsBloom" gencodec:"required"` - Logs []*Log `json:"logs" gencodec:"required"` - FeeInFeeCurrency *big.Int `json:"feeInFeeCurrency,omitempty"` + Type uint8 `json:"type,omitempty"` + PostState []byte `json:"root"` + Status uint64 `json:"status"` + CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` + Bloom Bloom `json:"logsBloom" gencodec:"required"` + Logs []*Log `json:"logs" gencodec:"required"` // Implementation fields: These fields are added by geth when processing a transaction. // They are stored in the chain database. @@ -71,6 +70,9 @@ type Receipt struct { BlockHash common.Hash `json:"blockHash,omitempty"` BlockNumber *big.Int `json:"blockNumber,omitempty"` TransactionIndex uint `json:"transactionIndex"` + + FeeInFeeCurrency *big.Int `json:"feeInFeeCurrency,omitempty"` // CIP-66 receipt field + } type receiptMarshaling struct { From d4c010f4865e5174599abade67653958d9c9e439 Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 11:18:51 -0300 Subject: [PATCH 07/11] Fix receipt encoding when rlp encoding types.Receipts array type --- core/types/receipt.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index 9e744a0a03..d58b0a1e68 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -400,7 +400,7 @@ func (rs Receipts) Len() int { return len(rs) } // EncodeIndex encodes the i'th receipt to w. func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { r := rs[i] - data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} + data := &preCIP66receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} switch r.Type { case LegacyTxType: rlp.Encode(w, data) @@ -418,7 +418,8 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { rlp.Encode(w, data) case CeloDenominatedTxType: w.WriteByte(CeloDenominatedTxType) - rlp.Encode(w, data) + cip66data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} + rlp.Encode(w, cip66data) default: // For unsupported types, write nothing. Since this is for // DeriveSha, the error will be caught matching the derived hash From 6361997d3cb7b0fb816b2c63ebf2a1be2303b580 Mon Sep 17 00:00:00 2001 From: Pasto Date: Mon, 8 Apr 2024 11:26:03 -0300 Subject: [PATCH 08/11] Fix receipt pre cip66 decoding on legacy tx --- core/types/receipt.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index d58b0a1e68..420ebd0d7d 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -186,12 +186,12 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return err case kind == rlp.List: // It's a legacy receipt. - var dec receiptRLP + var dec preCIP66receiptRLP if err := s.Decode(&dec); err != nil { return err } r.Type = LegacyTxType - return r.setFromRLP(dec) + return r.setFromPreCIP66RLP(dec) case kind == rlp.String: // It's an EIP-2718 typed tx receipt. b, err := s.Bytes() From ce8b669ff77bd1e7e5d43502743685fc6b28bb9b Mon Sep 17 00:00:00 2001 From: Pasto Date: Tue, 9 Apr 2024 11:43:13 -0300 Subject: [PATCH 09/11] Add failing test --- e2e_test/e2e_transfer_test.go | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/e2e_test/e2e_transfer_test.go b/e2e_test/e2e_transfer_test.go index 2fc06da77a..d5720072e8 100644 --- a/e2e_test/e2e_transfer_test.go +++ b/e2e_test/e2e_transfer_test.go @@ -306,7 +306,7 @@ func TestTransferERC20(t *testing.T) { txArgs: ðapi.TransactionArgs{ To: &recipient.Address, Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), - MaxFeePerGas: (*hexutil.Big)(datum.Mul(datum, new(big.Int).SetInt64(4))), + MaxFeePerGas: (*hexutil.Big)(datum), // set in previous test MaxPriorityFeePerGas: (*hexutil.Big)(datum), Gas: &intrinsicGas, FeeCurrency: &stableTokenAddress, @@ -315,6 +315,21 @@ func TestTransferERC20(t *testing.T) { expectedFeeInFeeCurrency: new(big.Int).Mul(actualRate, new(big.Int).Mul(datum, big.NewInt(int64(intrinsicGas)))), expectedErr: nil, }, + { + name: "Celo denominated -low MaxFeeInFeeCurrency", + txArgs: ðapi.TransactionArgs{ + To: &recipient.Address, + Value: (*hexutil.Big)(new(big.Int).SetInt64(oneCelo)), + MaxFeePerGas: (*hexutil.Big)(datum), // set in previous test + MaxPriorityFeePerGas: (*hexutil.Big)(datum), + Gas: &intrinsicGas, + FeeCurrency: &stableTokenAddress, + MaxFeeInFeeCurrency: (*hexutil.Big)(new(big.Int).Sub( + new(big.Int).Mul(actualRate, new(big.Int).Mul(datum, big.NewInt(int64(intrinsicGas)))), + common.Big1)), + }, + expectedErr: core.ErrDenominatedLowMaxFee, + }, } // Get feeHandlerAddress From 77c1b22156f6a5b6b7ca039055e4e93caf57f99c Mon Sep 17 00:00:00 2001 From: Pasto Date: Thu, 18 Apr 2024 03:32:35 -0300 Subject: [PATCH 10/11] Rename receipt RLP types --- core/types/receipt.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/core/types/receipt.go b/core/types/receipt.go index 420ebd0d7d..38a34cc53e 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -92,14 +92,14 @@ type receiptRLP struct { CumulativeGasUsed uint64 Bloom Bloom Logs []*Log - FeeInFeeCurrency *big.Int } -type preCIP66receiptRLP struct { +type celoDenominatedReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Bloom Bloom Logs []*Log + FeeInFeeCurrency *big.Int } // storedReceiptRLP is the storage encoding of a receipt. @@ -157,7 +157,7 @@ func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { // EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt // into an RLP stream. If no post state is present, byzantium fork is assumed. func (r *Receipt) EncodeRLP(w io.Writer) error { - data := &preCIP66receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} if r.Type == LegacyTxType { return rlp.Encode(w, data) } @@ -167,7 +167,7 @@ func (r *Receipt) EncodeRLP(w io.Writer) error { buf.WriteByte(r.Type) var d interface{} if r.Type == CeloDenominatedTxType { - d = &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} + d = &celoDenominatedReceiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} } else { d = data } @@ -186,12 +186,12 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { return err case kind == rlp.List: // It's a legacy receipt. - var dec preCIP66receiptRLP + var dec receiptRLP if err := s.Decode(&dec); err != nil { return err } r.Type = LegacyTxType - return r.setFromPreCIP66RLP(dec) + return r.setFromRLP(dec) case kind == rlp.String: // It's an EIP-2718 typed tx receipt. b, err := s.Bytes() @@ -203,17 +203,17 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { } r.Type = b[0] if r.Type == AccessListTxType || r.Type == DynamicFeeTxType || r.Type == CeloDynamicFeeTxType || r.Type == CeloDynamicFeeTxV2Type { - var dec preCIP66receiptRLP + var dec receiptRLP if err := rlp.DecodeBytes(b[1:], &dec); err != nil { return err } - return r.setFromPreCIP66RLP(dec) + return r.setFromRLP(dec) } else if r.Type == CeloDenominatedTxType { - var dec receiptRLP + var dec celoDenominatedReceiptRLP if err := rlp.DecodeBytes(b[1:], &dec); err != nil { return err } - return r.setFromRLP(dec) + return r.setFromCeloDenominatedRLP(dec) } return ErrTxTypeNotSupported default: @@ -221,12 +221,12 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error { } } -func (r *Receipt) setFromPreCIP66RLP(data preCIP66receiptRLP) error { +func (r *Receipt) setFromRLP(data receiptRLP) error { r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs return r.setStatus(data.PostStateOrStatus) } -func (r *Receipt) setFromRLP(data receiptRLP) error { +func (r *Receipt) setFromCeloDenominatedRLP(data celoDenominatedReceiptRLP) error { r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency = data.CumulativeGasUsed, data.Bloom, data.Logs, data.FeeInFeeCurrency return r.setStatus(data.PostStateOrStatus) } @@ -400,7 +400,7 @@ func (rs Receipts) Len() int { return len(rs) } // EncodeIndex encodes the i'th receipt to w. func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { r := rs[i] - data := &preCIP66receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} + data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs} switch r.Type { case LegacyTxType: rlp.Encode(w, data) @@ -418,7 +418,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { rlp.Encode(w, data) case CeloDenominatedTxType: w.WriteByte(CeloDenominatedTxType) - cip66data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} + cip66data := &celoDenominatedReceiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs, r.FeeInFeeCurrency} rlp.Encode(w, cip66data) default: // For unsupported types, write nothing. Since this is for From 0edebb37a512bb4f5dd0281fbffe3a7ee6618dc9 Mon Sep 17 00:00:00 2001 From: Pasto Date: Sun, 21 Apr 2024 22:06:52 -0300 Subject: [PATCH 11/11] Copy maxFeeInFeeCurrency in transaction getMaxFee... --- core/types/transaction.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/types/transaction.go b/core/types/transaction.go index ebec066f06..abd8be1f30 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -487,7 +487,13 @@ func (tx *Transaction) GatewaySet() bool { func (tx *Transaction) EthCompatible() bool { return tx.inner.ethCompatible() } // MaxFeeInFeeCurrency returns the max fee in the fee_currency for celo denominated txs. -func (tx *Transaction) MaxFeeInFeeCurrency() *big.Int { return tx.inner.maxFeeInFeeCurrency() } +func (tx *Transaction) MaxFeeInFeeCurrency() *big.Int { + maxFee := tx.inner.maxFeeInFeeCurrency() + if maxFee == nil { + return nil + } + return new(big.Int).Set(maxFee) +} // Fee calculates the fess paid by the transaction include the gateway fee. func (tx *Transaction) Fee() *big.Int {