Skip to content

Commit e4f570f

Browse files
authored
core/types: add MarshalBinary, UnmarshalBinary for Receipt (#22806)
1 parent f9d683b commit e4f570f

File tree

2 files changed

+223
-19
lines changed

2 files changed

+223
-19
lines changed

core/types/receipt.go

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,29 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
144144
buf := encodeBufferPool.Get().(*bytes.Buffer)
145145
defer encodeBufferPool.Put(buf)
146146
buf.Reset()
147-
buf.WriteByte(r.Type)
148-
if err := rlp.Encode(buf, data); err != nil {
147+
if err := r.encodeTyped(data, buf); err != nil {
149148
return err
150149
}
151150
return rlp.Encode(w, buf.Bytes())
152151
}
153152

153+
// encodeTyped writes the canonical encoding of a typed receipt to w.
154+
func (r *Receipt) encodeTyped(data *receiptRLP, w *bytes.Buffer) error {
155+
w.WriteByte(r.Type)
156+
return rlp.Encode(w, data)
157+
}
158+
159+
// MarshalBinary returns the consensus encoding of the receipt.
160+
func (r *Receipt) MarshalBinary() ([]byte, error) {
161+
if r.Type == LegacyTxType {
162+
return rlp.EncodeToBytes(r)
163+
}
164+
data := &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}
165+
var buf bytes.Buffer
166+
err := r.encodeTyped(data, &buf)
167+
return buf.Bytes(), err
168+
}
169+
154170
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
155171
// from an RLP stream.
156172
func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
@@ -189,6 +205,42 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
189205
}
190206
}
191207

