diff --git a/cmd/devp2p/internal/ethtest/conn.go b/cmd/devp2p/internal/ethtest/conn.go index 5182d71ce1..98baba81a4 100644 --- a/cmd/devp2p/internal/ethtest/conn.go +++ b/cmd/devp2p/internal/ethtest/conn.go @@ -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 = ð.StatusPacket69{ + status = ð.StatusPacket{ ProtocolVersion: uint32(c.negotiatedProtoVersion), NetworkID: chain.config.ChainID.Uint64(), Genesis: chain.blocks[0].Hash(), diff --git a/cmd/devp2p/internal/ethtest/suite.go b/cmd/devp2p/internal/ethtest/suite.go index 8bb488e358..7560c13137 100644 --- a/cmd/devp2p/internal/ethtest/suite.go +++ b/cmd/devp2p/internal/ethtest/suite.go @@ -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) } diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go index e5d4a7c59b..42bb15f8f7 100644 --- a/eth/downloader/downloader_test.go +++ b/eth/downloader/downloader_test.go @@ -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 := ð.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{}) diff --git a/eth/downloader/skeleton_test.go b/eth/downloader/skeleton_test.go index 5c54b4b5c2..8c38e9d0c5 100644 --- a/eth/downloader/skeleton_test.go +++ b/eth/downloader/skeleton_test.go @@ -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 diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go index 0330713071..68e91fa897 100644 --- a/eth/handler_eth_test.go +++ b/eth/handler_eth_test.go @@ -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() diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go index 2467e0c713..aa1c7d45bc 100644 --- a/eth/protocols/eth/handler.go +++ b/eth/protocols/eth/handler.go @@ -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) diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go index 8f7f82c3a1..6c3f7af2bd 100644 --- a/eth/protocols/eth/handler_test.go +++ b/eth/protocols/eth/handler_test.go @@ -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 ( diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go index 7f1ccc360d..56b4a4cd7f 100644 --- a/eth/protocols/eth/handlers.go +++ b/eth/protocols/eth/handlers.go @@ -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() diff --git a/eth/protocols/eth/handshake.go b/eth/protocols/eth/handshake.go index 4d1b6e9de7..359e4e36bb 100644 --- a/eth/protocols/eth/handshake.go +++ b/eth/protocols/eth/handshake.go @@ -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 } diff --git a/eth/protocols/eth/handshake_test.go b/eth/protocols/eth/handshake_test.go index 2fab3ea5a8..e2f1e7592a 100644 --- a/eth/protocols/eth/handshake_test.go +++ b/eth/protocols/eth/handshake_test.go @@ -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 diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go index 6ab800f4f4..41eabe78f1 100644 --- a/eth/protocols/eth/protocol.go +++ b/eth/protocols/eth/protocol.go @@ -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 } diff --git a/eth/protocols/eth/protocol_test.go b/eth/protocols/eth/protocol_test.go index e37d72dcd6..310ae2c7e0 100644 --- a/eth/protocols/eth/protocol_test.go +++ b/eth/protocols/eth/protocol_test.go @@ -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"), }, { diff --git a/eth/protocols/eth/receipt.go b/eth/protocols/eth/receipt.go index 06956df9f2..e812b43913 100644 --- a/eth/protocols/eth/receipt.go +++ b/eth/protocols/eth/receipt.go @@ -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) diff --git a/eth/protocols/eth/receipt_test.go b/eth/protocols/eth/receipt_test.go index 39a2728f7f..c7be70eed3 100644 --- a/eth/protocols/eth/receipt_test.go +++ b/eth/protocols/eth/receipt_test.go @@ -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) } } } diff --git a/eth/sync_test.go b/eth/sync_test.go index 509b836f82..2bf911e0b7 100644 --- a/eth/sync_test.go +++ b/eth/sync_test.go @@ -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.