This commit is contained in:
Bosul Mun 2026-02-25 21:55:25 -08:00 committed by GitHub
commit 21be332503
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 167 additions and 635 deletions

View file

@ -155,7 +155,7 @@ func (c *Conn) ReadEth() (any, error) {
var msg any
switch int(code) {
case eth.StatusMsg:
msg = new(eth.StatusPacket69)
msg = new(eth.StatusPacket)
case eth.GetBlockHeadersMsg:
msg = new(eth.GetBlockHeadersPacket)
case eth.BlockHeadersMsg:
@ -164,10 +164,6 @@ func (c *Conn) ReadEth() (any, error) {
msg = new(eth.GetBlockBodiesPacket)
case eth.BlockBodiesMsg:
msg = new(eth.BlockBodiesPacket)
case eth.NewBlockMsg:
msg = new(eth.NewBlockPacket)
case eth.NewBlockHashesMsg:
msg = new(eth.NewBlockHashesPacket)
case eth.TransactionsMsg:
msg = new(eth.TransactionsPacket)
case eth.NewPooledTransactionHashesMsg:
@ -229,7 +225,7 @@ func (c *Conn) ReadSnap() (any, error) {
}
// dialAndPeer creates a peer connection and runs the handshake.
func (s *Suite) dialAndPeer(status *eth.StatusPacket69) (*Conn, error) {
func (s *Suite) dialAndPeer(status *eth.StatusPacket) (*Conn, error) {
c, err := s.dial()
if err != nil {
return nil, err
@ -242,7 +238,7 @@ func (s *Suite) dialAndPeer(status *eth.StatusPacket69) (*Conn, error) {
// peer performs both the protocol handshake and the status message
// exchange with the node in order to peer with it.
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket69) error {
func (c *Conn) peer(chain *Chain, status *eth.StatusPacket) error {
if err := c.handshake(); err != nil {
return fmt.Errorf("handshake failed: %v", err)
}
@ -315,7 +311,7 @@ func (c *Conn) negotiateEthProtocol(caps []p2p.Cap) {
}
// statusExchange performs a `Status` message exchange with the given node.
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket69) error {
func (c *Conn) statusExchange(chain *Chain, status *eth.StatusPacket) error {
loop:
for {
code, data, err := c.Read()
@ -324,7 +320,7 @@ loop:
}
switch code {
case eth.StatusMsg + protoOffset(ethProto):
msg := new(eth.StatusPacket69)
msg := new(eth.StatusPacket)
if err := rlp.DecodeBytes(data, &msg); err != nil {
return fmt.Errorf("error decoding status packet: %w", err)
}
@ -363,7 +359,7 @@ loop:
}
if status == nil {
// default status message
status = &eth.StatusPacket69{
status = &eth.StatusPacket{
ProtocolVersion: uint32(c.negotiatedProtoVersion),
NetworkID: chain.config.ChainID.Uint64(),
Genesis: chain.blocks[0].Hash(),

View file

@ -447,7 +447,7 @@ func (s *Suite) TestGetReceipts(t *utesting.T) {
t.Fatalf("could not write to connection: %v", err)
}
// Wait for response.
resp := new(eth.ReceiptsPacket[*eth.ReceiptList69])
resp := new(eth.ReceiptsPacket)
if err := conn.ReadMsg(ethProto, eth.ReceiptsMsg, &resp); err != nil {
t.Fatalf("error reading block bodies msg: %v", err)
}

View file

@ -17,6 +17,7 @@
package downloader
import (
"bytes"
"fmt"
"math/big"
"sync"
@ -40,6 +41,16 @@ import (
"github.com/ethereum/go-ethereum/trie"
)
type receiptHashList struct {
items []eth.Receipt
bloomBuf [6]byte
}
func (l *receiptHashList) Len() int { return len(l.items) }
func (l *receiptHashList) EncodeIndex(i int, buf *bytes.Buffer) {
l.items[i].EncodeForHash(&l.bloomBuf, buf)
}
// downloadTester is a test simulator for mocking out local block chain.
type downloadTester struct {
chain *core.BlockChain
@ -261,16 +272,18 @@ func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash, sink chan *et
// peer in the download tester. The returned function can be used to retrieve
// batches of block receipts from the particularly requested peer.
func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash, sink chan *eth.Response) (*eth.Request, error) {
blobs := eth.ServiceGetReceiptsQuery68(dlp.chain, hashes)
blobs := eth.ServiceGetReceiptsQuery(dlp.chain, hashes)
receipts := make([]types.Receipts, len(blobs))
hashes = make([]common.Hash, len(blobs))
hasher := trie.NewStackTrie(nil)
for i, blob := range blobs {
rlp.DecodeBytes(blob, &receipts[i])
}
hasher := trie.NewStackTrie(nil)
hashes = make([]common.Hash, len(receipts))
for i, receipt := range receipts {
hashes[i] = types.DeriveSha(receipt, hasher)
var items []eth.Receipt
rlp.DecodeBytes(blob, &items)
hashes[i] = types.DeriveSha(&receiptHashList{items: items}, hasher)
}
req := &eth.Request{
Peer: dlp.id,
@ -398,8 +411,8 @@ func assertOwnChain(t *testing.T, tester *downloadTester, length int) {
}
}
func TestCanonicalSynchronisation68Full(t *testing.T) { testCanonSync(t, eth.ETH68, FullSync) }
func TestCanonicalSynchronisation68Snap(t *testing.T) { testCanonSync(t, eth.ETH68, SnapSync) }
func TestCanonicalSynchronisation68Full(t *testing.T) { testCanonSync(t, eth.ETH69, FullSync) }
func TestCanonicalSynchronisation68Snap(t *testing.T) { testCanonSync(t, eth.ETH69, SnapSync) }
func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
success := make(chan struct{})
@ -426,8 +439,8 @@ func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that if a large batch of blocks are being downloaded, it is throttled
// until the cached blocks are retrieved.
func TestThrottling68Full(t *testing.T) { testThrottling(t, eth.ETH68, FullSync) }
func TestThrottling68Snap(t *testing.T) { testThrottling(t, eth.ETH68, SnapSync) }
func TestThrottling68Full(t *testing.T) { testThrottling(t, eth.ETH69, FullSync) }
func TestThrottling68Snap(t *testing.T) { testThrottling(t, eth.ETH69, SnapSync) }
func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
tester := newTester(t, mode)
@ -504,8 +517,8 @@ func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
}
// Tests that a canceled download wipes all previously accumulated state.
func TestCancel68Full(t *testing.T) { testCancel(t, eth.ETH68, FullSync) }
func TestCancel68Snap(t *testing.T) { testCancel(t, eth.ETH68, SnapSync) }
func TestCancel68Full(t *testing.T) { testCancel(t, eth.ETH69, FullSync) }
func TestCancel68Snap(t *testing.T) { testCancel(t, eth.ETH69, SnapSync) }
func testCancel(t *testing.T, protocol uint, mode SyncMode) {
complete := make(chan struct{})
@ -536,8 +549,8 @@ func testCancel(t *testing.T, protocol uint, mode SyncMode) {
// Tests that synchronisations behave well in multi-version protocol environments
// and not wreak havoc on other nodes in the network.
func TestMultiProtoSynchronisation68Full(t *testing.T) { testMultiProtoSync(t, eth.ETH68, FullSync) }
func TestMultiProtoSynchronisation68Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH68, SnapSync) }
func TestMultiProtoSynchronisation68Full(t *testing.T) { testMultiProtoSync(t, eth.ETH69, FullSync) }
func TestMultiProtoSynchronisation68Snap(t *testing.T) { testMultiProtoSync(t, eth.ETH69, SnapSync) }
func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
complete := make(chan struct{})
@ -551,7 +564,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
chain := testChainBase.shorten(blockCacheMaxItems - 15)
// Create peers of every type
tester.newPeer("peer 68", eth.ETH68, chain.blocks[1:])
tester.newPeer("peer 68", eth.ETH69, chain.blocks[1:])
if err := tester.downloader.BeaconSync(chain.blocks[len(chain.blocks)-1].Header(), nil); err != nil {
t.Fatalf("failed to start beacon sync: %v", err)
@ -575,8 +588,8 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that if a block is empty (e.g. header only), no body request should be
// made, and instead the header should be assembled into a whole block in itself.
func TestEmptyShortCircuit68Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, FullSync) }
func TestEmptyShortCircuit68Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH68, SnapSync) }
func TestEmptyShortCircuit68Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH69, FullSync) }
func TestEmptyShortCircuit68Snap(t *testing.T) { testEmptyShortCircuit(t, eth.ETH69, SnapSync) }
func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
success := make(chan struct{})
@ -644,8 +657,8 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync
// Tests that peers below a pre-configured checkpoint block are prevented from
// being fast-synced from, avoiding potential cheap eclipse attacks.
func TestBeaconSync68Full(t *testing.T) { testBeaconSync(t, eth.ETH68, FullSync) }
func TestBeaconSync68Snap(t *testing.T) { testBeaconSync(t, eth.ETH68, SnapSync) }
func TestBeaconSync68Full(t *testing.T) { testBeaconSync(t, eth.ETH69, FullSync) }
func TestBeaconSync68Snap(t *testing.T) { testBeaconSync(t, eth.ETH69, SnapSync) }
func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) {
var cases = []struct {
@ -690,8 +703,8 @@ func testBeaconSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that synchronisation progress (origin block number, current block number
// and highest block number) is tracked and updated correctly.
func TestSyncProgress68Full(t *testing.T) { testSyncProgress(t, eth.ETH68, FullSync) }
func TestSyncProgress68Snap(t *testing.T) { testSyncProgress(t, eth.ETH68, SnapSync) }
func TestSyncProgress68Full(t *testing.T) { testSyncProgress(t, eth.ETH69, FullSync) }
func TestSyncProgress68Snap(t *testing.T) { testSyncProgress(t, eth.ETH69, SnapSync) }
func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
success := make(chan struct{})

View file

@ -845,7 +845,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
// Create a peer set to feed headers through
peerset := newPeerSet()
for _, peer := range tt.peers {
peerset.Register(newPeerConnection(peer.id, eth.ETH68, peer, log.New("id", peer.id)))
peerset.Register(newPeerConnection(peer.id, eth.ETH69, peer, log.New("id", peer.id)))
}
// Create a peer dropper to track malicious peers
dropped := make(map[string]int)
@ -912,7 +912,7 @@ func TestSkeletonSyncRetrievals(t *testing.T) {
// Apply the post-init events if there's any
endpeers := tt.peers
if tt.newPeer != nil {
if err := peerset.Register(newPeerConnection(tt.newPeer.id, eth.ETH68, tt.newPeer, log.New("id", tt.newPeer.id))); err != nil {
if err := peerset.Register(newPeerConnection(tt.newPeer.id, eth.ETH69, tt.newPeer, log.New("id", tt.newPeer.id))); err != nil {
t.Errorf("test %d: failed to register new peer: %v", i, err)
}
time.Sleep(time.Millisecond * 50) // given time for peer registration

View file

@ -38,9 +38,8 @@ import (
// testEthHandler is a mock event handler to listen for inbound network requests
// on the `eth` protocol and convert them into a more easily testable form.
type testEthHandler struct {
blockBroadcasts event.Feed
txAnnounces event.Feed
txBroadcasts event.Feed
txAnnounces event.Feed
txBroadcasts event.Feed
}
func (h *testEthHandler) Chain() *core.BlockChain { panic("no backing chain") }
@ -51,10 +50,6 @@ func (h *testEthHandler) PeerInfo(enode.ID) interface{} { panic("not used
func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
switch packet := packet.(type) {
case *eth.NewBlockPacket:
h.blockBroadcasts.Send(packet.Block)
return nil
case *eth.NewPooledTransactionHashesPacket:
h.txAnnounces.Send(packet.Hashes)
return nil
@ -82,7 +77,7 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
// Tests that peers are correctly accepted (or rejected) based on the advertised
// fork IDs in the protocol handshake.
func TestForkIDSplit68(t *testing.T) { testForkIDSplit(t, eth.ETH68) }
func TestForkIDSplit69(t *testing.T) { testForkIDSplit(t, eth.ETH69) }
func testForkIDSplit(t *testing.T, protocol uint) {
t.Parallel()
@ -234,7 +229,7 @@ func testForkIDSplit(t *testing.T, protocol uint) {
}
// Tests that received transactions are added to the local pool.
func TestRecvTransactions68(t *testing.T) { testRecvTransactions(t, eth.ETH68) }
func TestRecvTransactions69(t *testing.T) { testRecvTransactions(t, eth.ETH69) }
func testRecvTransactions(t *testing.T, protocol uint) {
t.Parallel()
@ -263,7 +258,8 @@ func testRecvTransactions(t *testing.T, protocol uint) {
return eth.Handle((*ethHandler)(handler.handler), peer)
})
// Run the handshake locally to avoid spinning up a source handler
if err := src.Handshake(1, handler.chain, eth.BlockRangeUpdatePacket{}); err != nil {
head := handler.chain.CurrentBlock()
if err := src.Handshake(1, handler.chain, eth.BlockRangeUpdatePacket{EarliestBlock: 0, LatestBlock: head.Number.Uint64(), LatestBlockHash: head.Hash()}); err != nil {
t.Fatalf("failed to run protocol handshake")
}
// Send the transaction to the sink and verify that it's added to the tx pool
@ -286,7 +282,7 @@ func testRecvTransactions(t *testing.T, protocol uint) {
}
// This test checks that pending transactions are sent.
func TestSendTransactions68(t *testing.T) { testSendTransactions(t, eth.ETH68) }
func TestSendTransactions69(t *testing.T) { testSendTransactions(t, eth.ETH69) }
func testSendTransactions(t *testing.T, protocol uint) {
t.Parallel()
@ -318,7 +314,8 @@ func testSendTransactions(t *testing.T, protocol uint) {
return eth.Handle((*ethHandler)(handler.handler), peer)
})
// Run the handshake locally to avoid spinning up a source handler
if err := sink.Handshake(1, handler.chain, eth.BlockRangeUpdatePacket{}); err != nil {
head := handler.chain.CurrentBlock()
if err := sink.Handshake(1, handler.chain, eth.BlockRangeUpdatePacket{EarliestBlock: 0, LatestBlock: head.Number.Uint64(), LatestBlockHash: head.Hash()}); err != nil {
t.Fatalf("failed to run protocol handshake")
}
// After the handshake completes, the source handler should stream the sink
@ -338,22 +335,16 @@ func testSendTransactions(t *testing.T, protocol uint) {
// Make sure we get all the transactions on the correct channels
seen := make(map[common.Hash]struct{})
for len(seen) < len(insert) {
switch protocol {
case 68:
select {
case hashes := <-anns:
for _, hash := range hashes {
if _, ok := seen[hash]; ok {
t.Errorf("duplicate transaction announced: %x", hash)
}
seen[hash] = struct{}{}
select {
case hashes := <-anns:
for _, hash := range hashes {
if _, ok := seen[hash]; ok {
t.Errorf("duplicate transaction announced: %x", hash)
}
case <-bcasts:
t.Errorf("initial tx broadcast received on post eth/66")
seen[hash] = struct{}{}
}
default:
panic("unsupported protocol, please extend test")
case <-bcasts:
t.Errorf("initial tx broadcast received on post eth/66")
}
}
for _, tx := range insert {
@ -365,7 +356,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
// Tests that transactions get propagated to all attached peers, either via direct
// broadcasts or via announcements/retrievals.
func TestTransactionPropagation68(t *testing.T) { testTransactionPropagation(t, eth.ETH68) }
func TestTransactionPropagation69(t *testing.T) { testTransactionPropagation(t, eth.ETH69) }
func testTransactionPropagation(t *testing.T, protocol uint) {
t.Parallel()

View file

@ -166,21 +166,6 @@ type Decoder interface {
Time() time.Time
}
var eth68 = map[uint64]msgHandler{
NewBlockHashesMsg: handleNewBlockhashes,
NewBlockMsg: handleNewBlock,
TransactionsMsg: handleTransactions,
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
GetBlockHeadersMsg: handleGetBlockHeaders,
BlockHeadersMsg: handleBlockHeaders,
GetBlockBodiesMsg: handleGetBlockBodies,
BlockBodiesMsg: handleBlockBodies,
GetReceiptsMsg: handleGetReceipts68,
ReceiptsMsg: handleReceipts[*ReceiptList68],
GetPooledTransactionsMsg: handleGetPooledTransactions,
PooledTransactionsMsg: handlePooledTransactions,
}
var eth69 = map[uint64]msgHandler{
TransactionsMsg: handleTransactions,
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
@ -188,8 +173,8 @@ var eth69 = map[uint64]msgHandler{
BlockHeadersMsg: handleBlockHeaders,
GetBlockBodiesMsg: handleGetBlockBodies,
BlockBodiesMsg: handleBlockBodies,
GetReceiptsMsg: handleGetReceipts69,
ReceiptsMsg: handleReceipts[*ReceiptList69],
GetReceiptsMsg: handleGetReceipts,
ReceiptsMsg: handleReceipts,
GetPooledTransactionsMsg: handleGetPooledTransactions,
PooledTransactionsMsg: handlePooledTransactions,
BlockRangeUpdateMsg: handleBlockRangeUpdate,
@ -209,9 +194,7 @@ func handleMessage(backend Backend, peer *Peer) error {
defer msg.Discard()
var handlers map[uint64]msgHandler
if peer.version == ETH68 {
handlers = eth68
} else if peer.version == ETH69 {
if peer.version == ETH69 {
handlers = eth69
} else {
return fmt.Errorf("unknown eth protocol version: %v", peer.version)

View file

@ -174,7 +174,7 @@ func (b *testBackend) Handle(*Peer, Packet) error {
}
// Tests that block headers can be retrieved from a remote chain based on user queries.
func TestGetBlockHeaders68(t *testing.T) { testGetBlockHeaders(t, ETH68) }
func TestGetBlockHeaders69(t *testing.T) { testGetBlockHeaders(t, ETH69) }
func testGetBlockHeaders(t *testing.T, protocol uint) {
t.Parallel()
@ -387,7 +387,7 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
}
// Tests that block contents can be retrieved from a remote chain based on their hashes.
func TestGetBlockBodies68(t *testing.T) { testGetBlockBodies(t, ETH68) }
func TestGetBlockBodies69(t *testing.T) { testGetBlockBodies(t, ETH69) }
func testGetBlockBodies(t *testing.T, protocol uint) {
t.Parallel()
@ -536,7 +536,7 @@ func TestHashBody(t *testing.T) {
}
// Tests that the transaction receipts can be retrieved based on hashes.
func TestGetBlockReceipts68(t *testing.T) { testGetBlockReceipts(t, ETH68) }
func TestGetBlockReceipts69(t *testing.T) { testGetBlockReceipts(t, ETH69) }
func testGetBlockReceipts(t *testing.T, protocol uint) {
t.Parallel()
@ -586,13 +586,16 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
// Collect the hashes to request, and the response to expect
var (
hashes []common.Hash
receipts rlp.RawList[*ReceiptList68]
receipts []rlp.RawValue
)
for i := uint64(0); i <= backend.chain.CurrentBlock().Number.Uint64(); i++ {
block := backend.chain.GetBlockByNumber(i)
hashes = append(hashes, block.Hash())
trs := backend.chain.GetReceiptsByHash(block.Hash())
receipts.Append(NewReceiptList68(trs))
receiptsRLP := backend.chain.GetReceiptsRLP(block.Hash())
bodyRLP := backend.chain.GetBodyRLP(block.Hash())
tr, _ := encodeTypes(receiptsRLP, bodyRLP)
receipts = append(receipts, tr)
}
// Send the hash request and verify the response
@ -600,9 +603,9 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
RequestId: 123,
GetReceiptsRequest: hashes,
})
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, &ReceiptsPacket[*ReceiptList68]{
RequestId: 123,
List: receipts,
if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, &ReceiptsRLPPacket{
RequestId: 123,
ReceiptsRLPResponse: receipts,
}); err != nil {
t.Errorf("receipts mismatch: %v", err)
}
@ -656,7 +659,7 @@ func setup() (*testBackend, *testPeer) {
}
}
backend := newTestBackendWithGenerator(maxBodiesServe+15, true, false, gen)
peer, _ := newTestPeer("peer", ETH68, backend)
peer, _ := newTestPeer("peer", ETH69, backend)
// Discard all messages
go func() {
for {
@ -701,7 +704,7 @@ func testGetPooledTransaction(t *testing.T, blobTx bool) {
backend := newTestBackendWithGenerator(0, true, true, nil)
defer backend.close()
peer, _ := newTestPeer("peer", ETH68, backend)
peer, _ := newTestPeer("peer", ETH69, backend)
defer peer.close()
var (

View file

@ -19,7 +19,6 @@ package eth
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"math"
@ -247,29 +246,20 @@ func ServiceGetBlockBodiesQuery(chain *core.BlockChain, query GetBlockBodiesRequ
return bodies
}
func handleGetReceipts68(backend Backend, msg Decoder, peer *Peer) error {
func handleGetReceipts(backend Backend, msg Decoder, peer *Peer) error {
// Decode the block receipts retrieval message
var query GetReceiptsPacket
if err := msg.Decode(&query); err != nil {
return err
}
response := ServiceGetReceiptsQuery68(backend.Chain(), query.GetReceiptsRequest)
response := ServiceGetReceiptsQuery(backend.Chain(), query.GetReceiptsRequest)
return peer.ReplyReceiptsRLP(query.RequestId, response)
}
func handleGetReceipts69(backend Backend, msg Decoder, peer *Peer) error {
// Decode the block receipts retrieval message
var query GetReceiptsPacket
if err := msg.Decode(&query); err != nil {
return err
}
response := serviceGetReceiptsQuery69(backend.Chain(), query.GetReceiptsRequest)
return peer.ReplyReceiptsRLP(query.RequestId, response)
}
// ServiceGetReceiptsQuery68 assembles the response to a receipt query. It is
// exposed to allow external packages to test protocol behavior.
func ServiceGetReceiptsQuery68(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue {
// ServiceGetReceiptsQuery assembles the response to a receipt query.
// It does not send the bloom filters for the receipts. It is exposed
// to allow external packages to test protocol behavior.
func ServiceGetReceiptsQuery(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue {
// Gather state data until the fetch or network limits is reached
var (
bytes int
@ -292,7 +282,7 @@ func ServiceGetReceiptsQuery68(chain *core.BlockChain, query GetReceiptsRequest)
continue
}
var err error
results, err = blockReceiptsToNetwork68(results, body)
results, err = encodeTypes(results, body)
if err != nil {
log.Error("Error in block receipts conversion", "hash", hash, "err", err)
continue
@ -304,51 +294,6 @@ func ServiceGetReceiptsQuery68(chain *core.BlockChain, query GetReceiptsRequest)
return receipts
}
// serviceGetReceiptsQuery69 assembles the response to a receipt query.
// It does not send the bloom filters for the receipts
func serviceGetReceiptsQuery69(chain *core.BlockChain, query GetReceiptsRequest) []rlp.RawValue {
// Gather state data until the fetch or network limits is reached
var (
bytes int
receipts []rlp.RawValue
)
for lookups, hash := range query {
if bytes >= softResponseLimit || len(receipts) >= maxReceiptsServe ||
lookups >= 2*maxReceiptsServe {
break
}
// Retrieve the requested block's receipts
results := chain.GetReceiptsRLP(hash)
if results == nil {
if header := chain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue
}
} else {
body := chain.GetBodyRLP(hash)
if body == nil {
continue
}
var err error
results, err = blockReceiptsToNetwork69(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
}
func handleNewBlockhashes(backend Backend, msg Decoder, peer *Peer) error {
return errors.New("block announcements disallowed") // We dropped support for non-merge networks
}
func handleNewBlock(backend Backend, msg Decoder, peer *Peer) error {
return errors.New("block broadcasts disallowed") // We dropped support for non-merge networks
}
func handleBlockHeaders(backend Backend, msg Decoder, peer *Peer) error {
// A batch of headers arrived to one of our previous requests
res := new(BlockHeadersPacket)
@ -490,9 +435,19 @@ func writeTxForHash(tx []byte, buf *bytes.Buffer) {
}
}
func handleReceipts[L ReceiptsList](backend Backend, msg Decoder, peer *Peer) error {
// writeReceiptForHash returns a write function that encode receipts for hash derivation.
func writeReceiptForHash(bloomBuf *[6]byte) func([]byte, *bytes.Buffer) {
return func(data []byte, outbuf *bytes.Buffer) {
var r Receipt
if rlp.DecodeBytes(data, &r) == nil {
r.EncodeForHash(bloomBuf, outbuf)
}
}
}
func handleReceipts(backend Backend, msg Decoder, peer *Peer) error {
// A batch of receipts arrived to one of our previous requests
res := new(ReceiptsPacket[L])
res := new(ReceiptsPacket)
if err := msg.Decode(res); err != nil {
return err
}
@ -502,25 +457,23 @@ func handleReceipts[L ReceiptsList](backend Backend, msg Decoder, peer *Peer) er
return fmt.Errorf("Receipts: %w", err)
}
// Assign temporary hashing buffer to each list item, the same buffer is shared
// between all receipt list instances.
receiptLists, err := res.List.Items()
if err != nil {
return fmt.Errorf("Receipts: %w", err)
}
buffers := new(receiptListBuffers)
for i := range receiptLists {
receiptLists[i].setBuffers(buffers)
}
var bloomBuf [6]byte
writeReceipt := writeReceiptForHash(&bloomBuf)
metadata := func() interface{} {
hasher := trie.NewStackTrie(nil)
hashes := make([]common.Hash, len(receiptLists))
for i := range receiptLists {
hashes[i] = types.DeriveSha(receiptLists[i].Derivable(), hasher)
receipts := newDerivableRawList(&receiptLists[i].items, writeReceipt)
hashes[i] = types.DeriveSha(receipts, hasher)
}
return hashes
}
var enc ReceiptsRLPResponse
for i := range receiptLists {
encReceipts, err := receiptLists[i].EncodeForStorage()

View file

@ -36,62 +36,6 @@ const (
// Handshake executes the eth protocol handshake, negotiating version number,
// network IDs, difficulties, head and genesis blocks.
func (p *Peer) Handshake(networkID uint64, chain forkid.Blockchain, rangeMsg BlockRangeUpdatePacket) error {
switch p.version {
case ETH69:
return p.handshake69(networkID, chain, rangeMsg)
case ETH68:
return p.handshake68(networkID, chain)
default:
return errors.New("unsupported protocol version")
}
}
func (p *Peer) handshake68(networkID uint64, chain forkid.Blockchain) error {
var (
genesis = chain.Genesis()
latest = chain.CurrentHeader()
forkID = forkid.NewID(chain.Config(), genesis, latest.Number.Uint64(), latest.Time)
forkFilter = forkid.NewFilter(chain)
)
errc := make(chan error, 2)
go func() {
pkt := &StatusPacket68{
ProtocolVersion: uint32(p.version),
NetworkID: networkID,
Head: latest.Hash(),
Genesis: genesis.Hash(),
ForkID: forkID,
}
errc <- p2p.Send(p.rw, StatusMsg, pkt)
}()
var status StatusPacket68 // safe to read after two values have been received from errc
go func() {
errc <- p.readStatus68(networkID, &status, genesis.Hash(), forkFilter)
}()
return waitForHandshake(errc, p)
}
func (p *Peer) readStatus68(networkID uint64, status *StatusPacket68, genesis common.Hash, forkFilter forkid.Filter) error {
if err := p.readStatusMsg(status); err != nil {
return err
}
if status.NetworkID != networkID {
return fmt.Errorf("%w: %d (!= %d)", errNetworkIDMismatch, status.NetworkID, networkID)
}
if uint(status.ProtocolVersion) != p.version {
return fmt.Errorf("%w: %d (!= %d)", errProtocolVersionMismatch, status.ProtocolVersion, p.version)
}
if status.Genesis != genesis {
return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis)
}
if err := forkFilter(status.ForkID); err != nil {
return fmt.Errorf("%w: %v", errForkIDRejected, err)
}
return nil
}
func (p *Peer) handshake69(networkID uint64, chain forkid.Blockchain, rangeMsg BlockRangeUpdatePacket) error {
var (
genesis = chain.Genesis()
latest = chain.CurrentHeader()
@ -101,7 +45,7 @@ func (p *Peer) handshake69(networkID uint64, chain forkid.Blockchain, rangeMsg B
errc := make(chan error, 2)
go func() {
pkt := &StatusPacket69{
pkt := &StatusPacket{
ProtocolVersion: uint32(p.version),
NetworkID: networkID,
Genesis: genesis.Hash(),
@ -112,15 +56,15 @@ func (p *Peer) handshake69(networkID uint64, chain forkid.Blockchain, rangeMsg B
}
errc <- p2p.Send(p.rw, StatusMsg, pkt)
}()
var status StatusPacket69 // safe to read after two values have been received from errc
var status StatusPacket // safe to read after two values have been received from errc
go func() {
errc <- p.readStatus69(networkID, &status, genesis.Hash(), forkFilter)
errc <- p.readStatus(networkID, &status, genesis.Hash(), forkFilter)
}()
return waitForHandshake(errc, p)
}
func (p *Peer) readStatus69(networkID uint64, status *StatusPacket69, genesis common.Hash, forkFilter forkid.Filter) error {
func (p *Peer) readStatus(networkID uint64, status *StatusPacket, genesis common.Hash, forkFilter forkid.Filter) error {
if err := p.readStatusMsg(status); err != nil {
return err
}

View file

@ -18,7 +18,6 @@ package eth
import (
"errors"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
@ -28,7 +27,7 @@ import (
)
// Tests that handshake failures are detected and reported correctly.
func TestHandshake68(t *testing.T) { testHandshake(t, ETH68) }
func TestHandshake69(t *testing.T) { testHandshake(t, ETH69) }
func testHandshake(t *testing.T, protocol uint) {
t.Parallel()
@ -52,21 +51,25 @@ func testHandshake(t *testing.T, protocol uint) {
want: errNoStatusMsg,
},
{
code: StatusMsg, data: StatusPacket68{10, 1, new(big.Int), head.Hash(), genesis.Hash(), forkID},
code: StatusMsg, data: StatusPacket{10, 1, genesis.Hash(), forkID, 0, head.Number.Uint64(), head.Hash()},
want: errProtocolVersionMismatch,
},
{
code: StatusMsg, data: StatusPacket68{uint32(protocol), 999, new(big.Int), head.Hash(), genesis.Hash(), forkID},
code: StatusMsg, data: StatusPacket{uint32(protocol), 999, genesis.Hash(), forkID, 0, head.Number.Uint64(), head.Hash()},
want: errNetworkIDMismatch,
},
{
code: StatusMsg, data: StatusPacket68{uint32(protocol), 1, new(big.Int), head.Hash(), common.Hash{3}, forkID},
code: StatusMsg, data: StatusPacket{uint32(protocol), 1, common.Hash{3}, forkID, 0, head.Number.Uint64(), head.Hash()},
want: errGenesisMismatch,
},
{
code: StatusMsg, data: StatusPacket68{uint32(protocol), 1, new(big.Int), head.Hash(), genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}},
code: StatusMsg, data: StatusPacket{uint32(protocol), 1, genesis.Hash(), forkid.ID{Hash: [4]byte{0x00, 0x01, 0x02, 0x03}}, 0, head.Number.Uint64(), head.Hash()},
want: errForkIDRejected,
},
{
code: StatusMsg, data: StatusPacket{uint32(protocol), 1, genesis.Hash(), forkID, head.Number.Uint64() + 1, head.Number.Uint64(), head.Hash()},
want: errInvalidBlockRange,
},
}
for i, test := range tests {
// Create the two peers to shake with each other

View file

@ -20,7 +20,6 @@ import (
"errors"
"fmt"
"io"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/forkid"
@ -30,7 +29,6 @@ import (
// Constants to match up protocol versions and messages
const (
ETH68 = 68
ETH69 = 69
)
@ -40,11 +38,11 @@ const ProtocolName = "eth"
// ProtocolVersions are the supported versions of the `eth` protocol (first
// is primary).
var ProtocolVersions = []uint{ETH69, ETH68}
var ProtocolVersions = []uint{ETH69}
// protocolLengths are the number of implemented message corresponding to
// different protocol versions.
var protocolLengths = map[uint]uint64{ETH68: 17, ETH69: 18}
var protocolLengths = map[uint]uint64{ETH69: 18}
// maxMessageSize is the maximum cap on the size of a protocol message.
const maxMessageSize = 10 * 1024 * 1024
@ -88,17 +86,7 @@ type Packet interface {
}
// StatusPacket is the network packet for the status message.
type StatusPacket68 struct {
ProtocolVersion uint32
NetworkID uint64
TD *big.Int
Head common.Hash
Genesis common.Hash
ForkID forkid.ID
}
// StatusPacket69 is the network packet for the status message.
type StatusPacket69 struct {
type StatusPacket struct {
ProtocolVersion uint32
NetworkID uint64
Genesis common.Hash
@ -109,26 +97,6 @@ type StatusPacket69 struct {
LatestBlockHash common.Hash
}
// NewBlockHashesPacket is the network packet for the block announcements.
type NewBlockHashesPacket []struct {
Hash common.Hash // Hash of one particular block being announced
Number uint64 // Number of one particular block being announced
}
// Unpack retrieves the block hashes and numbers from the announcement packet
// and returns them in a split flat format that's more consistent with the
// internal data structures.
func (p *NewBlockHashesPacket) Unpack() ([]common.Hash, []uint64) {
var (
hashes = make([]common.Hash, len(*p))
numbers = make([]uint64, len(*p))
)
for i, body := range *p {
hashes[i], numbers[i] = body.Hash, body.Number
}
return hashes, numbers
}
// TransactionsPacket is the network packet for broadcasting new transactions.
type TransactionsPacket struct {
rlp.RawList[*types.Transaction]
@ -203,12 +171,6 @@ type BlockHeadersRLPPacket struct {
BlockHeadersRLPResponse
}
// NewBlockPacket is the network packet for the block propagation message.
type NewBlockPacket struct {
Block *types.Block
TD *big.Int
}
// GetBlockBodiesRequest represents a block body query.
type GetBlockBodiesRequest []common.Hash
@ -258,19 +220,11 @@ type GetReceiptsPacket struct {
// ReceiptsResponse is the network packet for block receipts distribution.
type ReceiptsResponse []types.Receipts
// ReceiptsList is a type constraint for block receceipt list types.
type ReceiptsList interface {
*ReceiptList68 | *ReceiptList69
setBuffers(*receiptListBuffers)
EncodeForStorage() (rlp.RawValue, error)
Derivable() types.DerivableList
}
// ReceiptsPacket is the network packet for block receipts distribution with
// request ID wrapping.
type ReceiptsPacket[L ReceiptsList] struct {
type ReceiptsPacket struct {
RequestId uint64
List rlp.RawList[L]
List rlp.RawList[*ReceiptList]
}
// ReceiptsRLPResponse is used for receipts, when we already have it encoded
@ -325,14 +279,8 @@ type BlockRangeUpdatePacket struct {
LatestBlockHash common.Hash
}
func (*StatusPacket68) Name() string { return "Status" }
func (*StatusPacket68) Kind() byte { return StatusMsg }
func (*StatusPacket69) Name() string { return "Status" }
func (*StatusPacket69) Kind() byte { return StatusMsg }
func (*NewBlockHashesPacket) Name() string { return "NewBlockHashes" }
func (*NewBlockHashesPacket) Kind() byte { return NewBlockHashesMsg }
func (*StatusPacket) Name() string { return "Status" }
func (*StatusPacket) Kind() byte { return StatusMsg }
func (*TransactionsPacket) Name() string { return "Transactions" }
func (*TransactionsPacket) Kind() byte { return TransactionsMsg }
@ -349,9 +297,6 @@ func (*GetBlockBodiesRequest) Kind() byte { return GetBlockBodiesMsg }
func (*BlockBodiesResponse) Name() string { return "BlockBodies" }
func (*BlockBodiesResponse) Kind() byte { return BlockBodiesMsg }
func (*NewBlockPacket) Name() string { return "NewBlock" }
func (*NewBlockPacket) Kind() byte { return NewBlockMsg }
func (*NewPooledTransactionHashesPacket) Name() string { return "NewPooledTransactionHashes" }
func (*NewPooledTransactionHashesPacket) Kind() byte { return NewPooledTransactionHashesMsg }

View file

@ -95,8 +95,7 @@ func TestEmptyMessages(t *testing.T) {
BlockBodiesRLPPacket{1111, BlockBodiesRLPResponse([]rlp.RawValue{})},
// Receipts
GetReceiptsPacket{1111, GetReceiptsRequest([]common.Hash{})},
ReceiptsPacket[*ReceiptList68]{1111, encodeRL([]*ReceiptList68{})},
ReceiptsPacket[*ReceiptList69]{1111, encodeRL([]*ReceiptList69{})},
ReceiptsPacket{1111, encodeRL([]*ReceiptList{})},
// Transactions
GetPooledTransactionsPacket{1111, GetPooledTransactionsRequest([]common.Hash{})},
PooledTransactionsPacket{1111, encodeRL([]*types.Transaction{})},
@ -231,16 +230,11 @@ func TestMessages(t *testing.T) {
common.FromHex("f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"),
},
{
ReceiptsPacket[*ReceiptList68]{1111, encodeRL([]*ReceiptList68{NewReceiptList68(receipts)})},
common.FromHex("f902e6820457f902e0f902ddf901688082014db9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ffb9016f01f9016b018201bcb9010000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000001000000000000000000000000000000000000000000000000040000000000000000000000000004000000000000000000000000000000000000000000000000000000008000400000000000000000000000000000000000000000000000000000000000000000000000000000040f862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"),
},
{
// Identical to the eth/68 encoding above.
ReceiptsRLPPacket{1111, ReceiptsRLPResponse([]rlp.RawValue{receiptsRlp})},
common.FromHex("f902e6820457f902e0f902ddf901688082014db9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000004000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ffb9016f01f9016b018201bcb9010000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000001000000000000000000000000000000000000000000000000040000000000000000000000000004000000000000000000000000000000000000000000000000000000008000400000000000000000000000000000000000000000000000000000000000000000000000000000040f862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"),
},
{
ReceiptsPacket[*ReceiptList69]{1111, encodeRL([]*ReceiptList69{NewReceiptList69(receipts)})},
ReceiptsPacket{1111, encodeRL([]*ReceiptList{NewReceiptList(receipts)})},
common.FromHex("f8da820457f8d5f8d3f866808082014df85ff85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff86901018201bcf862f860940000000000000000000000000000000000000022f842a00000000000000000000000000000000000000000000000000000000000005668a0000000000000000000000000000000000000000000000000000000000000977386020f0f0f0608"),
},
{

View file

@ -27,9 +27,6 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
// 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 {
TxType byte
@ -49,154 +46,18 @@ func newReceipt(tr *types.Receipt) Receipt {
return r
}
// decode68 parses a receipt in the eth/68 network encoding.
func (r *Receipt) decode68(b []byte) error {
k, content, _, err := rlp.Split(b)
if err != nil {
return err
}
*r = Receipt{}
if k == rlp.List {
// Legacy receipt.
return r.decodeInnerList(b, false, true)
}
// Typed receipt.
if len(content) < 2 || len(content) > maxReceiptSize {
return fmt.Errorf("invalid receipt size %d", len(content))
}
r.TxType = content[0]
return r.decodeInnerList(content[1:], false, true)
}
// decode69 parses a receipt in the eth/69 network encoding.
func (r *Receipt) decode69(b []byte) error {
*r = Receipt{}
return r.decodeInnerList(b, true, false)
}
// decodeDatabase parses a receipt in the basic database encoding.
func (r *Receipt) decodeDatabase(txType byte, b []byte) error {
*r = Receipt{TxType: txType}
return r.decodeInnerList(b, false, false)
}
func (r *Receipt) decodeInnerList(input []byte, readTxType, readBloom bool) error {
input, _, err := rlp.SplitList(input)
if err != nil {
return fmt.Errorf("inner list: %v", err)
}
// txType
if readTxType {
var txType uint64
txType, input, err = rlp.SplitUint64(input)
if err != nil {
return fmt.Errorf("invalid txType: %w", err)
}
if txType > 0x7f {
return fmt.Errorf("invalid txType: too large")
}
r.TxType = byte(txType)
}
// status
r.PostStateOrStatus, input, err = rlp.SplitString(input)
if err != nil {
return fmt.Errorf("invalid postStateOrStatus: %w", err)
}
if len(r.PostStateOrStatus) > 1 && len(r.PostStateOrStatus) != 32 {
return fmt.Errorf("invalid postStateOrStatus length %d", len(r.PostStateOrStatus))
}
// gas
r.GasUsed, input, err = rlp.SplitUint64(input)
if err != nil {
return fmt.Errorf("invalid gasUsed: %w", err)
}
// bloom
if readBloom {
var bloomBytes []byte
bloomBytes, input, err = rlp.SplitString(input)
if err != nil {
return fmt.Errorf("invalid bloom: %v", err)
}
if len(bloomBytes) != types.BloomByteLength {
return fmt.Errorf("invalid bloom length %d", len(bloomBytes))
}
}
// logs
_, rest, err := rlp.SplitList(input)
if err != nil {
return fmt.Errorf("invalid logs: %w", err)
}
if len(rest) != 0 {
return fmt.Errorf("junk at end of receipt")
}
r.Logs = input
return nil
}
// encodeForStorage produces the the storage encoding, i.e. the result matches
// the RLP encoding of types.ReceiptForStorage.
func (r *Receipt) encodeForStorage(w *rlp.EncoderBuffer) {
list := w.List()
w.WriteBytes(r.PostStateOrStatus)
w.WriteUint64(r.GasUsed)
w.Write(r.Logs)
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) {
// EncodeForHash encodes a receipt for the block receiptsRoot derivation.
func (r *Receipt) EncodeForHash(bloomBuf *[6]byte, 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)
w := rlp.NewEncoderBuffer(out)
l := w.List()
w.WriteBytes(r.PostStateOrStatus)
w.WriteUint64(r.GasUsed)
bloom := r.bloom(&buf.bloom)
bloom := r.bloom(bloomBuf)
w.WriteBytes(bloom[:])
w.Write(r.Logs)
w.ListEnd(l)
@ -229,31 +90,31 @@ func (r *Receipt) bloom(buffer *[6]byte) types.Bloom {
return b
}
type receiptListBuffers struct {
enc rlp.EncoderBuffer
bloom [6]byte
tmp bytes.Buffer
// ReceiptList is the block receipt list as downloaded by eth/69.
type ReceiptList struct {
items rlp.RawList[Receipt]
}
func initBuffers(buf **receiptListBuffers) {
if *buf == nil {
*buf = new(receiptListBuffers)
}
}
// encodeForStorage encodes a list of receipts for the database.
func (buf *receiptListBuffers) encodeForStorage(rs rlp.RawList[Receipt], decode func([]byte, *Receipt) error) (rlp.RawValue, error) {
// EncodeForStorage encodes a list of receipts for the database.
// It only strips the first element (TxType) from each receipt's
// raw RLP without the actual decoding and re-encoding.
func (rl *ReceiptList) EncodeForStorage() (rlp.RawValue, error) {
var out bytes.Buffer
w := &buf.enc
w.Reset(&out)
w := rlp.NewEncoderBuffer(&out)
outer := w.List()
it := rs.ContentIterator()
it := rl.items.ContentIterator()
for it.Next() {
var receipt Receipt
if err := decode(it.Value(), &receipt); err != nil {
return nil, err
content, _, err := rlp.SplitList(it.Value())
if err != nil {
return nil, fmt.Errorf("bad receipt: %v", err)
}
receipt.encodeForStorage(w)
_, _, rest, err := rlp.Split(content)
if err != nil {
return nil, fmt.Errorf("bad receipt: %v", err)
}
inner := w.List()
w.Write(rest)
w.ListEnd(inner)
}
if it.Err() != nil {
return nil, fmt.Errorf("bad list: %v", it.Err())
@ -263,149 +124,32 @@ func (buf *receiptListBuffers) encodeForStorage(rs rlp.RawList[Receipt], decode
return out.Bytes(), nil
}
// 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 rlp.RawList[Receipt]
}
// NewReceiptList68 creates a receipt list.
// NewReceiptList creates a receipt list.
// This is slow, and exists for testing purposes.
func NewReceiptList68(trs []*types.Receipt) *ReceiptList68 {
rl := new(ReceiptList68)
initBuffers(&rl.buf)
enc := rlp.NewEncoderBuffer(nil)
func NewReceiptList(trs []*types.Receipt) *ReceiptList {
rl := new(ReceiptList)
for _, tr := range trs {
r := newReceipt(tr)
r.encodeForNetwork68(rl.buf, &enc)
rl.items.AppendRaw(enc.ToBytes())
enc.Reset(nil)
encoded, _ := rlp.EncodeToBytes(&r)
rl.items.AppendRaw(encoded)
}
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)
w := rlp.NewEncoderBuffer(&out)
outer := w.List()
for i := 0; blockReceiptIter.Next(); i++ {
txType, _ := nextTxType()
var r Receipt
if err := r.decodeDatabase(txType, blockReceiptIter.Value()); 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, error) {
initBuffers(&rl.buf)
return rl.buf.encodeForStorage(rl.items, func(data []byte, r *Receipt) error {
return r.decode68(data)
})
}
// Derivable turns the receipts into a list that can derive the root hash.
func (rl *ReceiptList68) Derivable() types.DerivableList {
initBuffers(&rl.buf)
return newDerivableRawList(&rl.items, func(data []byte, outbuf *bytes.Buffer) {
var r Receipt
if r.decode68(data) == nil {
r.encodeForHash(rl.buf, outbuf)
}
})
}
// DecodeRLP decodes a list of receipts from the network format.
func (rl *ReceiptList68) DecodeRLP(s *rlp.Stream) error {
return rl.items.DecodeRLP(s)
}
// EncodeRLP encodes the list into the network format of eth/68.
func (rl *ReceiptList68) EncodeRLP(w io.Writer) error {
return rl.items.EncodeRLP(w)
}
// ReceiptList69 is the block receipt list as downloaded by eth/69.
// This implements types.DerivableList for validation purposes.
type ReceiptList69 struct {
buf *receiptListBuffers
items rlp.RawList[Receipt]
}
// NewReceiptList69 creates a receipt list.
// This is slow, and exists for testing purposes.
func NewReceiptList69(trs []*types.Receipt) *ReceiptList69 {
rl := new(ReceiptList69)
enc := rlp.NewEncoderBuffer(nil)
for _, tr := range trs {
r := newReceipt(tr)
r.encodeForNetwork69(&enc)
rl.items.AppendRaw(enc.ToBytes())
enc.Reset(nil)
}
return rl
}
// 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, error) {
initBuffers(&rl.buf)
return rl.buf.encodeForStorage(rl.items, func(data []byte, r *Receipt) error {
return r.decode69(data)
})
}
// Derivable turns the receipts into a list that can derive the root hash.
func (rl *ReceiptList69) Derivable() types.DerivableList {
initBuffers(&rl.buf)
return newDerivableRawList(&rl.items, func(data []byte, outbuf *bytes.Buffer) {
var r Receipt
if r.decode69(data) == nil {
r.encodeForHash(rl.buf, outbuf)
}
})
}
// DecodeRLP decodes a list receipts from the network format.
func (rl *ReceiptList69) DecodeRLP(s *rlp.Stream) error {
func (rl *ReceiptList) DecodeRLP(s *rlp.Stream) error {
return rl.items.DecodeRLP(s)
}
// EncodeRLP encodes the list into the network format of eth/69.
func (rl *ReceiptList69) EncodeRLP(w io.Writer) error {
func (rl *ReceiptList) EncodeRLP(w io.Writer) error {
return rl.items.EncodeRLP(w)
}
// blockReceiptsToNetwork69 takes a slice of rlp-encoded receipts, and transactions,
// encodeTypes takes a slice of rlp-encoded receipts, and transactions,
// and applies the type-encoding on the receipts (for non-legacy receipts).
// e.g. for non-legacy receipts: receipt-data -> {tx-type || receipt-data}
func blockReceiptsToNetwork69(blockReceipts, blockBody rlp.RawValue) ([]byte, error) {
func encodeTypes(blockReceipts, blockBody rlp.RawValue) ([]byte, error) {
txTypesIter, err := txTypesInBody(blockBody)
if err != nil {
return nil, fmt.Errorf("invalid block body: %v", err)

View file

@ -95,7 +95,7 @@ func init() {
}
}
func TestReceiptList69(t *testing.T) {
func TestReceiptList(t *testing.T) {
for i, test := range receiptsTests {
// encode receipts from types.ReceiptForStorage object.
canonDB, _ := rlp.EncodeToBytes(test.input)
@ -105,13 +105,13 @@ func TestReceiptList69(t *testing.T) {
canonBody, _ := rlp.EncodeToBytes(blockBody)
// convert from storage encoding to network encoding
network, err := blockReceiptsToNetwork69(canonDB, canonBody)
network, err := encodeTypes(canonDB, canonBody)
if err != nil {
t.Fatalf("test[%d]: blockReceiptsToNetwork69 error: %v", i, err)
t.Fatalf("test[%d]: encodeTypes error: %v", i, err)
}
// parse as Receipts response list from network encoding
var rl ReceiptList69
var rl ReceiptList
if err := rlp.DecodeBytes(network, &rl); err != nil {
t.Fatalf("test[%d]: can't decode network receipts: %v", i, err)
}
@ -127,50 +127,13 @@ func TestReceiptList69(t *testing.T) {
t.Fatalf("test[%d]: re-encoded network receipt list not equal\nhave: %x\nwant: %x", i, rlNetworkEnc, network)
}
// compute root hash from ReceiptList69 and compare.
responseHash := types.DeriveSha(rl.Derivable(), trie.NewStackTrie(nil))
// compute root hash from ReceiptList and compare.
var bloomBuf [6]byte
writeReceipt := writeReceiptForHash(&bloomBuf)
receipts := newDerivableRawList(&rl.items, writeReceipt)
responseHash := types.DeriveSha(receipts, trie.NewStackTrie(nil))
if responseHash != test.root {
t.Fatalf("test[%d]: wrong root hash from ReceiptList69\nhave: %v\nwant: %v", i, responseHash, test.root)
}
}
}
func TestReceiptList68(t *testing.T) {
for i, test := range receiptsTests {
// encode receipts from types.ReceiptForStorage object.
canonDB, _ := rlp.EncodeToBytes(test.input)
// encode block body from types object.
blockBody := types.Body{Transactions: test.txs}
canonBody, _ := rlp.EncodeToBytes(blockBody)
// convert from storage encoding to network encoding
network, err := blockReceiptsToNetwork68(canonDB, canonBody)
if err != nil {
t.Fatalf("test[%d]: blockReceiptsToNetwork68 error: %v", i, err)
}
// parse as Receipts response list from network encoding
var rl ReceiptList68
if err := rlp.DecodeBytes(network, &rl); err != nil {
t.Fatalf("test[%d]: can't decode network receipts: %v", i, err)
}
rlStorageEnc, err := rl.EncodeForStorage()
if err != nil {
t.Fatalf("test[%d]: error from EncodeForStorage: %v", i, err)
}
if !bytes.Equal(rlStorageEnc, canonDB) {
t.Fatalf("test[%d]: re-encoded receipts not equal\nhave: %x\nwant: %x", i, rlStorageEnc, canonDB)
}
rlNetworkEnc, _ := rlp.EncodeToBytes(&rl)
if !bytes.Equal(rlNetworkEnc, network) {
t.Fatalf("test[%d]: re-encoded network receipt list not equal\nhave: %x\nwant: %x", i, rlNetworkEnc, network)
}
// compute root hash from ReceiptList68 and compare.
responseHash := types.DeriveSha(rl.Derivable(), trie.NewStackTrie(nil))
if responseHash != test.root {
t.Fatalf("test[%d]: wrong root hash from ReceiptList68\nhave: %v\nwant: %v", i, responseHash, test.root)
t.Fatalf("test[%d]: wrong root hash from ReceiptList\nhave: %v\nwant: %v", i, responseHash, test.root)
}
}
}

View file

@ -28,7 +28,7 @@ import (
)
// Tests that snap sync is disabled after a successful sync cycle.
func TestSnapSyncDisabling68(t *testing.T) { testSnapSyncDisabling(t, eth.ETH68, snap.SNAP1) }
func TestSnapSyncDisabling69(t *testing.T) { testSnapSyncDisabling(t, eth.ETH69, snap.SNAP1) }
// Tests that snap sync gets disabled as soon as a real block is successfully
// imported into the blockchain.