208+
// UnmarshalBinary decodes the consensus encoding of receipts.
209+
// It supports legacy RLP receipts and EIP-2718 typed receipts.
210+
func (r *Receipt) UnmarshalBinary(b []byte) error {
211+
if len(b) > 0 && b[0] > 0x7f {
212+
// It's a legacy receipt decode the RLP
213+
var data receiptRLP
214+
err := rlp.DecodeBytes(b, &data)
215+
if err != nil {
216+
return err
217+
}
218+
r.Type = LegacyTxType
219+
return r.setFromRLP(data)
220+
}
221+
// It's an EIP2718 typed transaction envelope.
222+
return r.decodeTyped(b)
223+
}
224+
225+
// decodeTyped decodes a typed receipt from the canonical format.
226+
func (r *Receipt) decodeTyped(b []byte) error {
227+
if len(b) == 0 {
228+
return errEmptyTypedReceipt
229+
}
230+
switch b[0] {
231+
case DynamicFeeTxType, AccessListTxType:
232+
var data receiptRLP
233+
err := rlp.DecodeBytes(b[1:], &data)
234+
if err != nil {
235+
return err
236+
}
237+
r.Type = b[0]
238+
return r.setFromRLP(data)
239+
default:
240+
return ErrTxTypeNotSupported
241+
}
242+
}
243+
192244
func (r *Receipt) setFromRLP(data receiptRLP) error {
193245
r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs
194246
return r.setStatus(data.PostStateOrStatus)
@@ -354,42 +406,42 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
354406

355407
// DeriveFields fills the receipts with their computed fields based on consensus
356408
// data and contextual infos like containing block and transactions.
357-
func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
409+
func (rs Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
358410
signer := MakeSigner(config, new(big.Int).SetUint64(number))
359411

360412
logIndex := uint(0)
361-
if len(txs) != len(r) {
413+
if len(txs) != len(rs) {
362414
return errors.New("transaction and receipt count mismatch")
363415
}
364-
for i := 0; i < len(r); i++ {
416+
for i := 0; i < len(rs); i++ {
365417
// The transaction type and hash can be retrieved from the transaction itself
366-
r[i].Type = txs[i].Type()
367-
r[i].TxHash = txs[i].Hash()
418+
rs[i].Type = txs[i].Type()
419+
rs[i].TxHash = txs[i].Hash()
368420

369421
// block location fields
370-
r[i].BlockHash = hash
371-
r[i].BlockNumber = new(big.Int).SetUint64(number)
372-
r[i].TransactionIndex = uint(i)
422+
rs[i].BlockHash = hash
423+
rs[i].BlockNumber = new(big.Int).SetUint64(number)
424+
rs[i].TransactionIndex = uint(i)
373425

374426
// The contract address can be derived from the transaction itself
375427
if txs[i].To() == nil {
376428
// Deriving the signer is expensive, only do if it's actually needed
377429
from, _ := Sender(signer, txs[i])
378-
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
430+
rs[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
379431
}
380432
// The used gas can be calculated based on previous r
381433
if i == 0 {
382-
r[i].GasUsed = r[i].CumulativeGasUsed
434+
rs[i].GasUsed = rs[i].CumulativeGasUsed
383435
} else {
384-
r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed
436+
rs[i].GasUsed = rs[i].CumulativeGasUsed - rs[i-1].CumulativeGasUsed
385437
}
386438
// The derived log fields can simply be set from the block and transaction
387-
for j := 0; j < len(r[i].Logs); j++ {
388-
r[i].Logs[j].BlockNumber = number
389-
r[i].Logs[j].BlockHash = hash
390-
r[i].Logs[j].TxHash = r[i].TxHash
391-
r[i].Logs[j].TxIndex = uint(i)
392-
r[i].Logs[j].Index = logIndex
439+
for j := 0; j < len(rs[i].Logs); j++ {
440+
rs[i].Logs[j].BlockNumber = number
441+
rs[i].Logs[j].BlockHash = hash
442+
rs[i].Logs[j].TxHash = rs[i].TxHash
443+
rs[i].Logs[j].TxIndex = uint(i)
444+
rs[i].Logs[j].Index = logIndex
393445
logIndex++
394446
}
395447
}

core/types/receipt_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,59 @@ import (
2929
"github.com/ethereum/go-ethereum/rlp"
3030
)
3131

32+
var (
33+
legacyReceipt = &Receipt{
34+
Status: ReceiptStatusFailed,
35+
CumulativeGasUsed: 1,
36+
Logs: []*Log{
37+
{
38+
Address: common.BytesToAddress([]byte{0x11}),
39+
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
40+
Data: []byte{0x01, 0x00, 0xff},
41+
},
42+
{
43+
Address: common.BytesToAddress([]byte{0x01, 0x11}),
44+
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
45+
Data: []byte{0x01, 0x00, 0xff},
46+
},
47+
},
48+
}
49+
accessListReceipt = &Receipt{
50+
Status: ReceiptStatusFailed,
51+
CumulativeGasUsed: 1,
52+
Logs: []*Log{
53+
{
54+
Address: common.BytesToAddress([]byte{0x11}),
55+
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
56+
Data: []byte{0x01, 0x00, 0xff},
57+
},
58+
{
59+
Address: common.BytesToAddress([]byte{0x01, 0x11}),
60+
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
61+
Data: []byte{0x01, 0x00, 0xff},
62+
},
63+
},
64+
Type: AccessListTxType,
65+
}
66+
eip1559Receipt = &Receipt{
67+
Status: ReceiptStatusFailed,
68+
CumulativeGasUsed: 1,
69+
Logs: []*Log{
70+
{
71+
Address: common.BytesToAddress([]byte{0x11}),
72+
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
73+
Data: []byte{0x01, 0x00, 0xff},
74+
},
75+
{
76+
Address: common.BytesToAddress([]byte{0x01, 0x11}),
77+
Topics: []common.Hash{common.HexToHash("dead"), common.HexToHash("beef")},
78+
Data: []byte{0x01, 0x00, 0xff},
79+
},
80+
},
81+
Type: DynamicFeeTxType,
82+
}
83+
)
84+
3285
func TestDecodeEmptyTypedReceipt(t *testing.T) {
3386
input := []byte{0x80}
3487
var r Receipt
@@ -312,6 +365,105 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
312365
}
313366
}
314367

368+
func TestReceiptMarshalBinary(t *testing.T) {
369+
// Legacy Receipt
370+
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
371+
have, err := legacyReceipt.MarshalBinary()
372+
if err != nil {
373+
t.Fatalf("marshal binary error: %v", err)
374+
}
375+
legacyReceipts := Receipts{legacyReceipt}
376+
buf := new(bytes.Buffer)
377+
legacyReceipts.EncodeIndex(0, buf)
378+
haveEncodeIndex := buf.Bytes()
379+
if !bytes.Equal(have, haveEncodeIndex) {
380+
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
381+
}
382+
buf.Reset()
383+
if err := legacyReceipt.EncodeRLP(buf); err != nil {
384+
t.Fatalf("encode rlp error: %v", err)
385+
}
386+
haveRLPEncode := buf.Bytes()
387+
if !bytes.Equal(have, haveRLPEncode) {
388+
t.Errorf("BinaryMarshal and EncodeRLP mismatch for legacy tx, got %x want %x", have, haveRLPEncode)
389+
}
390+
legacyWant := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
391+
if !bytes.Equal(have, legacyWant) {
392+
t.Errorf("encoded RLP mismatch, got %x want %x", have, legacyWant)
393+
}
394+
395+
// 2930 Receipt
396+
buf.Reset()
397+
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
398+
have, err = accessListReceipt.MarshalBinary()
399+
if err != nil {
400+
t.Fatalf("marshal binary error: %v", err)
401+
}
402+
accessListReceipts := Receipts{accessListReceipt}
403+
accessListReceipts.EncodeIndex(0, buf)
404+
haveEncodeIndex = buf.Bytes()
405+
if !bytes.Equal(have, haveEncodeIndex) {
406+
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
407+
}
408+
accessListWant := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
409+
if !bytes.Equal(have, accessListWant) {
410+
t.Errorf("encoded RLP mismatch, got %x want %x", have, accessListWant)
411+
}
412+
413+
// 1559 Receipt
414+
buf.Reset()
415+
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
416+
have, err = eip1559Receipt.MarshalBinary()
417+
if err != nil {
418+
t.Fatalf("marshal binary error: %v", err)
419+
}
420+
eip1559Receipts := Receipts{eip1559Receipt}
421+
eip1559Receipts.EncodeIndex(0, buf)
422+
haveEncodeIndex = buf.Bytes()
423+
if !bytes.Equal(have, haveEncodeIndex) {
424+
t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
425+
}
426+
eip1559Want := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
427+
if !bytes.Equal(have, eip1559Want) {
428+
t.Errorf("encoded RLP mismatch, got %x want %x", have, eip1559Want)
429+
}
430+
}
431+
432+
func TestReceiptUnmarshalBinary(t *testing.T) {
433+
// Legacy Receipt
434+
legacyBinary := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
435+
gotLegacyReceipt := new(Receipt)
436+
if err := gotLegacyReceipt.UnmarshalBinary(legacyBinary); err != nil {
437+
t.Fatalf("unmarshal binary error: %v", err)
438+
}
439+
legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
440+
if !reflect.DeepEqual(gotLegacyReceipt, legacyReceipt) {
441+
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotLegacyReceipt, legacyReceipt)
442+
}
443+
444+
// 2930 Receipt
445+
accessListBinary := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
446+
gotAccessListReceipt := new(Receipt)
447+
if err := gotAccessListReceipt.UnmarshalBinary(accessListBinary); err != nil {
448+
t.Fatalf("unmarshal binary error: %v", err)
449+
}
450+
accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
451+
if !reflect.DeepEqual(gotAccessListReceipt, accessListReceipt) {
452+
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", gotAccessListReceipt, accessListReceipt)
453+
}
454+
455+
// 1559 Receipt
456+
eip1559RctBinary := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
457+
got1559Receipt := new(Receipt)
458+
if err := got1559Receipt.UnmarshalBinary(eip1559RctBinary); err != nil {
459+
t.Fatalf("unmarshal binary error: %v", err)
460+
}
461+
eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
462+
if !reflect.DeepEqual(got1559Receipt, eip1559Receipt) {
463+
t.Errorf("receipt unmarshalled from binary mismatch, got %v want %v", got1559Receipt, eip1559Receipt)
464+
}
465+
}
466+
315467
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
316468
t.Helper()
317469

0 commit comments

Comments
 (0)