diff --git a/core/types/celo_receipt_test.go b/core/types/celo_receipt_test.go new file mode 100644 index 0000000000..297e8504ae --- /dev/null +++ b/core/types/celo_receipt_test.go @@ -0,0 +1,103 @@ +package types + +import ( + "bytes" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/rlp" + "github.com/stretchr/testify/require" +) + +func TestCeloDynamicFeeTxReceiptEncodeDecode(t *testing.T) { + checkEncodeDecodeConsistency(createTypedReceipt(CeloDynamicFeeTxType), t) +} + +func TestCeloDynamicFeeTxV2ReceiptEncodeDecode(t *testing.T) { + t.Run("NoBaseFee", func(t *testing.T) { + checkEncodeDecodeConsistency(createTypedReceipt(CeloDynamicFeeTxV2Type), t) + }) + + t.Run("WithBaseFee", func(t *testing.T) { + r := createTypedReceipt(CeloDynamicFeeTxV2Type) + r.BaseFee = big.NewInt(1000) + checkEncodeDecodeConsistency(r, t) + }) +} + +func createTypedReceipt(receiptType uint8) *Receipt { + // Note this receipt and logs lack lots of fields, those fields are derived from the + // block and transaction and so are not part of encoding/decoding. + r := &Receipt{ + Type: receiptType, + PostState: common.Hash{3}.Bytes(), + CumulativeGasUsed: 6, + Logs: []*Log{ + { + Address: common.BytesToAddress([]byte{0x33}), + Topics: []common.Hash{common.HexToHash("dead")}, + Data: []byte{0x01, 0x02, 0x03}, + }, + { + Address: common.BytesToAddress([]byte{0x03, 0x33}), + Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")}, + Data: []byte{0x01, 0x02}, + }, + }, + } + r.Bloom = CreateBloom(Receipts{r}) + return r +} + +// checkEncodeDecodeConsistency checks both RLP and binary encoding/decoding consistency. +func checkEncodeDecodeConsistency(r *Receipt, t *testing.T) { + checkRLPEncodeDecodeConsistency(r, t) + checkStorageRLPEncodeDecodeConsistency((*ReceiptForStorage)(r), t) + checkBinaryEncodeDecodeConsistency(r, t) +} + +// checkRLPEncodeDecodeConsistency encodes and decodes the receipt and checks that they are equal. +func checkRLPEncodeDecodeConsistency(r *Receipt, t *testing.T) { + buf := new(bytes.Buffer) + err := rlp.Encode(buf, r) + require.NoError(t, err) + + var r2 Receipt + err = rlp.Decode(buf, &r2) + require.NoError(t, err) + + require.EqualValues(t, r, &r2) +} + +// checkRLPEncodeDecodeConsistency encodes and decodes the receipt and checks that they are equal. +func checkBinaryEncodeDecodeConsistency(r *Receipt, t *testing.T) { + bytes, err := r.MarshalBinary() + require.NoError(t, err) + + r2 := &Receipt{} + err = r2.UnmarshalBinary(bytes) + require.NoError(t, err) + + require.EqualValues(t, r, r2) +} + +// checkStorageRLPEncodeDecodeConsistency encodes and decodes the receipt and checks that they are equal. +func checkStorageRLPEncodeDecodeConsistency(r *ReceiptForStorage, t *testing.T) { + buf := new(bytes.Buffer) + err := rlp.Encode(buf, r) + require.NoError(t, err) + + // Stored receipts do not encode the type, (although they do require it to be set during encoding) + // since it is derived from the associated transaction. So for the sake of the comparison we set it + // to 0 and restore it after the comparison. + receiptType := r.Type + defer func() { r.Type = receiptType }() + r.Type = 0 + + var r2 ReceiptForStorage + err = rlp.Decode(buf, &r2) + require.NoError(t, err) + + require.EqualValues(t, r, &r2) +} diff --git a/core/types/receipt.go b/core/types/receipt.go index 58984c056f..5344814249 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -151,7 +151,7 @@ type celoDynamicReceiptRLP struct { Bloom Bloom Logs []*Log // BaseFee was introduced as mandatory in Cel2 ONLY for the CeloDynamicFeeTxs - BaseFee *big.Int + BaseFee *big.Int `rlp:"optional"` } // storedReceiptRLP is the storage encoding of a receipt. @@ -173,7 +173,7 @@ type celoDynamicFeeStoredReceiptRLP struct { PostStateOrStatus []byte CumulativeGasUsed uint64 Logs []*Log - BaseFee *big.Int + BaseFee *big.Int `rlp:"optional"` } // LegacyOptimismStoredReceiptRLP is the pre bedrock storage encoding of a @@ -354,7 +354,7 @@ func (r *Receipt) decodeTyped(b []byte) error { return errShortTypedReceipt } switch b[0] { - case DynamicFeeTxType, AccessListTxType, BlobTxType: + case DynamicFeeTxType, AccessListTxType, BlobTxType, CeloDynamicFeeTxType: var data receiptRLP err := rlp.DecodeBytes(b[1:], &data) if err != nil { @@ -455,7 +455,7 @@ func (r *ReceiptForStorage) EncodeRLP(_w io.Writer) error { w.WriteUint64(*r.DepositReceiptVersion) } } - if r.Type == CeloDynamicFeeTxV2Type { + if r.Type == CeloDynamicFeeTxV2Type && r.BaseFee != nil { w.WriteBigInt(r.BaseFee) } w.ListEnd(outerList) @@ -574,7 +574,7 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) { } w.WriteByte(r.Type) switch r.Type { - case AccessListTxType, DynamicFeeTxType, BlobTxType: + case AccessListTxType, DynamicFeeTxType, BlobTxType, CeloDynamicFeeTxType: rlp.Encode(w, data) case CeloDynamicFeeTxV2Type: celoDynamicData := &celoDynamicReceiptRLP{data.PostStateOrStatus, data.CumulativeGasUsed, data.Bloom, data.Logs, r.BaseFee} @@ -610,8 +610,8 @@ func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, nu rs[i].Type = txs[i].Type() rs[i].TxHash = txs[i].Hash() - // The CeloDynamicFeeTxs set the baseFee in the receipt - if txs[i].Type() != CeloDynamicFeeTxV2Type { + // The post transition CeloDynamicFeeV2Txs set the baseFee in the receipt + if rs[i].BaseFee == nil { rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), baseFee) } else { rs[i].EffectiveGasPrice = txs[i].inner.effectiveGasPrice(new(big.Int), rs[i].BaseFee)