eth/protocols/eth: reimplement eth/68 receipts

This commit is contained in:
Felix Lange 2025-04-16 17:02:01 +02:00
parent 6b193040bd
commit 12eade0fef
7 changed files with 363 additions and 144 deletions

View file

@ -176,7 +176,7 @@ var eth68 = map[uint64]msgHandler{
GetBlockBodiesMsg: handleGetBlockBodies, GetBlockBodiesMsg: handleGetBlockBodies,
BlockBodiesMsg: handleBlockBodies, BlockBodiesMsg: handleBlockBodies,
GetReceiptsMsg: handleGetReceipts68, GetReceiptsMsg: handleGetReceipts68,
ReceiptsMsg: handleReceipts68, ReceiptsMsg: handleReceipts[*ReceiptList68],
GetPooledTransactionsMsg: handleGetPooledTransactions, GetPooledTransactionsMsg: handleGetPooledTransactions,
PooledTransactionsMsg: handlePooledTransactions, PooledTransactionsMsg: handlePooledTransactions,
} }
@ -189,7 +189,7 @@ var eth69 = map[uint64]msgHandler{
GetBlockBodiesMsg: handleGetBlockBodies, GetBlockBodiesMsg: handleGetBlockBodies,
BlockBodiesMsg: handleBlockBodies, BlockBodiesMsg: handleBlockBodies,
GetReceiptsMsg: handleGetReceipts69, GetReceiptsMsg: handleGetReceipts69,
ReceiptsMsg: handleReceipts69, ReceiptsMsg: handleReceipts[*ReceiptList69],
GetPooledTransactionsMsg: handleGetPooledTransactions, GetPooledTransactionsMsg: handleGetPooledTransactions,
PooledTransactionsMsg: handlePooledTransactions, PooledTransactionsMsg: handlePooledTransactions,
BlockRangeUpdateMsg: handleBlockRangeUpdate, BlockRangeUpdateMsg: handleBlockRangeUpdate,

View file

@ -529,22 +529,23 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
// Collect the hashes to request, and the response to expect // Collect the hashes to request, and the response to expect
var ( var (
hashes []common.Hash hashes []common.Hash
receipts []types.Receipts receipts []*ReceiptList68
) )
for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ { for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ {
block := backend.chain.GetBlockByNumber(i) block := backend.chain.GetBlockByNumber(i)
hashes = append(hashes, block.Hash()) hashes = append(hashes, block.Hash())
receipts = append(receipts, backend.chain.GetReceiptsByHash(block.Hash())) trs := backend.chain.GetReceiptsByHash(block.Hash())
receipts = append(receipts, NewReceiptList68(trs))
} }
// Send the hash request and verify the response // Send the hash request and verify the response
p2p.Send(peer.app, GetReceiptsMsg, &GetReceiptsPacket{ p2p.Send(peer.app, GetReceiptsMsg, &GetReceiptsPacket{
RequestId: 123, RequestId: 123,
GetReceiptsRequest: hashes, GetReceiptsRequest: hashes,
}) })
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, &ReceiptsPacket{ if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, &ReceiptsPacket[*ReceiptList68]{
RequestId: 123, RequestId: 123,
ReceiptsResponse: receipts, List: receipts,
}); err != nil { }); err != nil {
t.Errorf("receipts mismatch: %v", err) t.Errorf("receipts mismatch: %v", err)
} }

View file

