Skip to content

Commit

Permalink
Fix celo dynamic tx receipt encoding (#159)
Browse files Browse the repository at this point in the history
* Fix CeloDynamicFeeTx/V2 receipt encode/decode

The old code assumed that CeloDynamicFeeTxV2Type receipts had a base fee
defined, that is not the case for migrated receipts that have no base
fee. So the base fee was made optional to allow decoding migrated
CeloDynamicFeeTxV2Type receipts. Also we don't try to derive fields
using nil base fees from migrated CeloDynamicFeeTxV2Type receipts.

Also CeloDynamicFeeTxType receipts were not being handled in receipt
decodeing paths, so would throw an unknown type error.

* Add encode/decode test for CeloDynamicFeeTxV2 receipt
  • Loading branch information
piersy authored Jul 1, 2024
1 parent 93a4c1b commit 7ef25ef
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 7 deletions.
103 changes: 103 additions & 0 deletions core/types/celo_receipt_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
14 changes: 7 additions & 7 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 7ef25ef

Please sign in to comment.