From c01d5982ebd7e635fb21b1f827ccc21408ac4ab7 Mon Sep 17 00:00:00 2001 From: kourin Date: Wed, 8 Jan 2025 02:11:31 +0900 Subject: [PATCH 1/6] Add unit test for Celo Tx JSON marshaling Format imports Add test for CeloDenominatedTx --- .../celo_transaction_marshalling_test.go | 263 ++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 core/types/celo_transaction_marshalling_test.go diff --git a/core/types/celo_transaction_marshalling_test.go b/core/types/celo_transaction_marshalling_test.go new file mode 100644 index 0000000000..2ba98fe7b5 --- /dev/null +++ b/core/types/celo_transaction_marshalling_test.go @@ -0,0 +1,263 @@ +// Copyright 2024 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see . + +package types + +import ( + "encoding/json" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + "github.com/stretchr/testify/assert" +) + +func TestCeloTransactionMarshal(t *testing.T) { + t.Parallel() + + var ( + gingerbreadForkHeight int64 = 5 + signerBlockTime uint64 = 10 + cel2Time uint64 = 15 + + key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + signer = makeCeloSigner( + ¶ms.ChainConfig{ + ChainID: big.NewInt(params.CeloMainnetChainID), + Cel2Time: &cel2Time, + GingerbreadBlock: big.NewInt(gingerbreadForkHeight), + }, + signerBlockTime, + NewEIP155Signer(big.NewInt(params.CeloMainnetChainID)), + ) + + feeCurrencyAddress = common.HexToAddress("0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B") + toAddress = common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") + gatewayFeeRecipient = common.HexToAddress("0x471EcE3750Da237f93B8E339c536989b8978a438") + accessListAddress = common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7") + storageKey = common.HexToHash("0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2") + ) + + tests := []struct { + name string + tx *Transaction + expectedJson string + }{ + { + name: "Celo LegacyTx", + tx: MustSignNewTx(key, signer, &LegacyTx{ + Nonce: 10, + Gas: 1e6, + FeeCurrency: &feeCurrencyAddress, + GatewayFeeRecipient: &gatewayFeeRecipient, + GatewayFee: big.NewInt(1e7), + To: &toAddress, + Value: big.NewInt(1e8), + Data: []byte{0x11, 0x22, 0x33, 0x44}, + CeloLegacy: true, + }), + expectedJson: `{ + "type": "0x0", + "chainId": "0xa4ec", + "nonce": "0xa", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "gas": "0xf4240", + "gasPrice": null, + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "value": "0x5f5e100", + "input": "0x11223344", + "v": "0x149fc", + "r": "0x87e31aaf469f90072f46a87f48a60c6833f08216c0976e83d0f6d07ee16c2944", + "s": "0x6366cf4f800913df4db35d6e3360b7bf3db087aff63291ab7bfbe6bef4865bc9", + "hash": "0xa956b0aa70bc8e92ba260bb6865b46f36d8fc60cd72aaf472a3b2badc4379638", + "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", + "ethCompatible": false, + "gatewayFee": "0x989680", + "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438" + }`, + }, + { + name: "CeloDynamicFeeTx", + tx: MustSignNewTx(key, signer, &CeloDynamicFeeTx{ + ChainID: big.NewInt(params.CeloMainnetChainID), + Nonce: 10, + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(5e9), + Gas: 500000, + FeeCurrency: &feeCurrencyAddress, + GatewayFeeRecipient: &gatewayFeeRecipient, + GatewayFee: big.NewInt(1e7), + To: &toAddress, + Value: big.NewInt(1e8), + Data: []byte{0x11, 0x22, 0x33, 0x44}, + AccessList: AccessList{ + { + Address: accessListAddress, + StorageKeys: []common.Hash{ + storageKey, + }, + }, + }, + }), + expectedJson: `{ + "type": "0x7c", + "chainId": "0xa4ec", + "nonce": "0xa", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "gas": "0x7a120", + "gasPrice": null, + "maxPriorityFeePerGas": "0x1", + "maxFeePerGas": "0x12a05f200", + "value": "0x5f5e100", + "input": "0x11223344", + "accessList": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "storageKeys": [ + "0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2" + ] + } + ], + "v": "0x0", + "r": "0xe5c3c7490d804f15ab18a2c864eecd824cde653593c3e2bd1898d09bf0d59a51", + "s": "0x2c49f096c89f45dc0e0b24b7ce6e9b2b9cf13e7ab331e2e09c496080b4a6af2e", + "hash": "0x9a24ac05fb511923a40babf2826ff78ca71214f6315ef53476e7d3fd2aa51d18", + "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", + "gatewayFee": "0x989680", + "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438" + }`, + }, + { + name: "CeloDynamicFeeTxV2", + tx: MustSignNewTx(key, signer, &CeloDynamicFeeTxV2{ + ChainID: big.NewInt(params.CeloMainnetChainID), + Nonce: 10, + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(5e9), + Gas: 500000, + FeeCurrency: &feeCurrencyAddress, + To: &toAddress, + Value: big.NewInt(1e8), + Data: []byte{0x11, 0x22, 0x33, 0x44}, + AccessList: AccessList{ + { + Address: accessListAddress, + StorageKeys: []common.Hash{ + storageKey, + }, + }, + }, + }), + expectedJson: `{ + "type": "0x7b", + "chainId": "0xa4ec", + "nonce": "0xa", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "gas": "0x7a120", + "gasPrice": null, + "maxPriorityFeePerGas": "0x1", + "maxFeePerGas": "0x12a05f200", + "value": "0x5f5e100", + "input": "0x11223344", + "accessList": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "storageKeys": [ + "0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2" + ] + } + ], + "v": "0x0", + "r": "0xd0caa0257ad5e276c4164cc7cf033e95db5dc6f8c880c8b94aa132efd2378597", + "s": "0x63f213eb18899c862f1f9b84033f940bbc88de28f9af7a7c50b39e36896d1f51", + "hash": "0x8710502f18e464a4e44d6d660127c6173e1ba70ca5684e09d736d3b5e9e63e16", + "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b" + }`, + }, + { + name: "CeloDenominatedTx", + // Skip signing due to unsupported transaction type + tx: NewTx(&CeloDenominatedTx{ + ChainID: big.NewInt(params.CeloMainnetChainID), + Nonce: 10, + GasTipCap: big.NewInt(1), + GasFeeCap: big.NewInt(5e9), + Gas: 500000, + FeeCurrency: &feeCurrencyAddress, + MaxFeeInFeeCurrency: big.NewInt(1e8), + To: &toAddress, + Value: big.NewInt(1e8), + Data: []byte{0x11, 0x22, 0x33, 0x44}, + AccessList: AccessList{ + { + Address: accessListAddress, + StorageKeys: []common.Hash{ + storageKey, + }, + }, + }, + }), + expectedJson: `{ + "type": "0x7a", + "chainId": "0xa4ec", + "nonce": "0xa", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "gas": "0x7a120", + "gasPrice": null, + "maxPriorityFeePerGas": "0x1", + "maxFeePerGas": "0x12a05f200", + "maxFeeInFeeCurrency": "0x5f5e100", + "value": "0x5f5e100", + "input": "0x11223344", + "accessList": [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "storageKeys": [ + "0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2" + ] + } + ], + "v": "0x0", + "r": "0x0", + "s": "0x0", + "hash": "0x4812438d07f69839658264fd6bf9022ceb97e87f71ac7ebc56a463f550a8c065", + "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b" + }`, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + txJsonOuter, err := json.Marshal(test.tx) + assert.NoError(t, err) + + txJsonInner, isCeloTx, err := celoTransactionMarshal(test.tx) + assert.NoError(t, err) + + // Make sure the transaction is unmarshalled by celoTransactionMarshal + assert.True(t, isCeloTx) + + // Make sure Transaction.MarshalJSON returns the output of celoTransactionMarshal + assert.Equal(t, txJsonOuter, txJsonInner) + + // Make sure the output JSON is as expected + assert.JSONEq(t, test.expectedJson, string(txJsonOuter)) + }) + } +} From e64250711ff1ef6aa15ead0bf22c9492ae613f50 Mon Sep 17 00:00:00 2001 From: kourin Date: Wed, 8 Jan 2025 21:44:23 +0900 Subject: [PATCH 2/6] Add unit test for Celo Tx JSON unmarshaling --- core/types/celo_transaction_marshalling.go | 1 + .../celo_transaction_marshalling_test.go | 171 ++++++++++++++---- 2 files changed, 139 insertions(+), 33 deletions(-) diff --git a/core/types/celo_transaction_marshalling.go b/core/types/celo_transaction_marshalling.go index be0e7cf684..719faea22c 100644 --- a/core/types/celo_transaction_marshalling.go +++ b/core/types/celo_transaction_marshalling.go @@ -37,6 +37,7 @@ func celoTransactionMarshal(tx *Transaction) ([]byte, bool, error) { enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) enc.To = tx.To() enc.Gas = (*hexutil.Uint64)(&itx.Gas) + enc.GasPrice = (*hexutil.Big)(itx.GasPrice) enc.Value = (*hexutil.Big)(itx.Value) enc.Input = (*hexutil.Bytes)(&itx.Data) enc.V = (*hexutil.Big)(itx.V) diff --git a/core/types/celo_transaction_marshalling_test.go b/core/types/celo_transaction_marshalling_test.go index 2ba98fe7b5..7a5727a31a 100644 --- a/core/types/celo_transaction_marshalling_test.go +++ b/core/types/celo_transaction_marshalling_test.go @@ -18,6 +18,7 @@ package types import ( "encoding/json" + "fmt" "math/big" "testing" @@ -27,10 +28,12 @@ import ( "github.com/stretchr/testify/assert" ) -func TestCeloTransactionMarshal(t *testing.T) { +// TestCeloTransactionMarshalUnmarshal tests that each Celo transactions marshal and unmarshal correctly +func TestCeloTransactionMarshalUnmarshal(t *testing.T) { t.Parallel() var ( + chainId = big.NewInt(params.CeloMainnetChainID) gingerbreadForkHeight int64 = 5 signerBlockTime uint64 = 10 cel2Time uint64 = 15 @@ -38,12 +41,12 @@ func TestCeloTransactionMarshal(t *testing.T) { key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") signer = makeCeloSigner( ¶ms.ChainConfig{ - ChainID: big.NewInt(params.CeloMainnetChainID), + ChainID: chainId, Cel2Time: &cel2Time, GingerbreadBlock: big.NewInt(gingerbreadForkHeight), }, signerBlockTime, - NewEIP155Signer(big.NewInt(params.CeloMainnetChainID)), + NewEIP155Signer(chainId), ) feeCurrencyAddress = common.HexToAddress("0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B") @@ -54,15 +57,49 @@ func TestCeloTransactionMarshal(t *testing.T) { ) tests := []struct { - name string - tx *Transaction - expectedJson string + txType string + isCeloTx bool + tx *Transaction + json string + requiredFields []string }{ { - name: "Celo LegacyTx", + txType: "Ethereum LegacyTx", + isCeloTx: false, + tx: MustSignNewTx(key, signer, &LegacyTx{ + Nonce: 10, + Gas: 1e6, + GasPrice: big.NewInt(1e7), + To: &toAddress, + Value: big.NewInt(1e8), + Data: []byte{0x11, 0x22, 0x33, 0x44}, + CeloLegacy: false, + }), + json: `{ + "type": "0x0", + "chainId": "0xa4ec", + "nonce": "0xa", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "gas": "0xf4240", + "gasPrice": "0x989680", + "maxPriorityFeePerGas": null, + "maxFeePerGas": null, + "value": "0x5f5e100", + "input": "0x11223344", + "v": "0x149fc", + "r": "0x444416344542ecfd5824c0173395cca148cfa58cf7572d81196314ad4f5bf1f1", + "s": "0x23f6fd845489499c1170c8d0bd745f9fd3b99c2f4c979891b94e6764a03dcef0", + "hash": "0xf0051b6799141b669b18cf456ffb3509e089c00544878e74769819b345f00866" + }`, + requiredFields: []string{"nonce", "gas", "gasPrice", "value", "input", "v", "r", "s"}, + }, + { + txType: "Celo LegacyTx", + isCeloTx: true, tx: MustSignNewTx(key, signer, &LegacyTx{ Nonce: 10, Gas: 1e6, + GasPrice: big.NewInt(1e7), FeeCurrency: &feeCurrencyAddress, GatewayFeeRecipient: &gatewayFeeRecipient, GatewayFee: big.NewInt(1e7), @@ -71,31 +108,33 @@ func TestCeloTransactionMarshal(t *testing.T) { Data: []byte{0x11, 0x22, 0x33, 0x44}, CeloLegacy: true, }), - expectedJson: `{ + json: `{ "type": "0x0", "chainId": "0xa4ec", "nonce": "0xa", "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", "gas": "0xf4240", - "gasPrice": null, + "gasPrice": "0x989680", "maxPriorityFeePerGas": null, "maxFeePerGas": null, "value": "0x5f5e100", "input": "0x11223344", - "v": "0x149fc", - "r": "0x87e31aaf469f90072f46a87f48a60c6833f08216c0976e83d0f6d07ee16c2944", - "s": "0x6366cf4f800913df4db35d6e3360b7bf3db087aff63291ab7bfbe6bef4865bc9", - "hash": "0xa956b0aa70bc8e92ba260bb6865b46f36d8fc60cd72aaf472a3b2badc4379638", + "v": "0x149fb", + "r": "0x8ce12cd818c57c73354a1d18b5075bed356170f615246a5e4f7ac3a6a6c2d4c8", + "s": "0x64ab56cbf08dd6cf742f083084ed66167fc110192ee3365555e643f102b5cec7", + "hash": "0xfa30135c37ab29654ed0f75c07d6ba75cbb5d6739ab3249731ae80f80237bd5a", "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", "ethCompatible": false, "gatewayFee": "0x989680", "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438" }`, + requiredFields: []string{"nonce", "gas", "gasPrice", "value", "input", "v", "r", "s"}, }, { - name: "CeloDynamicFeeTx", + txType: "CeloDynamicFeeTx", + isCeloTx: true, tx: MustSignNewTx(key, signer, &CeloDynamicFeeTx{ - ChainID: big.NewInt(params.CeloMainnetChainID), + ChainID: chainId, Nonce: 10, GasTipCap: big.NewInt(1), GasFeeCap: big.NewInt(5e9), @@ -115,7 +154,7 @@ func TestCeloTransactionMarshal(t *testing.T) { }, }, }), - expectedJson: `{ + json: `{ "type": "0x7c", "chainId": "0xa4ec", "nonce": "0xa", @@ -133,7 +172,7 @@ func TestCeloTransactionMarshal(t *testing.T) { "0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2" ] } - ], + ], "v": "0x0", "r": "0xe5c3c7490d804f15ab18a2c864eecd824cde653593c3e2bd1898d09bf0d59a51", "s": "0x2c49f096c89f45dc0e0b24b7ce6e9b2b9cf13e7ab331e2e09c496080b4a6af2e", @@ -142,11 +181,13 @@ func TestCeloTransactionMarshal(t *testing.T) { "gatewayFee": "0x989680", "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438" }`, + requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s"}, }, { - name: "CeloDynamicFeeTxV2", + txType: "CeloDynamicFeeTxV2", + isCeloTx: true, tx: MustSignNewTx(key, signer, &CeloDynamicFeeTxV2{ - ChainID: big.NewInt(params.CeloMainnetChainID), + ChainID: chainId, Nonce: 10, GasTipCap: big.NewInt(1), GasFeeCap: big.NewInt(5e9), @@ -164,7 +205,7 @@ func TestCeloTransactionMarshal(t *testing.T) { }, }, }), - expectedJson: `{ + json: `{ "type": "0x7b", "chainId": "0xa4ec", "nonce": "0xa", @@ -189,12 +230,14 @@ func TestCeloTransactionMarshal(t *testing.T) { "hash": "0x8710502f18e464a4e44d6d660127c6173e1ba70ca5684e09d736d3b5e9e63e16", "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b" }`, + requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s"}, }, { - name: "CeloDenominatedTx", + txType: "CeloDenominatedTx", + isCeloTx: true, // Skip signing due to unsupported transaction type tx: NewTx(&CeloDenominatedTx{ - ChainID: big.NewInt(params.CeloMainnetChainID), + ChainID: chainId, Nonce: 10, GasTipCap: big.NewInt(1), GasFeeCap: big.NewInt(5e9), @@ -213,7 +256,7 @@ func TestCeloTransactionMarshal(t *testing.T) { }, }, }), - expectedJson: `{ + json: `{ "type": "0x7a", "chainId": "0xa4ec", "nonce": "0xa", @@ -239,25 +282,87 @@ func TestCeloTransactionMarshal(t *testing.T) { "hash": "0x4812438d07f69839658264fd6bf9022ceb97e87f71ac7ebc56a463f550a8c065", "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b" }`, + requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s"}, }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - txJsonOuter, err := json.Marshal(test.tx) + // testMarshaling tests that the transaction marshals to the expected JSON + testMarshaling := func(t *testing.T, tx *Transaction, expectedJson string, isCeloTxType bool) { + t.Helper() + + txJsonOuter, err := json.Marshal(tx) + assert.NoError(t, err) + + txJsonInner, isCeloTx, err := celoTransactionMarshal(tx) + assert.NoError(t, err) + + assert.Equal(t, isCeloTxType, isCeloTx) + + if isCeloTx { + // For Celo transaction types + // Make sure that celoTransactionMarshal produces the same JSON output as Transaction.MarshalJSON + assert.Equal(t, txJsonOuter, txJsonInner) + } + + // Make sure the output JSON is as expected + assert.JSONEq(t, expectedJson, string(txJsonOuter)) + } + + // testUnmarshaling tests that the transaction unmarshals to the expected Transaction + testUnmarshaling := func(t *testing.T, expectedTx *Transaction, jsonData string) { + t.Helper() + + tx := new(Transaction) + + err := json.Unmarshal([]byte(jsonData), tx) + assert.NoError(t, err) + + // Reassign the signature values because *hexutil.Big decodes "0x0" as nil for the `abs` field. + // This causes a mismatch with `big.NewInt(0)` + v2, r2, s2 := tx.inner.rawSignatureValues() + tx.inner.setSignatureValues( + chainId, + new(big.Int).SetBytes(v2.Bytes()), + new(big.Int).SetBytes(r2.Bytes()), + new(big.Int).SetBytes(s2.Bytes()), + ) + + assert.Equal(t, expectedTx.inner, tx.inner) + } + + // testUnmarshalMissingRequiredField tests that the transaction fails to unmarshal if a required field is missing + testUnmarshalMissingRequiredField := func(t *testing.T, jsonData string, requiredFields []string) { + t.Helper() + + for _, field := range requiredFields { + // Create a copy of the JSON data and remove one of the required fields + var jsonMap map[string]interface{} + err := json.Unmarshal([]byte(jsonData), &jsonMap) assert.NoError(t, err) - txJsonInner, isCeloTx, err := celoTransactionMarshal(test.tx) + delete(jsonMap, field) + + newJsonData, err := json.Marshal(jsonMap) assert.NoError(t, err) - // Make sure the transaction is unmarshalled by celoTransactionMarshal - assert.True(t, isCeloTx) + // Attempt to unmarshal the JSON data + tx := new(Transaction) + err = json.Unmarshal(newJsonData, tx) + assert.ErrorContains(t, err, fmt.Sprintf("missing required field '%s'", field)) + } + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%s should marshal to valid JSON successfully", test.txType), func(t *testing.T) { + testMarshaling(t, test.tx, test.json, test.isCeloTx) + }) - // Make sure Transaction.MarshalJSON returns the output of celoTransactionMarshal - assert.Equal(t, txJsonOuter, txJsonInner) + t.Run(fmt.Sprintf("%s should unmarshal valid JSON successfully", test.txType), func(t *testing.T) { + testUnmarshaling(t, test.tx, test.json) + }) - // Make sure the output JSON is as expected - assert.JSONEq(t, test.expectedJson, string(txJsonOuter)) + t.Run(fmt.Sprintf("%s should fail to marshal if required fields are missing", test.txType), func(t *testing.T) { + testUnmarshalMissingRequiredField(t, test.json, test.requiredFields) }) } } From fc05caf1f964e9a0af5a9931469c5c59084ad6bf Mon Sep 17 00:00:00 2001 From: kourin Date: Wed, 8 Jan 2025 22:58:11 +0900 Subject: [PATCH 3/6] Add unit test for Celo Tx RLP encoding/decoding --- core/types/celo_transaction_rlp_test.go | 326 ++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 core/types/celo_transaction_rlp_test.go diff --git a/core/types/celo_transaction_rlp_test.go b/core/types/celo_transaction_rlp_test.go new file mode 100644 index 0000000000..82dc5a5eb8 --- /dev/null +++ b/core/types/celo_transaction_rlp_test.go @@ -0,0 +1,326 @@ +// Copyright 2024 The Celo Authors +// This file is part of the celo library. +// +// The celo library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The celo library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the celo library. If not, see . + +package types + +import ( + "bytes" + "fmt" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/assert" +) + +// TestCeloTransactionRLPEncodingDecoding tests the RLP encoding and decoding of Celo transactions +func TestCeloTransactionRLPEncodingDecoding(t *testing.T) { + t.Parallel() + + var ( + chainId = big.NewInt(params.CeloMainnetChainID) + nonce = uint64(10) + gasPrice = big.NewInt(1e5) + gas = uint64(1e6) + feeCurrency = common.HexToAddress("0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B") + maxFeeInFeeCurrency = big.NewInt(1e6) + gatewayFee = big.NewInt(1e7) + gatewayFeeRecipient = common.HexToAddress("0x471EcE3750Da237f93B8E339c536989b8978a438") + to = common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") + value = big.NewInt(1e8) + data = []byte{0x12, 0x34, 0x56, 0x78} + v = common.Hex2Bytes("0x149fc") + r = common.Hex2Bytes("0x444416344542ecfd5824c0173395cca148cfa58cf7572d81196314ad4f5bf1f1") + s = common.Hex2Bytes("0x23f6fd845489499c1170c8d0bd745f9fd3b99c2f4c979891b94e6764a03dcef0") + gasTipCap = big.NewInt(1) + gasFeeCap = big.NewInt(1e9) + accessListAddress = common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7") + storageKey = common.HexToHash("0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2") + ) + + mustEncodeToBytes := func(t *testing.T, value interface{}) []byte { + t.Helper() + + data, err := rlp.EncodeToBytes(value) + assert.NoError(t, err) + + return data + } + + tests := []struct { + txType string + tx *Transaction + bytes []byte + }{ + { + txType: "Celo LegacyTx", + tx: NewTx(&LegacyTx{ + Nonce: nonce, + GasPrice: gasPrice, + Gas: gas, + FeeCurrency: &feeCurrency, + GatewayFeeRecipient: &gatewayFeeRecipient, + GatewayFee: gatewayFee, + To: &to, + Value: value, + Data: data, + V: new(big.Int).SetBytes(v), + R: new(big.Int).SetBytes(r), + S: new(big.Int).SetBytes(s), + CeloLegacy: true, + }), + bytes: mustEncodeToBytes(t, []interface{}{ + nonce, + gasPrice, + gas, + feeCurrency, + gatewayFeeRecipient, + gatewayFee, + to, + value, + data, + v, + r, + s, + }), + }, + { + txType: "CeloDynamicFeeTx", + tx: NewTx(&CeloDynamicFeeTx{ + ChainID: chainId, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gas, + FeeCurrency: &feeCurrency, + GatewayFeeRecipient: &gatewayFeeRecipient, + GatewayFee: gatewayFee, + To: &to, + Value: value, + Data: data, + AccessList: AccessList{ + { + Address: accessListAddress, + StorageKeys: []common.Hash{ + storageKey, + }, + }, + }, + V: new(big.Int).SetBytes(v), + R: new(big.Int).SetBytes(r), + S: new(big.Int).SetBytes(s), + }), + bytes: append( + []byte{CeloDynamicFeeTxType}, + mustEncodeToBytes(t, []interface{}{ + chainId, + nonce, + gasTipCap, + gasFeeCap, + gas, + feeCurrency, + gatewayFeeRecipient, + gatewayFee, + to, + value, + data, + []interface{}{ + []interface{}{ + accessListAddress, + []interface{}{ + storageKey, + }, + }, + }, + v, + r, + s, + })..., + ), + }, + { + txType: "CeloDynamicFeeTxV2", + tx: NewTx(&CeloDynamicFeeTxV2{ + ChainID: chainId, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gas, + To: &to, + Value: value, + Data: data, + AccessList: AccessList{ + { + Address: accessListAddress, + StorageKeys: []common.Hash{ + storageKey, + }, + }, + }, + FeeCurrency: &feeCurrency, + V: new(big.Int).SetBytes(v), + R: new(big.Int).SetBytes(r), + S: new(big.Int).SetBytes(s), + }), + bytes: append( + []byte{CeloDynamicFeeTxV2Type}, + mustEncodeToBytes(t, []interface{}{ + chainId, + nonce, + gasTipCap, + gasFeeCap, + gas, + to, + value, + data, + []interface{}{ + []interface{}{ + accessListAddress, + []interface{}{ + storageKey, + }, + }, + }, + feeCurrency, + v, + r, + s, + })..., + ), + }, + + { + txType: "CeloDenominatedTx", + tx: NewTx(&CeloDenominatedTx{ + ChainID: chainId, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gas, + To: &to, + Value: value, + Data: data, + AccessList: AccessList{ + { + Address: accessListAddress, + StorageKeys: []common.Hash{ + storageKey, + }, + }, + }, + FeeCurrency: &feeCurrency, + MaxFeeInFeeCurrency: maxFeeInFeeCurrency, + V: new(big.Int).SetBytes(v), + R: new(big.Int).SetBytes(r), + S: new(big.Int).SetBytes(s), + }), + bytes: append( + []byte{CeloDenominatedTxType}, + mustEncodeToBytes(t, []interface{}{ + chainId, + nonce, + gasTipCap, + gasFeeCap, + gas, + to, + value, + data, + []interface{}{ + []interface{}{ + accessListAddress, + []interface{}{ + storageKey, + }, + }, + }, + feeCurrency, + maxFeeInFeeCurrency, + v, + r, + s, + })..., + ), + }, + } + + testEncodeRLP := func(t *testing.T, tx *Transaction, expectedByte []byte) { + t.Helper() + + var buf bytes.Buffer + + err := tx.EncodeRLP(&buf) + assert.NoError(t, err) + + assert.Equal(t, expectedByte, buf.Bytes()) + } + + testMarshalBinary := func(t *testing.T, tx *Transaction, expectedByte []byte) { + t.Helper() + + data, err := tx.MarshalBinary() + + assert.NoError(t, err) + assert.Equal(t, expectedByte, data) + } + + testDecodeRLP := func(t *testing.T, expectedTx *Transaction, bytes []byte) { + t.Helper() + + tx := new(Transaction) + err := rlp.DecodeBytes(bytes, tx) + + assert.NoError(t, err) + assert.Equal(t, expectedTx.inner, tx.inner) + } + + testUnmarshalBinary := func(t *testing.T, expectedTx *Transaction, bytes []byte) { + t.Helper() + + tx := new(Transaction) + err := tx.UnmarshalBinary(bytes) + + assert.NoError(t, err) + assert.Equal(t, expectedTx.inner, tx.inner) + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%s should encode to valid RLP bytes", test.txType), func(t *testing.T) { + if _, ok := test.tx.inner.(*LegacyTx); ok { + testEncodeRLP(t, test.tx, test.bytes) + } else { + testEncodeRLP(t, test.tx, mustEncodeToBytes(t, test.bytes)) + } + }) + + t.Run(fmt.Sprintf("%s should marshal to valid RLP bytes", test.txType), func(t *testing.T) { + testMarshalBinary(t, test.tx, test.bytes) + }) + + t.Run(fmt.Sprintf("%s should decode RLP bytes to Transaction", test.txType), func(t *testing.T) { + if _, ok := test.tx.inner.(*LegacyTx); ok { + testDecodeRLP(t, test.tx, test.bytes) + } else { + testDecodeRLP(t, test.tx, mustEncodeToBytes(t, test.bytes)) + } + }) + + t.Run(fmt.Sprintf("%s should unmarshal valid RLP bytes to Transaction", test.txType), func(t *testing.T) { + testUnmarshalBinary(t, test.tx, test.bytes) + }) + } +} From 95797ff0438b8bdca871edff6b0b28ea9c235203 Mon Sep 17 00:00:00 2001 From: kourin Date: Wed, 8 Jan 2025 23:24:38 +0900 Subject: [PATCH 4/6] format and clean up unit tests for Celo Tx JSON marshaling/unmarshaling --- .../celo_transaction_marshalling_test.go | 197 +++++++++--------- 1 file changed, 103 insertions(+), 94 deletions(-) diff --git a/core/types/celo_transaction_marshalling_test.go b/core/types/celo_transaction_marshalling_test.go index 7a5727a31a..b6b60b2bc0 100644 --- a/core/types/celo_transaction_marshalling_test.go +++ b/core/types/celo_transaction_marshalling_test.go @@ -49,9 +49,18 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { NewEIP155Signer(chainId), ) - feeCurrencyAddress = common.HexToAddress("0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B") - toAddress = common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") + nonce = uint64(10) + gasPrice = big.NewInt(1e5) + gas = uint64(1e6) + feeCurrency = common.HexToAddress("0x2F25deB3848C207fc8E0c34035B3Ba7fC157602B") + maxFeeInFeeCurrency = big.NewInt(1e6) + gatewayFee = big.NewInt(1e7) gatewayFeeRecipient = common.HexToAddress("0x471EcE3750Da237f93B8E339c536989b8978a438") + to = common.HexToAddress("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045") + value = big.NewInt(1e8) + data = []byte{0x12, 0x34, 0x56, 0x78} + gasTipCap = big.NewInt(1) + gasFeeCap = big.NewInt(1e9) accessListAddress = common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7") storageKey = common.HexToHash("0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2") ) @@ -67,29 +76,29 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { txType: "Ethereum LegacyTx", isCeloTx: false, tx: MustSignNewTx(key, signer, &LegacyTx{ - Nonce: 10, - Gas: 1e6, - GasPrice: big.NewInt(1e7), - To: &toAddress, - Value: big.NewInt(1e8), - Data: []byte{0x11, 0x22, 0x33, 0x44}, + Nonce: nonce, + Gas: gas, + GasPrice: gasPrice, + To: &to, + Value: value, + Data: data, CeloLegacy: false, }), json: `{ "type": "0x0", "chainId": "0xa4ec", "nonce": "0xa", - "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "gasPrice": "0x186a0", "gas": "0xf4240", - "gasPrice": "0x989680", "maxPriorityFeePerGas": null, "maxFeePerGas": null, + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", "value": "0x5f5e100", - "input": "0x11223344", - "v": "0x149fc", - "r": "0x444416344542ecfd5824c0173395cca148cfa58cf7572d81196314ad4f5bf1f1", - "s": "0x23f6fd845489499c1170c8d0bd745f9fd3b99c2f4c979891b94e6764a03dcef0", - "hash": "0xf0051b6799141b669b18cf456ffb3509e089c00544878e74769819b345f00866" + "input": "0x12345678", + "v": "0x149fb", + "r": "0xdfa5f33872c59990dbbe5474e164492f8581d35e73cd8fc04aa6a90c68db8edd", + "s": "0x649e6ce1d912fec1df4db2433cad338b1426db45a8d4f17735ceb032fc889b91", + "hash": "0xd03cc6400e90416de0301727b1d9113426a732c880b379fad51738df8ce1db76" }`, requiredFields: []string{"nonce", "gas", "gasPrice", "value", "input", "v", "r", "s"}, }, @@ -97,36 +106,36 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { txType: "Celo LegacyTx", isCeloTx: true, tx: MustSignNewTx(key, signer, &LegacyTx{ - Nonce: 10, - Gas: 1e6, - GasPrice: big.NewInt(1e7), - FeeCurrency: &feeCurrencyAddress, + Nonce: nonce, + GasPrice: gasPrice, + Gas: gas, + FeeCurrency: &feeCurrency, GatewayFeeRecipient: &gatewayFeeRecipient, - GatewayFee: big.NewInt(1e7), - To: &toAddress, - Value: big.NewInt(1e8), - Data: []byte{0x11, 0x22, 0x33, 0x44}, + GatewayFee: gatewayFee, + To: &to, + Value: value, + Data: data, CeloLegacy: true, }), json: `{ "type": "0x0", "chainId": "0xa4ec", "nonce": "0xa", - "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "gasPrice": "0x186a0", "gas": "0xf4240", - "gasPrice": "0x989680", "maxPriorityFeePerGas": null, "maxFeePerGas": null, - "value": "0x5f5e100", - "input": "0x11223344", - "v": "0x149fb", - "r": "0x8ce12cd818c57c73354a1d18b5075bed356170f615246a5e4f7ac3a6a6c2d4c8", - "s": "0x64ab56cbf08dd6cf742f083084ed66167fc110192ee3365555e643f102b5cec7", - "hash": "0xfa30135c37ab29654ed0f75c07d6ba75cbb5d6739ab3249731ae80f80237bd5a", "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", - "ethCompatible": false, + "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438", "gatewayFee": "0x989680", - "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438" + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "value": "0x5f5e100", + "input": "0x12345678", + "v": "0x149fb", + "r": "0x7114020891fdc29e8f661593aaac8e732eb81600a305f8cbb2cc16a7c8e74344", + "s": "0x5ac556fc86adc0ae139d15d030d708e0d01bc3070a4555b18de911b76ec39766", + "hash": "0x98d9e286c319b9e349eef956d81e2377ab73bb603f21b950d0a78db638f1008e", + "ethCompatible": false }`, requiredFields: []string{"nonce", "gas", "gasPrice", "value", "input", "v", "r", "s"}, }, @@ -135,16 +144,16 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { isCeloTx: true, tx: MustSignNewTx(key, signer, &CeloDynamicFeeTx{ ChainID: chainId, - Nonce: 10, - GasTipCap: big.NewInt(1), - GasFeeCap: big.NewInt(5e9), - Gas: 500000, - FeeCurrency: &feeCurrencyAddress, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gas, + FeeCurrency: &feeCurrency, GatewayFeeRecipient: &gatewayFeeRecipient, - GatewayFee: big.NewInt(1e7), - To: &toAddress, - Value: big.NewInt(1e8), - Data: []byte{0x11, 0x22, 0x33, 0x44}, + GatewayFee: gatewayFee, + To: &to, + Value: value, + Data: data, AccessList: AccessList{ { Address: accessListAddress, @@ -158,13 +167,16 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { "type": "0x7c", "chainId": "0xa4ec", "nonce": "0xa", - "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", - "gas": "0x7a120", - "gasPrice": null, "maxPriorityFeePerGas": "0x1", - "maxFeePerGas": "0x12a05f200", + "maxFeePerGas": "0x3b9aca00", + "gasPrice": null, + "gas": "0xf4240", + "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", + "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438", + "gatewayFee": "0x989680", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", "value": "0x5f5e100", - "input": "0x11223344", + "input": "0x12345678", "accessList": [ { "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", @@ -172,14 +184,11 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { "0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2" ] } - ], - "v": "0x0", - "r": "0xe5c3c7490d804f15ab18a2c864eecd824cde653593c3e2bd1898d09bf0d59a51", - "s": "0x2c49f096c89f45dc0e0b24b7ce6e9b2b9cf13e7ab331e2e09c496080b4a6af2e", - "hash": "0x9a24ac05fb511923a40babf2826ff78ca71214f6315ef53476e7d3fd2aa51d18", - "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", - "gatewayFee": "0x989680", - "gatewayFeeRecipient": "0x471ece3750da237f93b8e339c536989b8978a438" + ], + "v": "0x1", + "r": "0x59156829e96e9bcac82a15e74dd9488adc24d86aee847943c6938bf4c7c5f8b2", + "s": "0x14f49fe32fdae94ec24d275b36ff75b97ab34b4270c4074814042d50504f4c74", + "hash": "0x36610786a28d7ae3b5e4d07210b3f287e0fa78aec4093bd1532ec9780ed260be" }`, requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s"}, }, @@ -187,15 +196,14 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { txType: "CeloDynamicFeeTxV2", isCeloTx: true, tx: MustSignNewTx(key, signer, &CeloDynamicFeeTxV2{ - ChainID: chainId, - Nonce: 10, - GasTipCap: big.NewInt(1), - GasFeeCap: big.NewInt(5e9), - Gas: 500000, - FeeCurrency: &feeCurrencyAddress, - To: &toAddress, - Value: big.NewInt(1e8), - Data: []byte{0x11, 0x22, 0x33, 0x44}, + ChainID: chainId, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gas, + To: &to, + Value: value, + Data: data, AccessList: AccessList{ { Address: accessListAddress, @@ -204,18 +212,19 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { }, }, }, + FeeCurrency: &feeCurrency, }), json: `{ "type": "0x7b", "chainId": "0xa4ec", "nonce": "0xa", - "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", - "gas": "0x7a120", - "gasPrice": null, "maxPriorityFeePerGas": "0x1", - "maxFeePerGas": "0x12a05f200", + "maxFeePerGas": "0x3b9aca00", + "gasPrice": null, + "gas": "0xf4240", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", "value": "0x5f5e100", - "input": "0x11223344", + "input": "0x12345678", "accessList": [ { "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", @@ -223,12 +232,12 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { "0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2" ] } - ], - "v": "0x0", - "r": "0xd0caa0257ad5e276c4164cc7cf033e95db5dc6f8c880c8b94aa132efd2378597", - "s": "0x63f213eb18899c862f1f9b84033f940bbc88de28f9af7a7c50b39e36896d1f51", - "hash": "0x8710502f18e464a4e44d6d660127c6173e1ba70ca5684e09d736d3b5e9e63e16", - "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b" + ], + "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", + "v": "0x1", + "r": "0x605b2b9125a17c346f4569382cb127695e92ebdeedbcf249ec86e98feb6da04", + "s": "0xd98bff12452ccf6268d63ead95bafa45d67020a9a35bce504ac68dbc1a49812", + "hash": "0x9d668d91172a442c72509003235c386d7a0b2bd29da4411271f8f58231d33611" }`, requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s"}, }, @@ -237,16 +246,14 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { isCeloTx: true, // Skip signing due to unsupported transaction type tx: NewTx(&CeloDenominatedTx{ - ChainID: chainId, - Nonce: 10, - GasTipCap: big.NewInt(1), - GasFeeCap: big.NewInt(5e9), - Gas: 500000, - FeeCurrency: &feeCurrencyAddress, - MaxFeeInFeeCurrency: big.NewInt(1e8), - To: &toAddress, - Value: big.NewInt(1e8), - Data: []byte{0x11, 0x22, 0x33, 0x44}, + ChainID: chainId, + Nonce: nonce, + GasTipCap: gasTipCap, + GasFeeCap: gasFeeCap, + Gas: gas, + To: &to, + Value: value, + Data: data, AccessList: AccessList{ { Address: accessListAddress, @@ -255,19 +262,20 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { }, }, }, + FeeCurrency: &feeCurrency, + MaxFeeInFeeCurrency: maxFeeInFeeCurrency, }), json: `{ "type": "0x7a", "chainId": "0xa4ec", "nonce": "0xa", - "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", - "gas": "0x7a120", - "gasPrice": null, "maxPriorityFeePerGas": "0x1", - "maxFeePerGas": "0x12a05f200", - "maxFeeInFeeCurrency": "0x5f5e100", + "maxFeePerGas": "0x3b9aca00", + "gasPrice": null, + "gas": "0xf4240", + "to": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", "value": "0x5f5e100", - "input": "0x11223344", + "input": "0x12345678", "accessList": [ { "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", @@ -275,12 +283,13 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { "0x2ab2bf4c5cabc3000e2502e33470a863db2755809d7561237424a0eb373154c2" ] } - ], + ], + "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b", + "maxFeeInFeeCurrency": "0xf4240", "v": "0x0", "r": "0x0", "s": "0x0", - "hash": "0x4812438d07f69839658264fd6bf9022ceb97e87f71ac7ebc56a463f550a8c065", - "feeCurrency": "0x2f25deb3848c207fc8e0c34035b3ba7fc157602b" + "hash": "0xefdbf85d98faa0f9e00c1060a0b27d12cca785ea5977367ec1187637c3005bf1" }`, requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s"}, }, From 87e007d16cde2b3b0b10d7de70760425b39f768b Mon Sep 17 00:00:00 2001 From: kourin Date: Wed, 8 Jan 2025 23:59:02 +0900 Subject: [PATCH 5/6] Replace assert.NoError with require.NoError --- core/types/celo_transaction_marshalling_test.go | 11 ++++++----- core/types/celo_transaction_rlp_test.go | 11 ++++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/core/types/celo_transaction_marshalling_test.go b/core/types/celo_transaction_marshalling_test.go index b6b60b2bc0..16df52bc2e 100644 --- a/core/types/celo_transaction_marshalling_test.go +++ b/core/types/celo_transaction_marshalling_test.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TestCeloTransactionMarshalUnmarshal tests that each Celo transactions marshal and unmarshal correctly @@ -300,10 +301,10 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { t.Helper() txJsonOuter, err := json.Marshal(tx) - assert.NoError(t, err) + require.NoError(t, err) txJsonInner, isCeloTx, err := celoTransactionMarshal(tx) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, isCeloTxType, isCeloTx) @@ -324,7 +325,7 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { tx := new(Transaction) err := json.Unmarshal([]byte(jsonData), tx) - assert.NoError(t, err) + require.NoError(t, err) // Reassign the signature values because *hexutil.Big decodes "0x0" as nil for the `abs` field. // This causes a mismatch with `big.NewInt(0)` @@ -347,12 +348,12 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { // Create a copy of the JSON data and remove one of the required fields var jsonMap map[string]interface{} err := json.Unmarshal([]byte(jsonData), &jsonMap) - assert.NoError(t, err) + require.NoError(t, err) delete(jsonMap, field) newJsonData, err := json.Marshal(jsonMap) - assert.NoError(t, err) + require.NoError(t, err) // Attempt to unmarshal the JSON data tx := new(Transaction) diff --git a/core/types/celo_transaction_rlp_test.go b/core/types/celo_transaction_rlp_test.go index 82dc5a5eb8..8aafb863a5 100644 --- a/core/types/celo_transaction_rlp_test.go +++ b/core/types/celo_transaction_rlp_test.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) // TestCeloTransactionRLPEncodingDecoding tests the RLP encoding and decoding of Celo transactions @@ -57,7 +58,7 @@ func TestCeloTransactionRLPEncodingDecoding(t *testing.T) { t.Helper() data, err := rlp.EncodeToBytes(value) - assert.NoError(t, err) + require.NoError(t, err) return data } @@ -264,7 +265,7 @@ func TestCeloTransactionRLPEncodingDecoding(t *testing.T) { var buf bytes.Buffer err := tx.EncodeRLP(&buf) - assert.NoError(t, err) + require.NoError(t, err) assert.Equal(t, expectedByte, buf.Bytes()) } @@ -273,8 +274,8 @@ func TestCeloTransactionRLPEncodingDecoding(t *testing.T) { t.Helper() data, err := tx.MarshalBinary() + require.NoError(t, err) - assert.NoError(t, err) assert.Equal(t, expectedByte, data) } @@ -283,8 +284,8 @@ func TestCeloTransactionRLPEncodingDecoding(t *testing.T) { tx := new(Transaction) err := rlp.DecodeBytes(bytes, tx) + require.NoError(t, err) - assert.NoError(t, err) assert.Equal(t, expectedTx.inner, tx.inner) } @@ -293,8 +294,8 @@ func TestCeloTransactionRLPEncodingDecoding(t *testing.T) { tx := new(Transaction) err := tx.UnmarshalBinary(bytes) + require.NoError(t, err) - assert.NoError(t, err) assert.Equal(t, expectedTx.inner, tx.inner) } From 1d0162ff52cf0635eb1380d7b1795ea89978d37f Mon Sep 17 00:00:00 2001 From: kourin Date: Tue, 14 Jan 2025 11:33:21 +0900 Subject: [PATCH 6/6] Adds checks for presence of feeCurrency and maxFeeInFeeCurrency fields during unmarshalling of CeloDenominatedTx --- core/types/celo_transaction_marshalling.go | 6 ++++++ core/types/celo_transaction_marshalling_test.go | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/core/types/celo_transaction_marshalling.go b/core/types/celo_transaction_marshalling.go index 719faea22c..672d13d732 100644 --- a/core/types/celo_transaction_marshalling.go +++ b/core/types/celo_transaction_marshalling.go @@ -341,7 +341,13 @@ func celoTransactionUnmarshal(dec txJSON, inner *TxData) (bool, error) { } } // Celo specific fields + if dec.FeeCurrency == nil { + return true, errors.New("missing required field 'feeCurrency' in transaction") + } itx.FeeCurrency = dec.FeeCurrency + if dec.MaxFeeInFeeCurrency == nil { + return true, errors.New("missing required field 'maxFeeInFeeCurrency' in transaction") + } itx.MaxFeeInFeeCurrency = (*big.Int)(dec.MaxFeeInFeeCurrency) default: return false, nil diff --git a/core/types/celo_transaction_marshalling_test.go b/core/types/celo_transaction_marshalling_test.go index 16df52bc2e..acc2cb79be 100644 --- a/core/types/celo_transaction_marshalling_test.go +++ b/core/types/celo_transaction_marshalling_test.go @@ -292,7 +292,7 @@ func TestCeloTransactionMarshalUnmarshal(t *testing.T) { "s": "0x0", "hash": "0xefdbf85d98faa0f9e00c1060a0b27d12cca785ea5977367ec1187637c3005bf1" }`, - requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s"}, + requiredFields: []string{"chainId", "nonce", "gas", "maxPriorityFeePerGas", "maxFeePerGas", "value", "input", "v", "r", "s", "feeCurrency", "maxFeeInFeeCurrency"}, }, }