This is an implementation of #5982 (and based off @sipa’s commit in #5307, which has been squashed into this commit).
Currently new blocks are announced via inv messages. Since headers-first download was introduced in 0.10, blocks are not processed unless the headers for the block connect to known, valid headers, so peers receiving inv messages respond with a getheaders and a getdata: the expectation is that the announcing peer will send the headers for the block and any parents unknown to the peer before delivering the new block.
In the case of a reorg, nodes currently announce only the new tip, and not the blocks that lead to the tip. This means that an extra round-trip is required to relay a reorg, because the peer receiving the inv for the tip must wait for the response to the getheaders message before being able to request the missing blocks. (For additional reasons discussed in #5307 (comment), it’s not optimal to inv all the blocks added in the reorg to a headers-first peer, because this results in needless traffic due to the way getheaders requests are generated when direct-fetching inv‘ed blocks.)
This pull implements a new sendheaders p2p message, which a node sends to its peers to indicate that the node prefers to receive new block announcements via a headers message rather than an inv. This implementation does a few things:
- When announcing a new block to a peer that prefers headers, try to announce all the blocks that are being added, back to the last fork point, by sending headers for each.
- Avoid sending headers for blocks that are already known to the peer (determined by checking
pindexBestKnownBlockandpindexBestHeaderSentand their ancestors).- pindexBestKnownBlock is updated based on
invmessages,headerssent from that peer, andgetheadersmessages sent from the peer. pindexBestHeaderSentis a new variable that tracks the best header we have sent to the peer
- pindexBestKnownBlock is updated based on
- Avoid sending more than 8 headers to the peer. This code is designed to be optimized for relaying at the tip, so a large reorg is some kind of error condition; in that case avoid spamming the peer with a lot of headers and instead fall back to the old
invmechanism. - If the headers to be announced aren’t known to connect to headers that the peer has, then revert to sending an
invat the tip instead. This is designed to avoid DoS points from sending headers that don’t connect. Since every new block should generally be announced by one peer in any pair of peers, it’s expected that once headers-announcement begins on a link (ie once two nodes are known to be synced), it should be able to continue. - After #6148 is merged, this would allow pruning nodes to generally be able to relay reorgs to peers via headers announcements. The code that implements direct fetching upon receipt of a headers message will send
getdatarequests to any peer that announces a block via a headers message.
This pull includes a new python p2p test exercising the new functionality.
BIP 130 describes the proposed new sendheaders p2p message here:
https://github.com/bitcoin/bips/blob/master/bip-0130.mediawiki