Skip to content

eth/protocols/eth/handlers_pow.go — Version Comparison

v1.17.3 v1.17.4
Branch etc/v1.17.3-full-node etc/v1.17.4-full-node
Delta +82 -0 +206 -0

Diff between branches

diff --git a/eth/protocols/eth/handlers_pow.go b/eth/protocols/eth/handlers_pow.go
index 340e4d7cb..e6b1c33dd 100644
--- a/eth/protocols/eth/handlers_pow.go
+++ b/eth/protocols/eth/handlers_pow.go
@@ -17,10 +17,15 @@
 package eth

 import (
+   "fmt"
    "time"

+   "github.com/ethereum/go-ethereum/common"
+   "github.com/ethereum/go-ethereum/core"
    "github.com/ethereum/go-ethereum/core/types"
    "github.com/ethereum/go-ethereum/log"
+   "github.com/ethereum/go-ethereum/p2p/tracker"
+   "github.com/ethereum/go-ethereum/rlp"
    "github.com/ethereum/go-ethereum/trie"
 )

@@ -34,8 +39,8 @@ func init() {
        BlockHeadersMsg:               handleBlockHeaders,
        GetBlockBodiesMsg:             handleGetBlockBodies,
        BlockBodiesMsg:                handleBlockBodies,
-       GetReceiptsMsg:                handleGetReceipts69,
-       ReceiptsMsg:                   handleReceipts69,
+       GetReceiptsMsg:                handleGetReceipts68,
+       ReceiptsMsg:                   handleReceipts68,
        GetPooledTransactionsMsg:      handleGetPooledTransactions,
        PooledTransactionsMsg:         handlePooledTransactions,
    }
@@ -47,6 +52,9 @@ func handleNewBlockhashesPow(backend Backend, msg Decoder, peer *Peer) error {
    if err := msg.Decode(ann); err != nil {
        return err
    }
+   if len(*ann) > maxBlockAnnouncements {
+       return fmt.Errorf("too many block announcements: %d > %d", len(*ann), maxBlockAnnouncements)
+   }
    // Mark the hashes as present at the remote node
    for _, block := range *ann {
        peer.markBlock(block.Hash)
@@ -56,27 +64,143 @@ func handleNewBlockhashesPow(backend Backend, msg Decoder, peer *Peer) error {
 }

 func handleNewBlockPow(backend Backend, msg Decoder, peer *Peer) error {
-   // Retrieve and decode the propagated block
-   ann := new(NewBlockPacket)
+   // Retrieve the propagated block, leaving its body encoded for now
+   ann := new(rawNewBlockPacket)
    if err := msg.Decode(ann); err != nil {
        return err
    }
    if err := ann.sanityCheck(); err != nil {
        return err
    }
-   if hash := types.CalcUncleHash(ann.Block.Uncles()); hash != ann.Block.UncleHash() {
-       log.Warn("Propagated block has invalid uncles", "have", hash, "exp", ann.Block.UncleHash())
+   // Bound what the body may expand into. Decoding into rlp.RawList only counts
+   // the items, so this runs before any transaction or uncle is materialized.
+   if txs := ann.Block.Transactions.Len(); txs > maxBlockTransactions {
+       return fmt.Errorf("too many transactions in propagated block: %d > %d", txs, maxBlockTransactions)
+   }
+   if uncles := ann.Block.Uncles.Len(); uncles > maxBlockUncles {
+       return fmt.Errorf("too many uncles in propagated block: %d > %d", uncles, maxBlockUncles)
+   }
+   // Check the body against the header on the encoded form, so a block that is
+   // not what it claims to be is dropped without being materialized either.
+   header := ann.Block.Header
+   roots := hashBodyParts([]BlockBody{ann.Block.body()})
+   if hash := roots.UncleHashes[0]; hash != header.UncleHash {
+       log.Warn("Propagated block has invalid uncles", "have", hash, "exp", header.UncleHash)
        return nil // TODO(karalabe): return error eventually, but wait a few releases
    }
-   if hash := types.DeriveSha(ann.Block.Transactions(), trie.NewStackTrie(nil)); hash != ann.Block.TxHash() {
-       log.Warn("Propagated block has invalid body", "have", hash, "exp", ann.Block.TxHash())
+   if hash := roots.TransactionRoots[0]; hash != header.TxHash {
+       log.Warn("Propagated block has invalid body", "have", hash, "exp", header.TxHash)
        return nil // TODO(karalabe): return error eventually, but wait a few releases
    }
-   ann.Block.ReceivedAt = time.Now()
-   ann.Block.ReceivedFrom = peer
+   // Bounded and self-consistent, so it is safe to materialize now.
+   txs, err := ann.Block.Transactions.Items()
+   if err != nil {
+       return fmt.Errorf("NewBlock: %w", err)
+   }
+   uncles, err := ann.Block.Uncles.Items()
+   if err != nil {
+       return fmt.Errorf("NewBlock: %w", err)
+   }
+   block := types.NewBlockWithHeader(header).WithBody(types.Body{Transactions: txs, Uncles: uncles})
+   block.ReceivedAt = time.Now()
+   block.ReceivedFrom = peer

    // Mark the peer as owning the block
-   peer.markBlock(ann.Block.Hash())
+   peer.markBlock(block.Hash())

-   return backend.Handle(peer, ann)
+   return backend.Handle(peer, &NewBlockPacket{Block: block, TD: ann.TD})
+}
+
+func handleGetReceipts68(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)
+   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 {
+   // 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
+           }
+           results = rlp.EmptyList
+       } else {
+           body := chain.GetBodyRLP(hash)
+           if body == nil {
+               continue
+           }
+           var err error
+           results, err = blockReceiptsToNetwork68(results, body)
+           if err != nil {
+               log.Error("Error in block receipts conversion", "hash", hash, "err", err)
+               continue
+           }
+       }
+       receipts = append(receipts, results)
+       bytes += len(results)
+   }
+   return receipts
+}
+
+func handleReceipts68(backend Backend, msg Decoder, peer *Peer) error {
+   // A batch of receipts arrived to one of our previous requests
+   res := new(ReceiptsPacket68)
+   if err := msg.Decode(res); err != nil {
+       return err
+   }
+
+   tresp := tracker.Response{ID: res.RequestId, MsgCode: ReceiptsMsg, Size: res.List.Len()}
+   if err := peer.tracker.Fulfil(tresp); err != nil {
+       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)
+   }
+
+   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)
+       }
+       return hashes
+   }
+   var enc ReceiptsRLPResponse
+   for i := range receiptLists {
+       encReceipts, err := receiptLists[i].EncodeForStorage()
+       if err != nil {
+           return fmt.Errorf("Receipts: invalid list %d: %v", i, err)
+       }
+       enc = append(enc, encReceipts)
+   }
+   return peer.dispatchResponse(&Response{
+       id:   res.RequestId,
+       code: ReceiptsMsg,
+       Res:  &enc,
+   }, metadata)
 }
← Back to Version Comparison