@ -282,19 +282,25 @@ func ServiceGetReceiptsQuery68(chain *core.BlockChain, query GetReceiptsRequest)
break break
} }
// Retrieve the requested block's receipts // Retrieve the requested block's receipts
results := chain.GetReceiptsByHash(hash) results := chain.GetRawReceiptsByHash(hash)
if results == nil { if results == nil {
if header := chain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { if header := chain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue continue
} }
}
// If known, encode and queue for response packet
if encoded, err := rlp.EncodeToBytes(results); err != nil {
log.Error("Failed to encode receipt", "err", err)
} else { } else {
receipts = append(receipts, encoded) body := chain.GetBodyRLP(hash)
bytes += len(encoded) if body == nil {
continue
}
var err error
results, err = blockReceiptsToNetwork68(results, body)
if err != nil {
log.Error("Error in block receipts conversion", "hash", hash, "err", err)
continue
}
} }
receipts = append(receipts, results)
bytes += len(results)
} }
return receipts return receipts
} }
@ -324,7 +330,7 @@ func serviceGetReceiptsQuery69(chain *core.BlockChain, query GetReceiptsRequest)
continue continue
} }
var err error var err error
results, err = blockReceiptsToNetwork(results, body) results, err = blockReceiptsToNetwork69(results, body)
if err != nil { if err != nil {
log.Error("Error in block receipts conversion", "hash", hash, "err", err) log.Error("Error in block receipts conversion", "hash", hash, "err", err)
continue continue
@ -393,53 +399,30 @@ func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
}, metadata) }, metadata)
} }
func handleReceipts68(backend Backend, msg Decoder, peer *Peer) error { func handleReceipts[L ReceiptsList](backend Backend, msg Decoder, peer *Peer) error {
// A batch of receipts arrived to one of our previous requests // A batch of receipts arrived to one of our previous requests
res := new(ReceiptsPacket) res := new(ReceiptsPacket[L])
if err := msg.Decode(res); err != nil { if err := msg.Decode(res); err != nil {
return err return err
} }
metadata := func() interface{} {
hasher := trie.NewStackTrie(nil)
hashes := make([]common.Hash, len(res.ReceiptsResponse))
for i, receipt := range res.ReceiptsResponse {
hashes[i] = types.DeriveSha(receipt, hasher)
}
return hashes
}
encoded := ReceiptsRLPResponse(types.ReceiptsToRLP(res.ReceiptsResponse))
return peer.dispatchResponse(&Response{
id: res.RequestId,
code: ReceiptsMsg,
Res: &encoded,
}, metadata)
}
func handleReceipts69(backend Backend, msg Decoder, peer *Peer) error {
// A batch of receipts arrived to one of our previous requests
var res ReceiptsPacket69
if err := msg.Decode(&res); err != nil {
return err
}
// Assign temporary hashing buffer to each list item, the same buffer is shared // Assign temporary hashing buffer to each list item, the same buffer is shared
// between all receipt list instances. // between all receipt list instances.
buffers := new(receiptListBuffers) buffers := new(receiptListBuffers)
for i := range res.List { for i := range res.List {
res.List[i].buf = buffers res.List[i].setBuffers(buffers)
} }
metadata := func() any {
metadata := func() interface{} {
hasher := trie.NewStackTrie(nil) hasher := trie.NewStackTrie(nil)
hashes := make([]common.Hash, len(res.List)) hashes := make([]common.Hash, len(res.List))
for i := range res.List { for i := range res.List {
hashes[i] = types.DeriveSha(&res.List[i], hasher) hashes[i] = types.DeriveSha(res.List[i], hasher)
} }
return hashes return hashes
} }
// TODO this can probably be made 0-alloc.
var enc ReceiptsRLPResponse var enc ReceiptsRLPResponse
for i := range res.List { for i := range res.List {
enc = append(enc, res.List[i].toStorageReceiptsRLP()) enc = append(enc, res.List[i].EncodeForStorage())
} }
return peer.dispatchResponse(&Response{ return peer.dispatchResponse(&Response{
id: res.RequestId, id: res.RequestId,

View file

@ -265,18 +265,19 @@ type GetReceiptsPacket struct {
// ReceiptsResponse is the network packet for block receipts distribution. // ReceiptsResponse is the network packet for block receipts distribution.
type ReceiptsResponse []types.Receipts type ReceiptsResponse []types.Receipts
// ReceiptsPacket is the network packet for block receipts distribution with // ReceiptsList is a type constraint for block receceipt list types.
// request ID wrapping. type ReceiptsList interface {
type ReceiptsPacket struct { *ReceiptList68 | *ReceiptList69
RequestId uint64 setBuffers(*receiptListBuffers)
ReceiptsResponse EncodeForStorage() rlp.RawValue
types.DerivableList
} }
// ReceiptsPacket is the network packet for block receipts distribution with // ReceiptsPacket is the network packet for block receipts distribution with
// request ID wrapping. // request ID wrapping.
type ReceiptsPacket69 struct { type ReceiptsPacket[L ReceiptsList] struct {
RequestId uint64 RequestId uint64
List []ReceiptList69 List []L
} }
// ReceiptsRLPResponse is used for receipts, when we already have it encoded // ReceiptsRLPResponse is used for receipts, when we already have it encoded

View file

@ -75,7 +75,7 @@ func TestEmptyMessages(t *testing.T) {
// All empty messages encodes to the same format // All empty messages encodes to the same format
want := common.FromHex("c4820457c0") want := common.FromHex("c4820457c0")
for i, msg := range []interface{}{ for i, msg := range []any{
// Headers // Headers
GetBlockHeadersPacket{1111, nil}, GetBlockHeadersPacket{1111, nil},
BlockHeadersPacket{1111, nil}, BlockHeadersPacket{1111, nil},
@ -85,7 +85,6 @@ func TestEmptyMessages(t *testing.T) {
BlockBodiesRLPPacket{1111, nil}, BlockBodiesRLPPacket{1111, nil},
// Receipts // Receipts
GetReceiptsPacket{1111, nil}, GetReceiptsPacket{1111, nil},
ReceiptsPacket{1111, nil},
// Transactions // Transactions
GetPooledTransactionsPacket{1111, nil}, GetPooledTransactionsPacket{1111, nil},
PooledTransactionsPacket{1111, nil}, PooledTransactionsPacket{1111, nil},
@ -99,7 +98,8 @@ func TestEmptyMessages(t *testing.T) {
BlockBodiesRLPPacket{1111, BlockBodiesRLPResponse([]rlp.RawValue{})}, BlockBodiesRLPPacket{1111, BlockBodiesRLPResponse([]rlp.RawValue{})},
// Receipts // Receipts
GetReceiptsPacket{1111, GetReceiptsRequest([]common.Hash{})}, GetReceiptsPacket{1111, GetReceiptsRequest([]common.Hash{})},
ReceiptsPacket{1111, ReceiptsResponse([]types.Receipts{})}, ReceiptsPacket[*ReceiptList68]{1111, []*ReceiptList68{}},
ReceiptsPacket[*ReceiptList69]{1111, []*ReceiptList69{}},
// Transactions // Transactions
GetPooledTransactionsPacket{1111, GetPooledTransactionsRequest([]common.Hash{})}, GetPooledTransactionsPacket{1111, GetPooledTransactionsRequest([]common.Hash{})},
PooledTransactionsPacket{1111, PooledTransactionsResponse([]*types.Transaction{})}, PooledTransactionsPacket{1111, PooledTransactionsResponse([]*types.Transaction{})},
@ -168,7 +168,7 @@ func TestMessages(t *testing.T) {
receipts = []*types.Receipt{ receipts = []*types.Receipt{
{ {
Status: types.ReceiptStatusFailed, Status: types.ReceiptStatusFailed,
CumulativeGasUsed: 1, CumulativeGasUsed: 333,
Logs: []*types.Log{ Logs: []*types.Log{
{ {
Address: common.BytesToAddress([]byte{0x11}), Address: common.BytesToAddress([]byte{0x11}),
@ -176,11 +176,21 @@ func TestMessages(t *testing.T) {
Data: []byte{0x01, 0x00, 0xff}, Data: []byte{0x01, 0x00, 0xff},
}, },
}, },
TxHash: hashes[0], },
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), {
GasUsed: 111111, Status: types.ReceiptStatusSuccessful,
CumulativeGasUsed: 444,
Logs: []*types.Log{
{
Address: common.BytesToAddress([]byte{0x22}),
Topics: []common.Hash{common.HexToHash("05668"), common.HexToHash("9773")},
Data: []byte{0x02, 0x0f, 0x0f, 0x0f, 0x06, 0x08},
},
},
}, },
} }
miniDeriveFields(receipts[0], 0)
miniDeriveFields(receipts[1], 1)
rlpData, err := rlp.EncodeToBytes(receipts) rlpData, err := rlp.EncodeToBytes(receipts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -221,12 +231,17 @@ func TestMessages(t *testing.T) {
common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"), common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"),
}, },
{ {
ReceiptsPacket{1111, ReceiptsResponse([]types.Receipts{receipts})}, ReceiptsPacket[*ReceiptList68]{1111, []*ReceiptList68{NewReceiptList68(receipts)}},
common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), common.FromHex("f902e6820457f902e0f902ddf901688082014db9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ffb9016f01f9016b018201bcb9010000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000001000000000000000000000000000000000000000000000000040000000000000000000000000004000000000000000000000000000000000000000000000000000000008000400000000000000000000000000000000000000000000000000000000000000000000000000000040f862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"),
}, },
{ {
// Identical to the eth/68 encoding above.
ReceiptsRLPPacket{1111, ReceiptsRLPResponse([]rlp.RawValue{receiptsRlp})}, ReceiptsRLPPacket{1111, ReceiptsRLPResponse([]rlp.RawValue{receiptsRlp})},
common.FromHex("f90172820457f9016cf90169f901668001b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff"), common.FromHex("f902e6820457f902e0f902ddf901688082014db9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ffb9016f01f9016b018201bcb9010000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000001000000000000000000000000000000000000000000000000040000000000000000000000000004000000000000000000000000000000000000000000000000000000008000400000000000000000000000000000000000000000000000000000000000000000000000000000040f862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"),
},
{
ReceiptsPacket[*ReceiptList69]{1111, []*ReceiptList69{NewReceiptList69(receipts)}},
common.FromHex("f8da820457f8d5f8d3f866808082014df85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff86901018201bcf862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"),
}, },
{ {
GetPooledTransactionsPacket{1111, GetPooledTransactionsRequest(hashes)}, GetPooledTransactionsPacket{1111, GetPooledTransactionsRequest(hashes)},

View file

@ -19,13 +19,18 @@ package eth
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"iter" "iter"
"math/big"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
// Receipt is the eth/69 network encoding of a receipt. // This is just a sanity limit for the size of a single receipt.
const maxReceiptSize = 16 * 1024 * 1024
// Receipt is the representation of receipts for networking purposes.
type Receipt struct { type Receipt struct {
TxType byte TxType byte
PostStateOrStatus []byte PostStateOrStatus []byte
@ -33,38 +38,91 @@ type Receipt struct {
Logs rlp.RawValue Logs rlp.RawValue
} }
func (r *Receipt) DecodeRLP(s *rlp.Stream) error { func newReceipt(tr *types.Receipt) Receipt {
if _, err := s.List(); err != nil { r := Receipt{TxType: tr.Type, GasUsed: tr.CumulativeGasUsed}
if tr.PostState != nil {
r.PostStateOrStatus = tr.PostState
} else {
r.PostStateOrStatus = new(big.Int).SetUint64(tr.Status).Bytes()
}
r.Logs, _ = rlp.EncodeToBytes(tr.Logs)
return r
}
// decode68 parses a receipt in the eth/68 network encoding.
func (r *Receipt) decode68(buf *receiptListBuffers, s *rlp.Stream) error {
k, size, err := s.Kind()
if err != nil {
return err return err
} }
txtype, err := s.Uint8()
if err != nil { *r = Receipt{}
return fmt.Errorf("invalid txType: %w", err) if k == rlp.List {
// Legacy receipt.
return r.decodeInnerList(s, false, true)
} }
postStateOrStatus, err := s.Bytes() // Typed receipt.
if size < 2 || size > maxReceiptSize {
return fmt.Errorf("invalid receipt size %d", size)
}
buf.tmp.Reset()
buf.tmp.Grow(int(size))
payload := buf.tmp.Bytes()[:int(size)]
if err := s.ReadBytes(payload); err != nil {
return err
}
r.TxType = payload[0]
s2 := rlp.NewStream(bytes.NewReader(payload[1:]), 0)
return r.decodeInnerList(s2, false, true)
}
// decode69 parses a receipt in the eth/69 network encoding.
func (r *Receipt) decode69(s *rlp.Stream) error {
*r = Receipt{}
return r.decodeInnerList(s, true, false)
}
// decodeDatabase parses a receipt in the basic database encoding.
func (r *Receipt) decodeDatabase(txType byte, s *rlp.Stream) error {
*r = Receipt{TxType: txType}
return r.decodeInnerList(s, false, false)
}
func (r *Receipt) decodeInnerList(s *rlp.Stream, readTxType, readBloom bool) error {
_, err := s.List()
if err != nil {
return err
}
if readTxType {
r.TxType, err = s.Uint8()
if err != nil {
return fmt.Errorf("invalid txType: %w", err)
}
}
r.PostStateOrStatus, err = s.Bytes()
if err != nil { if err != nil {
return fmt.Errorf("invalid postStateOrStatus: %w", err) return fmt.Errorf("invalid postStateOrStatus: %w", err)
} }
gasUsed, err := s.Uint64() r.GasUsed, err = s.Uint64()
if err != nil { if err != nil {
return fmt.Errorf("invalid gasUsed: %w", err) return fmt.Errorf("invalid gasUsed: %w", err)
} }
logs, err := s.Raw() if readBloom {
var b types.Bloom
if err := s.ReadBytes(b[:]); err != nil {
return fmt.Errorf("invalid bloom: %v", err)
}
}
r.Logs, err = s.Raw()
if err != nil { if err != nil {
return fmt.Errorf("invalid logs: %w", err) return fmt.Errorf("invalid logs: %w", err)
} }
*r = Receipt{
TxType: txtype,
PostStateOrStatus: postStateOrStatus,
GasUsed: gasUsed,
Logs: logs,
}
return s.ListEnd() return s.ListEnd()
} }
// encodeStorage writes the storage encoding, i.e. the result matches // encodeForStorage produces the the storage encoding, i.e. the result matches
// types.ReceiptForStorage. // the RLP encoding of types.ReceiptForStorage.
func (r *Receipt) encodeStorage(w *rlp.EncoderBuffer) { func (r *Receipt) encodeForStorage(w *rlp.EncoderBuffer) {
list := w.List() list := w.List()
w.WriteBytes(r.PostStateOrStatus) w.WriteBytes(r.PostStateOrStatus)
w.WriteUint64(r.GasUsed) w.WriteUint64(r.GasUsed)
@ -72,6 +130,60 @@ func (r *Receipt) encodeStorage(w *rlp.EncoderBuffer) {
w.ListEnd(list) w.ListEnd(list)
} }
// encodeForNetwork68 produces the eth/68 network protocol encoding of a receipt.
// Note this recomputes the bloom filter of the receipt.
func (r *Receipt) encodeForNetwork68(buf *receiptListBuffers, w *rlp.EncoderBuffer) {
writeInner := func(w *rlp.EncoderBuffer) {
list := w.List()
w.WriteBytes(r.PostStateOrStatus)
w.WriteUint64(r.GasUsed)
bloom := r.bloom(&buf.bloom)
w.WriteBytes(bloom[:])
w.Write(r.Logs)
w.ListEnd(list)
}
if r.TxType == 0 {
writeInner(w)
} else {
buf.tmp.Reset()
buf.tmp.WriteByte(r.TxType)
buf.enc.Reset(&buf.tmp)
writeInner(&buf.enc)
buf.enc.Flush()
w.WriteBytes(buf.tmp.Bytes())
}
}
// encodeForNetwork69 produces the eth/69 network protocol encoding of a receipt.
func (r *Receipt) encodeForNetwork69(w *rlp.EncoderBuffer) {
list := w.List()
w.WriteUint64(uint64(r.TxType))
w.WriteBytes(r.PostStateOrStatus)
w.WriteUint64(r.GasUsed)
w.Write(r.Logs)
w.ListEnd(list)
}
// encodeForHash encodes a receipt for the block receiptsRoot derivation.
func (r *Receipt) encodeForHash(buf *receiptListBuffers, out *bytes.Buffer) {
// For typed receipts, add the tx type.
if r.TxType != 0 {
out.WriteByte(r.TxType)
}
// Encode list = [postStateOrStatus, gasUsed, bloom, logs].
w := &buf.enc
w.Reset(out)
l := w.List()
w.WriteBytes(r.PostStateOrStatus)
w.WriteUint64(r.GasUsed)
bloom := r.bloom(&buf.bloom)
w.WriteBytes(bloom[:])
w.Write(r.Logs)
w.ListEnd(l)
w.Flush()
}
// bloom computes the bloom filter of the receipt. // bloom computes the bloom filter of the receipt.
// Note this doesn't check the validity of encoding, and will produce an invalid filter // Note this doesn't check the validity of encoding, and will produce an invalid filter
// for invalid input. This is acceptable for the purpose of this function, which is // for invalid input. This is acceptable for the purpose of this function, which is
@ -83,8 +195,7 @@ func (r *Receipt) bloom(buffer *[6]byte) types.Bloom {
return b return b
} }
for logsIter.Next() { for logsIter.Next() {
log := logsIter.Value() log, _, _ := rlp.SplitList(logsIter.Value())
log, _, _ = rlp.SplitList(log)
address, log, _ := rlp.SplitString(log) address, log, _ := rlp.SplitString(log)
b.AddWithBuffer(address, buffer) b.AddWithBuffer(address, buffer)
topicsIter, err := rlp.NewListIterator(log) topicsIter, err := rlp.NewListIterator(log)
@ -99,6 +210,133 @@ func (r *Receipt) bloom(buffer *[6]byte) types.Bloom {
return b return b
} }
type receiptListBuffers struct {
enc rlp.EncoderBuffer
bloom [6]byte
tmp bytes.Buffer
}
func initBuffers(buf **receiptListBuffers) {
if *buf == nil {
*buf = new(receiptListBuffers)
}
}
// encodeForStorage encodes a list of receipts for the database.
func (buf *receiptListBuffers) encodeForStorage(rs []Receipt) rlp.RawValue {
var out bytes.Buffer
w := &buf.enc
w.Reset(&out)
outer := w.List()
for _, receipts := range rs {
receipts.encodeForStorage(w)
}
w.ListEnd(outer)
w.Flush()
return out.Bytes()
}
// ReceiptList68 is a block receipt list as downloaded by eth/68.
// This also implements types.DerivableList for validation purposes.
type ReceiptList68 struct {
buf *receiptListBuffers
items []Receipt
}
// NewReceiptList68 creates a receipt list.
// This is slow, and exists for testing purposes.
func NewReceiptList68(trs []*types.Receipt) *ReceiptList68 {
rl := &ReceiptList68{items: make([]Receipt, len(trs))}
for i, tr := range trs {
rl.items[i] = newReceipt(tr)
}
return rl
}
func blockReceiptsToNetwork68(blockReceipts, blockBody rlp.RawValue) ([]byte, error) {
txTypesIter, err := txTypesInBody(blockBody)
if err != nil {
return nil, fmt.Errorf("invalid block body: %v", err)
}
nextTxType, stopTxTypes := iter.Pull(txTypesIter)
defer stopTxTypes()
var (
out bytes.Buffer
buf receiptListBuffers
)
blockReceiptIter, _ := rlp.NewListIterator(blockReceipts)
innerReader := bytes.NewReader(nil)
innerStream := rlp.NewStream(innerReader, 0)
w := rlp.NewEncoderBuffer(&out)
outer := w.List()
for i := 0; blockReceiptIter.Next(); i++ {
content := blockReceiptIter.Value()
innerReader.Reset(content)
innerStream.Reset(innerReader, uint64(len(content)))
var r Receipt
txType, _ := nextTxType()
if err := r.decodeDatabase(txType, innerStream); err != nil {
return nil, fmt.Errorf("invalid database receipt %d: %v", i, err)
}
r.encodeForNetwork68(&buf, &w)
}
w.ListEnd(outer)
w.Flush()
return out.Bytes(), nil
}
// setBuffers implements ReceiptsList.
func (rl *ReceiptList68) setBuffers(buf *receiptListBuffers) {
rl.buf = buf
}
// EncodeForStorage encodes the receipts for storage into the database.
func (rl *ReceiptList68) EncodeForStorage() rlp.RawValue {
initBuffers(&rl.buf)
return rl.buf.encodeForStorage(rl.items)
}
// Len implements types.DerivableList.
func (rl *ReceiptList68) Len() int {
return len(rl.items)
}
// EncodeIndex implements types.DerivableList.
func (rl *ReceiptList68) EncodeIndex(i int, out *bytes.Buffer) {
initBuffers(&rl.buf)
rl.items[i].encodeForHash(rl.buf, out)
}
// DecodeRLP decodes a list of receipts from the network format.
func (rl *ReceiptList68) DecodeRLP(s *rlp.Stream) error {
initBuffers(&rl.buf)
if _, err := s.List(); err != nil {
return err
}
for i := 0; s.MoreDataInList(); i++ {
var item Receipt
err := item.decode68(rl.buf, s)
if err != nil {
return fmt.Errorf("receipt %d: %v", i, err)
}
rl.items = append(rl.items, item)
}
return s.ListEnd()
}
// EncodeRLP encodes the list into the network format of eth/68.
func (rl *ReceiptList68) EncodeRLP(_w io.Writer) error {
initBuffers(&rl.buf)
w := rlp.NewEncoderBuffer(_w)
outer := w.List()
for i := range rl.items {
rl.items[i].encodeForNetwork68(rl.buf, &w)
}
w.ListEnd(outer)
return w.Flush()
}
// ReceiptList69 is the block receipt list as downloaded by eth/69. // ReceiptList69 is the block receipt list as downloaded by eth/69.
// This implements types.DerivableList for validation purposes. // This implements types.DerivableList for validation purposes.
type ReceiptList69 struct { type ReceiptList69 struct {
@ -106,39 +344,36 @@ type ReceiptList69 struct {
items []Receipt items []Receipt
} }
type receiptListBuffers struct { // NewReceiptList69 creates a receipt list.
enc rlp.EncoderBuffer // This is slow, and exists for testing purposes.
bloom [6]byte func NewReceiptList69(trs []*types.Receipt) *ReceiptList69 {
rl := &ReceiptList69{items: make([]Receipt, len(trs))}
for i, tr := range trs {
rl.items[i] = newReceipt(tr)
}
return rl
} }
// Len returns the length of the list. // setBuffers implements ReceiptsList.
func (rl *ReceiptList69) setBuffers(buf *receiptListBuffers) {
rl.buf = buf
}
// EncodeForStorage encodes the receipts for storage into the database.
func (rl *ReceiptList69) EncodeForStorage() rlp.RawValue {
initBuffers(&rl.buf)
return rl.buf.encodeForStorage(rl.items)
}
// Len implements types.DerivableList.
func (rl *ReceiptList69) Len() int { func (rl *ReceiptList69) Len() int {
return len(rl.items) return len(rl.items)
} }
// EncodeIndex implements types.DerivableList. // EncodeIndex implements types.DerivableList.
func (rl *ReceiptList69) EncodeIndex(i int, b *bytes.Buffer) { func (rl *ReceiptList69) EncodeIndex(i int, out *bytes.Buffer) {
if rl.buf == nil { initBuffers(&rl.buf)
rl.buf = new(receiptListBuffers) rl.items[i].encodeForHash(rl.buf, out)
}
var (
r = &rl.items[i]
bloom = r.bloom(&rl.buf.bloom)
w = &rl.buf.enc
)
if r.TxType != 0 {
b.WriteByte(r.TxType)
}
// encode list = [postStateOrStatus, gasUsed, bloom, logs]
w.Reset(b)
l := w.List()
w.WriteBytes(r.PostStateOrStatus)
w.WriteUint64(r.GasUsed)
w.WriteBytes(bloom[:])
w.Write(r.Logs)
w.ListEnd(l)
w.Flush()
} }
// DecodeRLP decodes a list receipts from the network format. // DecodeRLP decodes a list receipts from the network format.
@ -148,7 +383,7 @@ func (rl *ReceiptList69) DecodeRLP(s *rlp.Stream) error {
} }
for i := 0; s.MoreDataInList(); i++ { for i := 0; s.MoreDataInList(); i++ {
var item Receipt var item Receipt
err := item.DecodeRLP(s) err := item.decode69(s)
if err != nil { if err != nil {
return fmt.Errorf("receipt %d: %v", i, err) return fmt.Errorf("receipt %d: %v", i, err)
} }
@ -157,30 +392,21 @@ func (rl *ReceiptList69) DecodeRLP(s *rlp.Stream) error {
return s.ListEnd() return s.ListEnd()
} }
// toStorageReceiptsRLP encodes the receipts for storage into the database. // EncodeRLP encodes the list into the network format of eth/69.
func (rl *ReceiptList69) toStorageReceiptsRLP() rlp.RawValue { func (rl *ReceiptList69) EncodeRLP(_w io.Writer) error {
if rl.buf == nil { w := rlp.NewEncoderBuffer(_w)
rl.buf = new(receiptListBuffers)
}
var (
out bytes.Buffer
w = &rl.buf.enc
)
w.Reset(&out)
outer := w.List() outer := w.List()
for _, receipts := range rl.items { for i := range rl.items {
receipts.encodeStorage(w) rl.items[i].encodeForNetwork69(&w)
} }
w.ListEnd(outer) w.ListEnd(outer)
w.Flush() return w.Flush()
return out.Bytes()
} }
// blockReceiptsToNetwork takes a slice of rlp-encoded receipts, and transactions, // blockReceiptsToNetwork69 takes a slice of rlp-encoded receipts, and transactions,
// and applies the type-encoding on the receipts (for non-legacy receipts). // and applies the type-encoding on the receipts (for non-legacy receipts).
// e.g. for non-legacy receipts: receipt-data -> {tx-type || receipt-data} // e.g. for non-legacy receipts: receipt-data -> {tx-type || receipt-data}
func blockReceiptsToNetwork(blockReceipts, blockBody rlp.RawValue) ([]byte, error) { func blockReceiptsToNetwork69(blockReceipts, blockBody rlp.RawValue) ([]byte, error) {
txTypesIter, err := txTypesInBody(blockBody) txTypesIter, err := txTypesInBody(blockBody)
if err != nil { if err != nil {
return nil, fmt.Errorf("invalid block body: %v", err) return nil, fmt.Errorf("invalid block body: %v", err)

View file

@ -26,7 +26,13 @@ import (
"github.com/ethereum/go-ethereum/trie" "github.com/ethereum/go-ethereum/trie"
) )
func TestTransformReceipts(t *testing.T) { // miniDeriveFields derives the necessary receipt fields to make types.DeriveSha work.
func miniDeriveFields(r *types.Receipt, txType byte) {
r.Type = txType
r.Bloom = types.CreateBloom(r)
}
func TestReceiptList69(t *testing.T) {
logs := []*types.Log{{Address: common.Address{1}, Topics: []common.Hash{{1}}}} logs := []*types.Log{{Address: common.Address{1}, Topics: []common.Hash{{1}}}}
encLogs, _ := rlp.EncodeToBytes(logs) encLogs, _ := rlp.EncodeToBytes(logs)
@ -66,7 +72,7 @@ func TestTransformReceipts(t *testing.T) {
encBlockBody, _ := rlp.EncodeToBytes(blockBody) encBlockBody, _ := rlp.EncodeToBytes(blockBody)
// convert from storage encoding to network encoding // convert from storage encoding to network encoding
network, err := blockReceiptsToNetwork(in, encBlockBody) network, err := blockReceiptsToNetwork69(in, encBlockBody)
if err != nil { if err != nil {
t.Fatalf("test[%d]: blockReceiptsToNetwork error: %v", i, err) t.Fatalf("test[%d]: blockReceiptsToNetwork error: %v", i, err)
} }
@ -76,7 +82,7 @@ func TestTransformReceipts(t *testing.T) {
if err := rlp.DecodeBytes(network, &rl); err != nil { if err := rlp.DecodeBytes(network, &rl); err != nil {
t.Fatalf("test[%d]: can't decode network receipts: %v", i, err) t.Fatalf("test[%d]: can't decode network receipts: %v", i, err)
} }
storageEnc := rl.toStorageReceiptsRLP() storageEnc := rl.EncodeForStorage()
if !bytes.Equal(storageEnc, in) { if !bytes.Equal(storageEnc, in) {
t.Fatalf("test[%d]: re-encoded receipts not equal\nhave: %x\nwant: %x", i, storageEnc, in) t.Fatalf("test[%d]: re-encoded receipts not equal\nhave: %x\nwant: %x", i, storageEnc, in)
} }
@ -85,9 +91,7 @@ func TestTransformReceipts(t *testing.T) {
receipts := make(types.Receipts, len(test.input)) receipts := make(types.Receipts, len(test.input))
for i := range test.input { for i := range test.input {
r := types.Receipt(test.input[i]) r := types.Receipt(test.input[i])
// need to derive these fields to get the correct result from DeriveSha. miniDeriveFields(&r, test.txs[i].Type())
r.Type = test.txs[i].Type()
r.Bloom = types.CreateBloom(&r)
receipts[i] = &r receipts[i] = &r
} }
expectedHash := types.DeriveSha(receipts, trie.NewStackTrie(nil)) expectedHash := types.DeriveSha(receipts, trie.NewStackTrie(nil))
@ -99,14 +103,3 @@ func TestTransformReceipts(t *testing.T) {
} }
} }
} }
func TestReceiptsMessage69(t *testing.T) {
msg := common.FromHex("f9037c880e17dea70d94cb05f90370e7e680a0c97ed91127dd6f6a099889686685b3b204a5888a9d84431747324654eca53e4c825208c0e7e680a00ba7c3d24be245396fc5d3f80d8a69813637c63b2750a0aecca0ecf1893ae6de825208c0e7e680a0b1bdb7832a63083237ae909f02b5290e5ead11e947f90ad406ce58af42d9cbf2825208c0e7e680a0a8db4c173896d1961aab985d4084276cfbb5b460184c72ed9148093427813e50825208c0e7e680a02ac83e5239694103e4b8bfe88055573eb6947eb9ec456bd1ecd40c3a390c1184825208c0e7e680a05d61474327cd80ea904886eb01497db86aa4e1826f60ba8041b342b7e3271451825208c0e7e680a0c586c7bffdaa7eafd1e64755f6438d4737d3da328e4bf15bf36f425e35dcba57825208c0e7e680a0db8c7c749080c80efc9b8fec0036b7aebe1fdb130cc0529cd41658f0e4073f32825208c0e7e680a052820bf047785bce72e004175cf20430228ed6e3aafd2f0dc9d881ab4b8247b0825208c0e7e680a00b8fa31c4ba7486ba43779175b05f07e9a913d97f0d784294283c67ca08ecade825208c0e7e680a059d6258b1d014be483b4393850a81fa109d80d0d222acc0ca4897332839f8760825208c0e7e680a0128b6226c363fe1fe0a94bc576b46569ee4d59cfcff913df74165294bb1478de825208c0e7e680a0525f09191eb703dce45154db01474a84191b45bddd36e99b6cbf59a5a40ee935825208c0e7e680a032664bd056b1e2ae4dd7293e578b5d7300f97e1a1a81432d310d7100d1931c2d825208c0e7e680a07d29340b854047e215e787d04073bbf4451a4fbdeaaaac8ef67934eb2612d264825208c0e7e680a054b35228ab2fd0a096976f3c144cb86bed4f48e952ac78b4c4bd8ee377ce686a8256d0c0e7e680a0bf5fb6d0da92232bc4b0a5c8bc6675a9f298c23ce747877074fe24f6e2d75dfa825208c0e7e680a04fbf751cb29c49f34c9da0d8fe456afc8672b2e6049d51b01ff40456661456e9825208c0e7e680a050397035c265d9d628bf382e072fec34e72c98a86f48f7c6afdd7bbb128ea7e2825208c0e7e680a0ed10826649c2bdb34763b3f293cc18564cc883d6b6cbb84df75e7e328d3e2594825208c0e7e680a02ca0ecfc7149619fac0b5376a8a6fd1b0da6371ad875d50603d2adb667f12c2f825208c0e7e680a0801743bd8c5e43b7b77024ff80c4e437d8b505f8295dfb179f667b22c0762637825208c0")
var dec ReceiptsPacket69
if err := rlp.DecodeBytes(msg, &dec); err != nil {
t.Fatal(err)
}
if len(dec.List) != 22 {
t.Fatalf("wrong list length %d", len(dec.List))
}
}