{"number":332,"title":"Stale Tip Relay","authors":["Anthony Towns","w0xlt","Ram"],"status":"Draft","type":"Specification","layer":"Peer Services","created":"2026-08-21","license":"BSD-3-Clause","version":"0.1.0","requires":[434],"replaces":[],"replacedBy":[],"discussion":["https://gnusha.org/pi/bitcoindev/d92f1615-368b-4406-b326-a1799c72a555n@googlegroups.com/"],"abstract":"Bitcoin miners sporadically produce stale blocks: valid blocks or valid block headers that do not become part of the node's active chain. This BIP defines a new optional peer-to-peer (P2P) staletip message for announcing recent stale chain tips to peers. The announcement includes the stale branch headers and whether the sender is willing to serve corresponding stale block data.","eli5":"Defines an optional message for nodes to tell peers about recent stale blocks, valid blocks that did not end up in the main chain. Sharing them helps researchers and operators monitor mining activity and network health.","categories":["network"],"filename":"bip-0332.md","githubUrl":"https://github.com/bitcoin/bips/blob/master/bip-0332.md","format":"markdown","readingMinutes":22,"inbound":0,"revisions":1,"impact":0,"core":false,"references":[],"lastModified":"2026-08-21","html":"<h2 id=\"abstract\">Abstract</h2>\n<p>Bitcoin miners sporadically produce stale blocks: valid blocks or valid block\nheaders that do not become part of the node&#39;s active chain. This BIP defines a\nnew optional peer-to-peer (P2P) <code>staletip</code> message for announcing recent stale\nchain tips to peers. The announcement includes the stale branch headers and\nwhether the sender is willing to serve corresponding stale block data.</p>\n<h2 id=\"motivation\">Motivation</h2>\n<p>Stale blocks are a potentially useful signal about the health of the network&#39;s\nblock-relay layer. The rate at which they occur tracks how quickly new blocks\nreach the rest of the miners, and an elevated rate can be a symptom of a range\nof network-related problems – degraded or congested propagation, network\npartitions, or adversarial mining behavior such as selfish mining.</p>\n<p>An added benefit is that when a stale block has the same cumulative proof of\nwork as the active tip, the current active tip may be re-orged out in favor of a\nchild of the stale block; having the block already downloaded (and possibly already\nvalidated) makes the reorg faster. The contents of stale blocks additionally\nreveal differences in block-creation policy between mining pools.</p>\n<p>These benefits are not essential to the operation of the Bitcoin network, so\nthis is proposed as an optional feature, with low performance demands and strict\nresource limits.</p>\n<h2 id=\"specification\">Specification</h2>\n<p>The key words &quot;MUST&quot;, &quot;MUST NOT&quot;, &quot;REQUIRED&quot;, &quot;SHOULD&quot;, &quot;SHOULD NOT&quot;,\n&quot;RECOMMENDED&quot;, &quot;MAY&quot;, and &quot;OPTIONAL&quot; in this document are to be interpreted as\ndescribed in RFC 2119.</p>\n<p>For the purposes of this document, &quot;active chain&quot; means the chain that a node\nhas selected as its current best chain. A &quot;stale branch&quot; is a sequence of valid\nheaders that descends from a known block but whose tip is not on the node&#39;s\nactive chain. The &quot;stale tip&quot; is the last header in that branch.</p>\n<h3 id=\"the-staletip-message\">The <code>staletip</code> Message</h3>\n<p>The <code>staletip</code> P2P message has the ASCII message type <code>staletip</code> and the\nfollowing payload:</p>\n<table>\n<thead>\n<tr>\n<th>Type</th>\n<th>Name</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>uint256</code></td>\n<td><code>fork_point</code></td>\n<td>The block hash used as the previous block hash of the first compressed header</td>\n</tr>\n<tr>\n<td>vector of <code>CompressedHeader</code></td>\n<td><code>headers</code></td>\n<td>The headers forming a single stale branch after <code>fork_point</code>, in chain order</td>\n</tr>\n<tr>\n<td><code>bool</code></td>\n<td><code>have_block</code></td>\n<td>Whether the sender is willing to serve block data for the stale branch</td>\n</tr>\n</tbody></table>\n<p>The <code>fork_point</code> MUST be known by the receiver and MUST be the predecessor of\nthe first reconstructed header. In the common case, it is the last common block\nbefore the stale branch diverges from the receiver&#39;s active chain. It MAY be a\nlater known stale-branch header if both peers are expected to already know that\nheader; in that case the <code>headers</code> vector only contains the unknown suffix.</p>\n<p>The <code>headers</code> field MUST describe a single linear branch. The first header\nimmediately follows <code>fork_point</code>, and each subsequent header immediately\nfollows the preceding header. The final header is the stale tip. A separate\n<code>staletip</code> message is required for each competing branch that the sender\nchooses to announce.</p>\n<p>The <code>headers</code> vector is serialized as normal: encode the length of the vector as\na 1, 3, 5 or 9 byte <code>CompactSize</code>, then serialize each member of the vector.\nOnly minimally-encoded <code>CompactSize</code> values are supported.</p>\n<p>The <code>bool</code> serialization is a single byte <code>\\x00</code> for false, and a single byte\n<code>\\x01</code> for true. Other encodings are malformed.</p>\n<p><code>have_block</code> set to <code>true</code> means the sender currently has, and is willing to\nserve through normal block download mechanisms, the full block data for the\nstale tip, which is the final header in this <code>staletip</code> message. A sender that\nsets <code>have_block</code> to <code>true</code> SHOULD additionally be willing to serve earlier\nblocks on the announced stale branch that it has. <code>have_block</code> set to <code>false</code>\nmakes no claim about stale-branch block-data availability. A sender SHOULD NOT\nset <code>have_block</code> to <code>true</code> unless it expects a request for the stale tip block to\nsucceed.</p>\n<p>Because stale tips are very rare, this BIP does not reserve a 1-byte <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki\">BIP\n324</a> message type ID for the <code>staletip</code> message.</p>\n<h4 id=\"compressedheader-format\"><code>CompressedHeader</code> Format</h4>\n<p>The <code>CompressedHeader</code> is a 48 byte structure equivalent to regular block header\nserialization, but omits the previous block&#39;s hash[^rat-compressedheader]. The\nfields are serialized exactly as the corresponding fields are serialized in a\nBitcoin block header:</p>\n<table>\n<thead>\n<tr>\n<th>Size</th>\n<th>Name</th>\n<th>Type</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>4</td>\n<td><code>version</code></td>\n<td><code>int32_t</code></td>\n<td>Block version information</td>\n</tr>\n<tr>\n<td>32</td>\n<td><code>merkle_root</code></td>\n<td><code>uint256</code></td>\n<td>The Merkle root of the block&#39;s transactions</td>\n</tr>\n<tr>\n<td>4</td>\n<td><code>time</code></td>\n<td><code>uint32_t</code></td>\n<td>The block&#39;s timestamp</td>\n</tr>\n<tr>\n<td>4</td>\n<td><code>bits</code></td>\n<td><code>uint32_t</code></td>\n<td>The calculated difficulty target being used for this block</td>\n</tr>\n<tr>\n<td>4</td>\n<td><code>nonce</code></td>\n<td><code>uint32_t</code></td>\n<td>The nonce used to generate this block</td>\n</tr>\n</tbody></table>\n<h3 id=\"protocol-constants\">Protocol Constants</h3>\n<p>Implementations of this BIP use the following limits unless otherwise noted:</p>\n<table>\n<thead>\n<tr>\n<th>Name</th>\n<th>Value</th>\n<th>Meaning</th>\n</tr>\n</thead>\n<tbody><tr>\n<td><code>MAX_STALETIP_HEADERS</code></td>\n<td>20</td>\n<td>Recommended maximum number of <code>CompressedHeader</code> entries in one <code>staletip</code> message</td>\n</tr>\n<tr>\n<td><code>STALETIP_RECENT_WINDOW</code></td>\n<td>1000 blocks</td>\n<td>Recommended maximum distance from the receiver&#39;s active tip</td>\n</tr>\n<tr>\n<td><code>MAX_ADVERTISED_STALETIPS</code></td>\n<td>10</td>\n<td>Recommended maximum number of previously known stale tips advertised to a peer</td>\n</tr>\n</tbody></table>\n<p><code>MAX_STALETIP_HEADERS</code> is a resource-management limit[^rat-maxforklen]. The\nvalue of 20 is intended to cover the short stale branches useful for reorg\ndetection while avoiding long-term tracking of persistent chain splits.</p>\n<p><code>STALETIP_RECENT_WINDOW</code> is a resource-management limit[^rat-maxheight]. The\n1000-block window is about seven days and balances limiting resource usage with\nallowing stale tips to propagate.</p>\n<p><code>MAX_ADVERTISED_STALETIPS</code> is a bandwidth limit[^rat-denialofservice]. Advertising\nup to 10 previously known stale tips, combined with the 20-header branch limit,\nkeeps informing a new peer of known stale tips under 10kB. It does not limit\nhow many stale tips a node retains, or how many newly learned tips it relays\nover the lifetime of a connection. Implementations MAY use stricter limits. A\npeer SHOULD NOT be punished solely because a single announcement exceeds local\npolicy limits.</p>\n<h3 id=\"staletip-feature-definition\">Staletip Feature Definition</h3>\n<p>This BIP defines a new <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0434.md\">BIP 434</a> feature id (&quot;the <code>staletip</code> feature&quot;):</p>\n<ul>\n<li><code>BIP332</code></li>\n</ul>\n<p>The <code>staletip</code> feature data MUST contain exactly one byte. This byte is a\nboolean, <code>prefers_blocks</code>, indicating whether the node advertising the feature\nprefers to collect the full block data associated with stale tips:</p>\n<ul>\n<li><code>\\x00</code>: the node prefers header-only announcements and does not request that\nannouncements be delayed until block data is available.</li>\n<li><code>\\x01</code>: the node prefers announcements that include availability of block\ndata where practical.</li>\n</ul>\n<p>Nodes receiving <code>staletip</code> feature data whose length is not exactly one byte,\nor whose byte is not <code>\\x00</code> or <code>\\x01</code>, MUST ignore that peer&#39;s <code>staletip</code>\nfeature advertisement. They SHOULD NOT disconnect solely because the feature\ndata is not understood.</p>\n<h3 id=\"bip-434-negotiation\">BIP 434 Negotiation</h3>\n<p>Nodes implementing this specification MUST use BIP 434 to negotiate support\nbefore sending any <code>staletip</code> messages.</p>\n<p>Nodes MUST NOT send a <code>staletip</code> message to a peer unless that peer advertised a\nvalid <code>staletip</code> feature during BIP 434 feature negotiation. Nodes SHOULD ignore\n<code>staletip</code> messages received from peers that did not advertise a valid\n<code>staletip</code> feature. Nodes MAY disconnect peers that repeatedly send such\nmessages without negotiation.</p>\n<p>Advertising the feature only signals willingness to receive <code>staletip</code> messages.\nIt does not oblige a node to send them or to serve block data. A node MAY\nadvertise the feature to receive stale tips while never sending any itself, for\nexample a node that only collects stale tips for research.</p>\n<h3 id=\"sending-staletip-messages\">Sending <code>staletip</code> Messages</h3>\n<p>Nodes implementing this BIP MAY send <code>staletip</code> messages to advertise recent\nstale tips they are aware of. If so:</p>\n<ul>\n<li>Nodes SHOULD send <code>staletip</code> messages advertising recent stale tips\nthat they are aware of to peers that support the <code>staletip</code> feature.</li>\n<li>Nodes SHOULD NOT advertise stale tips that violate header consensus rules\n(invalid version, invalid timestamps, invalid difficulty changes,\ninsufficient proof of work).</li>\n<li>Nodes MUST NOT send <code>staletip</code> messages if they are not sure that the\n<code>fork_point</code> is a block that their peer is aware of. This can be\nestimated by tracking the highest work blocks announced by the peer,\nand assuming the peer is treating that block as its active tip. Provided\nthe <code>fork_point</code> is an ancestor of that highest work block, the peer\nwill be aware of the <code>fork_point</code>.</li>\n<li>The <code>fork_point</code> SHOULD be chosen to minimise the number of entries\nin the <code>headers</code> vector, while still being a block that the peer already\nhas header information for.</li>\n<li>Nodes MAY choose a <code>fork_point</code> based on their own active chain,\neven if that results in the <code>headers</code> vector repeating some headers\nthat the peer already knows.</li>\n<li>Nodes SHOULD NOT advertise stale tips whose tip height is more than\n<code>STALETIP_RECENT_WINDOW</code> blocks behind the sender&#39;s active tip.</li>\n<li>Nodes SHOULD NOT advertise stale branches longer than <code>MAX_STALETIP_HEADERS</code>\nheaders.</li>\n<li>Nodes SHOULD apply proof-of-work or chainwork thresholds sufficient to avoid\nusing this message as a low-cost spam channel[^rat-denialofservice].</li>\n<li>When informing a peer of stale tips that were already known when stale-tip\nrelay with that peer began, nodes SHOULD advertise no more than\n<code>MAX_ADVERTISED_STALETIPS</code> eligible tips. If more tips are eligible, nodes\nSHOULD select those with the greatest chainwork, preferring the most recently\nlearned tip when chainwork is equal. Implementations MAY instead select a\ndifferent subset, for example by sampling independently for each peer to\nincrease the diversity of stale tips available across the network. This limit\ndoes not prevent relaying newly learned stale tips to an existing peer.</li>\n<li>Nodes SHOULD avoid advertising the same tip to the same peer repeatedly via\nmultiple <code>staletip</code> messages.</li>\n<li>Nodes SHOULD respect the peer&#39;s <code>prefers_blocks</code> setting. If a peer sets\n<code>prefers_blocks</code> to <code>\\x00</code>, stale tips SHOULD be relayed to that peer\npromptly, without waiting to obtain block data. Conversely, for a peer that\nsets <code>prefers_blocks</code> to <code>\\x01</code>, a node that does attempt to obtain block\ndata SHOULD defer sending the <code>staletip</code> message to that peer until it has\nobtained the relevant block data, unless doing so would substantially delay\npropagation.</li>\n<li>Nodes that have the block data corresponding to the tip block (and will\nprovide that data to peers that request it) SHOULD set <code>have_block</code>\nas <code>true</code>. A sender that sets <code>have_block</code> to <code>true</code> SHOULD additionally be\nwilling to serve earlier blocks on the announced stale branch that it has.\nNodes without the full tip block data, or that are unwilling to relay the data\nto peers, SHOULD set <code>have_block</code> as <code>false</code>.</li>\n</ul>\n<h3 id=\"receiving-staletip-messages\">Receiving <code>staletip</code> Messages</h3>\n<p>Nodes implementing this BIP MAY process <code>staletip</code> messages from peers to gain\nmore knowledge about stale tips. If so:</p>\n<ul>\n<li>Nodes SHOULD reject messages whose payload cannot be parsed exactly as a\n<code>staletip</code> payload, including non-minimally encoded <code>CompactSize</code> values,\ntruncated data, invalid boolean values, or trailing bytes. Nodes MAY\ndisconnect peers for malformed payloads.</li>\n<li>Nodes SHOULD reject (ignore) <code>staletip</code> messages where the <code>fork_point</code> is\nnot known, and MAY disconnect peers that repeatedly send unknown fork points. \nSending such a message violates the requirement above that senders MUST NOT \nsend <code>staletip</code> messages unless they are sure the receiver knows the\n<code>fork_point</code> block.</li>\n<li>Nodes SHOULD reject messages where the <code>headers</code> vector is empty, and MAY\ndisconnect the sending peer if this occurs.</li>\n<li>When processing a <code>staletip</code> message, nodes MUST bound the resources they\ndevote to it so that a denial-of-service vector is not\ncreated[^rat-denialofservice]. Enforcing the <code>MAX_STALETIP_HEADERS</code> and\n<code>STALETIP_RECENT_WINDOW</code> limits, together with implementation-specific storage\nlimits, is one way to satisfy this requirement; nodes MAY apply stricter\nlimits.</li>\n<li>Nodes MAY ignore messages where the <code>headers</code> vector contains more than\n<code>MAX_STALETIP_HEADERS</code> entries, or MAY partially process headers until local\nlimits are reached.</li>\n<li>Nodes SHOULD ignore messages whose stale tip is not recent according to local\npolicy, for example more than <code>STALETIP_RECENT_WINDOW</code> blocks behind the\nreceiver&#39;s active tip.</li>\n<li>Nodes MAY ignore messages that violate local denial-of-service checks, or MAY\npartially process headers until local limits are reached. Nodes SHOULD NOT\ndisconnect or otherwise punish a peer solely because a single message exceeds\nlocal policy limits. Nodes MAY rate-limit or disconnect peers that repeatedly\nsend messages exceeding those limits.</li>\n<li>After receiving a <code>staletip</code> message that passes local denial-of-service\nchecks, nodes SHOULD reconstruct the block headers from the <code>CompressedHeader</code>\nencoding and validate the headers.</li>\n<li>Nodes SHOULD ignore any headers found to be invalid, and SHOULD NOT\ndisconnect or otherwise punish a peer solely for a single invalid\nannouncement. Nodes MAY rate-limit or disconnect peers that repeatedly\nannounce invalid headers[^rat-ignoreinvalid].</li>\n<li>If <code>have_block</code> is <code>true</code>, nodes that prefer to collect the full block data\nSHOULD request missing block data for the announced stale branch in the normal\nway, for example by walking back from the reconstructed stale tip toward a\nknown block and sending <code>getdata</code> messages for missing blocks.</li>\n<li>Nodes that receive a new stale tip SHOULD announce that tip to their peers\nthat negotiated the <code>staletip</code> feature, subject to local relay policy.\nNodes SHOULD NOT announce the same stale tip to the same peer more than\nonce, and SHOULD NOT announce a stale tip to a peer from which they have\nalready received an announcement of that tip.</li>\n</ul>\n<p>Nodes in initial block download SHOULD NOT announce stale tips, and MAY ignore\nreceived <code>staletip</code> messages, until they are close enough to the active network\ntip for stale-tip recency checks to be meaningful.</p>\n<p>If a received <code>staletip</code> branch has more cumulative proof of work than the\nreceiver&#39;s current active chain, the receiver SHOULD process the reconstructed\nheaders through its normal header-processing logic. Such a branch may cease to\nbe stale from the receiver&#39;s perspective.</p>\n<h4 id=\"active-tip-announcements\">Active Tip Announcements</h4>\n<p>In order for peers to send useful <code>staletip</code> messages, they must be able to\ntrack which block is your active tip in order to deduce whether you already know\nthe fork point corresponding to new stale tips.</p>\n<p>Currently, many node implementations make a small optimisation in their block\nannouncement handling that conflicts with this requirement: they will not\nannounce a block to a peer if they have already processed an announcement for\nthe same block from that peer at the time they validate the block. That said,\ndue to delays in obtaining block data and validating the block, it is usually\nthe case that two well-connected peers will announce the same new block to each\nother.</p>\n<p>Nodes implementing this BIP SHOULD always announce their new active tip to all\npeers. To minimise additional bandwidth, they MAY do so via an <code>inv</code> message\nrather than a <code>headers</code> or compact block message.</p>\n<h4 id=\"reconstructing-headers\">Reconstructing Headers</h4>\n<p>Headers may be reconstructed from a <code>staletip</code> message via the following\nalgorithm:</p>\n<pre><code>  prev_hash = staletip.fork_point\n\n  for compressed_header in staletip.headers:\n      header = BlockHeader(\n          version = compressed_header.version,\n          previous_block_hash = prev_hash,\n          merkle_root = compressed_header.merkle_root,\n          time = compressed_header.time,\n          bits = compressed_header.bits,\n          nonce = compressed_header.nonce\n      )\n      prev_hash = hash(header)\n</code></pre>\n<p>Headers are reconstructed in chain order, beginning with the header immediately\nfollowing <code>fork_point</code> and ending with the stale tip.</p>\n<h3 id=\"optionality\">Optionality</h3>\n<p>Node software implementing this BIP SHOULD provide a configuration option to\ndisable it entirely. Implementations MAY provide separate options for relaying\nstale headers and for requesting or serving stale block data.</p>\n<h2 id=\"rationale\">Rationale</h2>\n<p>This proposal treats stale-tip relay as a negotiated peer service because stale\ntips are useful but not required for normal participation in the Bitcoin network.\nA node can fully validate the active chain, relay blocks, and serve peers without\never learning about stale branches that did not become part of its active chain.\nNegotiating support avoids imposing bandwidth, storage, privacy, or\nimplementation complexity on nodes that do not use this information.</p>\n<p>The feature uses BIP 434 negotiation instead of a protocol-version bump or\nopportunistic transmission of a new message. A protocol-version bump would\nunnecessarily coordinate this feature with unrelated P2P changes and would imply\nbroader capability than is actually needed. Sending a new post-handshake message\nwithout feature-specific negotiation would rely on every peer safely ignoring\nunknown messages. BIP 434 provides a narrower capability signal: peers only send\n<code>staletip</code> messages after the receiver has advertised support for this specific\nfeature.</p>\n<p>A separate <code>staletip</code> message is used instead of reusing <code>headers</code>, <code>inv</code>, or\n<code>block</code> relay. Existing relay messages are primarily active-chain mechanisms and\ndo not communicate the extra information needed here: that the branch is believed\nto be stale, which known block should be used as the reconstruction base, and\nwhether the sender expects stale branch block data to be available. Reusing\nexisting messages would either overload their meaning or require receivers to\ninfer stale-tip intent from context. A dedicated message keeps the behavior\nexplicit and allows implementations to apply separate resource, privacy, and\nrelay policies.</p>\n<p>The protocol is announcement-based rather than request-based. An alternative\nwould be to define messages for requesting known stale tips from a peer, but that\nwould add more state, more peer-specific behavior, and a possible query\namplification surface for a feature whose events are expected to be rare. Simple\nannouncements are sufficient for the main use cases: faster preparation for\nnear-tip reorgs and passive measurement of stale-block propagation.</p>\n<p>The message includes a <code>fork_point</code> followed by compressed headers because the\nprevious-block hash in each header is redundant once the fork point is known.\nThis saves 32 bytes per header while preserving the normal block-header fields\nneeded for validation. More aggressive compression of <code>nVersion</code>, <code>nTime</code>,\n<code>nBits</code>, or <code>nonce</code> was not chosen because those fields are consensus-relevant\nand chain-dependent. Compressing them would add implementation complexity and\nincrease the risk of divergent reconstruction behavior for relatively small\nadditional savings.</p>\n<p>The <code>fork_point</code> is required to be known by the receiver so that the message can\nbe processed without an extra round trip. Allowing the fork point to be either an\nactive-chain block or an already-known stale-branch header lets senders avoid\nresending headers the peer is expected to know. Requiring the fork point to be\nthe exact divergence point from the active chain would be simpler conceptually,\nbut less efficient when both peers already know part of the stale branch.</p>\n<p>The branch length limit of 20 headers is a deliberate resource bound. Stale\nbranches relevant to near-tip reorg handling are expected to be short, and longer\nbranches can be handled by normal header synchronization if they become important.\nThe limit keeps each message small, bounds validation work, and prevents\n<code>staletip</code> from becoming a cheap bulk-header relay mechanism.</p>\n<p>The recency and advertised-tip limits are recommendations rather than strict wire\nrequirements because different nodes have different goals. A monitoring node may\nadvertise more stale tips for measurement, while a resource-constrained node may\nadvertise fewer or ignore them entirely. Selecting the greatest-chainwork tips\nprioritizes branches most likely to become relevant to a reorganization, while\nrandomizing the selection independently for each peer can improve network-wide\navailability when more tips are eligible than can be advertised. Treating these\nlimits as local policy allows implementations to differ without making honest\npeers appear malformed or misbehaving.</p>\n<p>Invalid or locally unacceptable stale-tip announcements are generally ignored\nrather than punished. Stale-tip relay is inherently race-prone: peers may have\ndifferent active tips, different retained stale headers, or different local\nresource policies. Disconnecting peers for these cases would risk penalizing\nhonest nodes during exactly the network conditions where stale tips are most\nlikely to appear.</p>\n<p>The <code>have_block</code> flag is a compact signal that block data for the announced stale\nbranch may be available from the sender. The tip block is the minimum object\ndescribed by <code>have_block</code>, because it is the final header in the announcement and\nthe block most immediately useful for reorg preparation and block-policy\nanalysis. Receivers may also request earlier missing blocks on the announced\nbranch. Describing availability for every block in the branch would make the\nmessage larger and more complex; peers can instead request missing blocks through\nnormal block download and retry with other announcers if requests fail.</p>\n<p>No one-byte BIP 324 message type is assigned for <code>staletip</code>. BIP 324 short\nmessage identifiers save the 12-byte ASCII command overhead for messages that are\nsent frequently. Stale-tip announcements are expected to be rare, and each\nannouncement already carries at least a 32-byte fork point and one 48-byte\ncompressed header, so the bandwidth saved by replacing the <code>staletip</code> command\nname with a one-byte identifier would be negligible. Since the one-byte identifier\nspace is limited, it is better reserved for messages that are common enough for\nthe per-message saving to matter.</p>\n<p>The test-network guidance is stricter because test networks have weaker\nanti-spam properties than mainnet. Signet headers cannot be fully validated\nwithout the block data containing the signature, and testnet difficulty rules can\nmake low-work stale headers cheap to produce. The specification therefore leaves\nroom for implementations to disable the feature on test networks or apply\nstronger relay policy there than they would on mainnet.</p>\n<h2 id=\"test-networks\">Test Networks</h2>\n<p>Making this feature available on test networks raises additional concerns, as\nthey are less protected by proof of work, and thus may have a larger attack\nsurface for denial of service issues. As a result, node software implementing\nthis BIP MAY choose to do so only for mainnet.</p>\n<h3 id=\"signet\">Signet</h3>\n<p>The default signet is expected to reorg roughly once per day, with a branch of\nrandom length. Stale tips are therefore common on signet rather than rare, and a\nstale branch may be longer than <code>MAX_STALETIP_HEADERS</code>. Such a branch will not be\nrelayed via <code>staletip</code>; nodes fall back to normal header synchronisation for it,\nand implementations MAY raise the limit on signet.</p>\n<p>The signet network is designed around the assumption that all valid blocks are\nsigned, and thus much lower proof of work is needed. However, because the block\nsignatures are included in the coinbase transaction, they cannot be verified if\nonly the block&#39;s headers are available. As such, when implementing this BIP for\nsignet:</p>\n<ul>\n<li>Nodes MUST NOT send <code>staletip</code> messages when they do not have the\ncorresponding block data needed to verify the signet signature.</li>\n<li>Nodes MAY disconnect peers that send a <code>staletip</code> message with <code>have_block</code>\nset to <code>false</code>.</li>\n<li>Nodes SHOULD set <code>prefers_blocks</code> to <code>\\x01</code> when negotiating the feature, as\nthey will always need to download the full block data for a stale tip to\nvalidate the signature.</li>\n</ul>\n<p>In addition, when the difficulty is low, it is possible that varying the <code>nBits</code>\nfield of a valid block will generate another valid block (see <a href=\"https://github.com/bitcoin/bitcoin/issues/33266\">#33266</a>).\nAs such:</p>\n<ul>\n<li>Nodes SHOULD consider variant headers, where the previous block and Merkle\nroot are both the same, to be duplicates, and only advertise the first seen\nvariant as a stale tip.</li>\n</ul>\n<p>While nodes MAY also deduplicate such variant headers when receiving them via\n<code>staletip</code> messages, if they do not perform the same deduplication logic when\nreceiving such headers via standard <code>headers</code> or <code>block</code> relay, that provides\nlittle protection. Deduplicating when sending <code>staletip</code> messages is primarily\naimed at avoiding nodes implementing this BIP from being used to amplify\nattacks.</p>\n<p>Only variant tips need to be deduplicated: it is possible, though unlikely, that\nvalid signed blocks may be descendants of variant blocks. These blocks will not\nthemselves be variants, as they will have different previous blocks.</p>\n<h3 id=\"testnet3-and-testnet4\">Testnet3 and Testnet4</h3>\n<p>While testnet3 and testnet4 blocks are proof-of-work based like mainnet, they\nboth allow for low difficulty blocks:</p>\n<ul>\n<li>testnet3 allows minimum difficulty blocks when a block&#39;s timestamp is 20\nminutes after the previous block&#39;s timestamp, and resets the difficulty on an\nongoing basis if this occurs for the last block in a retarget period.</li>\n<li>testnet4 updates these rules, as specified in <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0094.mediawiki\">BIP 94</a>, basing the\ndifficulty calculation for the first block in a new retarget period on the\ndifficulty of the first block in the previous retarget period, rather than\nthe previous block, avoiding a reset.</li>\n</ul>\n<p>As a result, nodes could see many valid stale tips with minimum difficulty,\neither due to such tips being created with long timestamps, or, on testnet3,\nbecause the difficulty has been reset. To avoid this scenario, when implementing\nthis BIP on testnet3 or testnet4:</p>\n<ul>\n<li>Nodes SHOULD only advertise stale tips when the stale tip itself has a\ndifficulty greater than the minimum difficulty.</li>\n<li>Nodes MAY advertise stale tips only when the stale tip itself has a\ndifficulty greater than some higher threshold, for example 1,000,000.</li>\n</ul>\n<h2 id=\"backward-compatibility\">Backward Compatibility</h2>\n<p>This BIP introduces a new P2P message (<code>staletip</code>) and relies on <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0434.md\">BIP 434</a>\nfor feature negotiation to avoid impacting nodes that do not support this BIP.\nNodes MUST NOT send <code>staletip</code> messages to peers that have not negotiated the\n<code>staletip</code> feature.</p>\n<p>Nodes that do not support this BIP may still see more frequent notifications of\nwhen their peers update to a new active tip, if implementations choose to make\nactive-tip <code>inv</code> announcements to all peers rather than only to peers that\nnegotiated the <code>staletip</code> feature.</p>\n<h2 id=\"privacy-impact\">Privacy Impact</h2>\n<p>Sharing stale tips raises several potential privacy concerns.</p>\n<p>The first concern is fingerprinting. A node&#39;s behaviour when negotiating and\nsharing stale tips may make it easier for peers to distinguish that node from\nothers on the network, perhaps allowing attackers to relate a node&#39;s onion\naddress with its IPv4 address. The BIP 434 feature advertisement itself, the\n<code>prefers_blocks</code> value, active-tip announcement behaviour, and stale-tip relay\ntiming are all potentially fingerprintable. Since stale tips are expected to\npropagate fairly efficiently and consistently, and to remain rare, this is not\nexpected to be a significant problem in practice. For users that remain\nconcerned, disabling stale tip relay entirely is probably the best approach.</p>\n<p>The second concern is for miners with private transaction pools. In that case,\nminers have a selection of transactions that they wish to mine themselves\nwithout giving other miners the opportunity to compete with them, only\npublishing them when they construct a block with valid proof-of-work that\nincludes them. Relaying those transactions via <code>staletip</code> announcements will\nlargely be harmful to a private transaction pool operator, as it will not help\nthem win the stale block race. It will only allow nodes to reorg slightly faster\nif they do win, but if they lose the stale block race it will make their private\ntransactions available to competing miners. This is a particular concern if the\nreason for the block being stale is due to inefficiencies in updating ASIC\nminers to a new work target, in which case there is almost no chance of winning\nthe stale block race. As such, miners operating private transaction pools should\nprobably disable stale tip relay entirely on the nodes they use for processing\ntheir mined blocks.</p>\n<h2 id=\"reference-implementation\">Reference Implementation</h2>\n<p>A prototype implementation is available at:</p>\n<ul>\n<li>Bitcoin Core branch: <a href=\"https://github.com/w0xlt/bitcoin/tree/staletip-v4\">https://github.com/w0xlt/bitcoin/tree/staletip-v4</a></li>\n</ul>\n<h2 id=\"test-vectors\">Test Vectors</h2>\n<h3 id=\"signet-stale-branch-19-blocks\">Signet Stale Branch (19 blocks)</h3>\n<p>This serialization test vector is from signet, where a 19-block stale branch\noccurred at height 287767-287785. Because this vector has <code>have_block</code> set to\n<code>false</code>, it is not an example of a Signet <code>staletip</code> message that conforming\nnodes should relay under the Signet-specific rules above.</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>Network</td>\n<td>Signet</td>\n</tr>\n<tr>\n<td>Fork point height</td>\n<td>287766</td>\n</tr>\n<tr>\n<td>Fork point hash</td>\n<td><code>00000012602fde2eaf33a90523f42fb07ca854c1d26108782dc4592a80507e1c</code></td>\n</tr>\n<tr>\n<td>Stale tip height</td>\n<td>287785</td>\n</tr>\n<tr>\n<td>Stale tip hash</td>\n<td><code>0000000024ff924ff932668d497bba7da9157559a68d9c87d2f28d22e5e4a001</code></td>\n</tr>\n<tr>\n<td>Branch length</td>\n<td>19</td>\n</tr>\n<tr>\n<td><code>have_block</code></td>\n<td><code>false</code></td>\n</tr>\n</tbody></table>\n<p>Serialized <code>staletip</code> payload (946 bytes):</p>\n<pre><code>1c7e50802a59c42d780861d2c154a87cb02ff42305a933af2ede2f60120000001300000020\n67aa2937b235e209f1ad40f37b17dcad8ca02d1fb9cf4c4928902eab3ff853a6cda16e6916\n58151d81ad7b0400000020721ab6f36042842bab4583f98e15181b646c47c7b9dfb07a204c\n4e5152e54e97fda46e691658151dd2dcec1800000020ddc6b628101b404f78d446591a623e\n12bb7e7cd2ae1593d0cc6ef082e19fa81014a66e691658151d46070e10000000208be5b960\n7b20ac0163b885881aa6018ef60a5637b93a9cd85981515a169a31c36aab6e691658151de4\ne10600000000206c974ef806b2e6a21cc9a53fedcbd5d5e203b4929a1bd7c5859ec77d0a75\n5c1d64ae6e691658151d2a2e130e0000002039505fa2ee91f5e5b2540b4a9a7f02ed58d79f\nc387412cf2520a39ee16bf435f6eb16e691658151db29c830a00000020411bce69cda2b239\nc74faea1f0cfaceabd90377114dc0ace14e2a58858cd4f9a91b46e691658151d4c2d3f0500\n00002096464a2ec42fea8419a5c6adcc41ca7dc3f53461cd0afc880f76498aa8eedbf993b5\n6e691658151d5e99700b00000020c3cad611ee3c7bffec3f80fc83f68192ca0a86e8c89050\nf8353480310d080caf08b76e691658151dbc61a6150000002079e36b5a06bb5a524a2f5bad\n6725451613c354cbf64f9a0283f77fe90782d59fcbba6e691658151d6c669300000000204c\n69fd467818994e7ff885181e7451d23869a7fd19447dad7a95398077ed7774dcba6e691658\n151dc9c7ff02000000208ea9a871b12cd45226ab6df280ec46b8c9ca196b98dc8adc27718c\nab333e19a50abc6e691658151df6bae902000000209c182979424a5c85d55d5bddbf3b9421\nb01b7810edf95ad2529552dd2eec80848ebd6e691658151d439e5c0000000020cfb9f6d164\n3613dfdd23a6e1eabadac38c392f985f5dc05b00ef682c428922c96cbe6e691658151d278b\n4d1400000020eea983b8d64947368de4f7626531a3134d18f20a8d87462ba011a4f56a53b6\nac4fbf6e691658151ddae8640800000020140bc70b1a3282fbebf9d48ff40a9b73ab7c76ad\n522799cee73315e9c21aab50dcc06e691658151dedaae90f000000209b91a056fe089429f2\nba4298e764acb4177249ce7d68e6883836f70d1d9218b355c16e691658151d28a432060000\n0020b26e80c6a12cc4595556d2318fda564ced5d2a526c7b77f57a9897a56c10539056c16e\n691658151dc64fb50000000020369d36495749c96d36260d839abfa703a611aad7b3016342\nef05d0412f42221e6ac56e691658151d8f8bfb0f00\n</code></pre>\n<p>Payload breakdown:</p>\n<ul>\n<li>Bytes 0-31: <code>fork_point</code> (<code>uint256</code>, little-endian)</li>\n<li>Byte 32: vector length (<code>0x13</code> = 19)</li>\n<li>Bytes 33-944: 19 <code>CompressedHeader</code> entries (48 bytes each)</li>\n<li>Byte 945: <code>have_block</code> (<code>0x00</code> = <code>false</code>)</li>\n</ul>\n<h3 id=\"bch-fork-headers-18-blocks\">BCH Fork Headers (18 blocks)</h3>\n<p>This test vector uses mainnet headers from the BCH fork. Blocks 478559-478576\nhave valid Bitcoin headers meeting proof-of-work requirements, but are invalid\nBitcoin blocks because block 478559 exceeds the 1MB block size limit. Block\n478577 changed difficulty under BCH&#39;s Emergency Difficulty Adjustment rules,\nmaking it an invalid Bitcoin header as well.</p>\n<table>\n<thead>\n<tr>\n<th>Field</th>\n<th>Value</th>\n</tr>\n</thead>\n<tbody><tr>\n<td>Network</td>\n<td>Mainnet</td>\n</tr>\n<tr>\n<td>Fork point height</td>\n<td>478558</td>\n</tr>\n<tr>\n<td>Fork point hash</td>\n<td><code>0000000000000000011865af4122fe3b144e2cbeea86142e8ff2fb4107352d43</code></td>\n</tr>\n<tr>\n<td>Stale tip height</td>\n<td>478576</td>\n</tr>\n<tr>\n<td>Stale tip hash</td>\n<td><code>000000000000000001416af072f8989829f4c60a1a9658e1cec08411798e4ffa</code></td>\n</tr>\n<tr>\n<td>Branch length</td>\n<td>18</td>\n</tr>\n<tr>\n<td><code>have_block</code></td>\n<td><code>false</code></td>\n</tr>\n</tbody></table>\n<p>Serialized <code>staletip</code> payload (898 bytes):</p>\n<pre><code>432d350741fbf28f2e1486eabe2c4e143bfe2241af65180100000000000000001200000020\nabaa4bd8a48c1c6bc08ee39b66065e5e9484304cab8b56d5eed3e40b1ac996c899c4805935\n47011822ca4ae80000002082afc8ef7eb41a4ecac1fea46983742e491f804ad662e3745ab9\nc6c4297d8a0862c980593547011840a772cb0200002058874e50628fdf83aeea4e8cbc7ade\n946e9ba14bcb1d8ffb28c3daf8ade84df65fca805935470118e2f5100300000020111b85f9\nd3b969a1f7ff3d50af08893c500edfc5623b96dbeab6daf16a5164a40ace805935470118c4\nf4240a0000002040a045063b551b61d6a1c9db6d3231e2d7403185bbb2332ae1f66db24aac\n7fa288d8805935470118f15dd76b0000002070cb14529e8757c359c2e8b1e987f6eee6fbc4\n472ee9ad4a2e5df6905c19d6d70bed80593547011885ae00d0000000209653314c1d73e463\n0bb485fb25ce7a2583cec7c3ccfc27a6d24163be1e9fb19530f4805935470118f17ad2c500\n000020cdf48b8e7ac6bf3a51d1878ee3ff7e6fd0022926dd69cc5cc8d9126e77c4dba809f5\n8059354701188114e83600000020890cf1dc60edbf0fd4fb667f28ac785849c031d8b24d5e\n5a0af56ee2bd8a739bf51081593547011812f32a9600000020ff5244613ad20fdc39b7ee6f\n4fbc7016432d2dbf45c2a950c59665b39c3954b5b525815935470118aa790d6600000020ae\ned520e7c1693de5cfe7531e7d3e73dff7858b09cb6e1ec29229a75c3da2b92453e81593547\n011830a4314d000000202e4a4054e64c3f5810c23ec0144d9793aab2d5a7d77d1660eee24d\n3d55e8b715b543815935470118da1c00e00000002073152af68778a98fd984a158aeb29d28\ne094e23c3a7dff02260c345791e52498c3fb8159354701188d5abed90000002022606e744a\n29f9d4a67ff1fcd2f0e31300ddbd145f8f1db8a68270bfbde77dd88fff8159354701186af1\n75f8000000209f5db27969fecc0ef71503279069b2df981ba545592a7b425f353b5060e77f\n3e7e13825935470118da70378e000000202f0d316b08350f5cd998c6a11762d10adb9f951b\n5f79ce2a073f8187c05f561f1b1c8259354701184834c62300000020cf8fc3bad8dad139a3\ndd6a30481d87e1f760122573168002cc9ef7a58fc53ad387848259354701188a3b54f70000\n00200eae92d9b46d81a011a79726a802d4eb195a7af8b70a09b0e115c391968c50d51c8a82\n5935470118cd786d1300\n</code></pre>\n<p>Payload breakdown:</p>\n<ul>\n<li>Bytes 0-31: <code>fork_point</code> (<code>uint256</code>, little-endian)</li>\n<li>Byte 32: vector length (<code>0x12</code> = 18)</li>\n<li>Bytes 33-896: 18 <code>CompressedHeader</code> entries (48 bytes each)</li>\n<li>Byte 897: <code>have_block</code> (<code>0x00</code> = <code>false</code>)</li>\n</ul>\n<h2 id=\"copyright\">Copyright</h2>\n<p>This BIP is licensed under the 3-clause BSD license.</p>\n<p>[^rat-compressedheader]: Omitting the previous block hash from each header saves\n    32 bytes per header (40%), as this field can be reconstructed from the\n    preceding headers in the message. This does not apply to the first header,\n    which is why the fork point must be included explicitly. This BIP does not\n    attempt to omit <code>nBits</code> or compress <code>nTime</code> or <code>nVersion</code>, because\n    reconstructing those fields is significantly more complicated for\n    comparatively much less potential gain.</p>\n<p>[^rat-maxheight]: A window of 1000 blocks (about 7 days) provides a\n    reasonable balance between limiting resource usage, and maximising\n    propagation potential. By limiting the distance to the tip to being no\n    older than the previous retarget period, we ensure that the difficulty\n    of creating a stale block is comparable to creating a new tip, making\n    it uneconomical to use this as a spamming mechanism. By choosing\n    a longer period, we provide the opportunity for nodes interested in\n    stale block data for research purposes plenty of opportunity to obtain\n    the data. In particular, many nodes will tend to discover it through\n    the periodic blocks-only connections (see Bitcoin Core PR <a href=\"https://github.com/bitcoin/bitcoin/pull/19858\">#19858</a>).</p>\n<p>[^rat-maxforklen]: Limiting the branch length to 20 headers avoids nodes\n    being burdened with tracking deliberate chain splits. When\n    incompatible consensus rules are enforced, the majority chain need not\n    continue sharing the minority&#39;s fork beyond a short window. Twenty\n    blocks is sufficient to capture the short-term stale blocks useful\n    for reorg detection, while avoiding long-term tracking of persistent\n    chain splits.</p>\n<p>[^rat-denialofservice]: Limiting stale branches to 20 headers bounds the\n    per-message size to under 1kB. Combining this with a default limit of 10\n    previously known stale tips advertised to a peer ensures that the initial\n    relay bandwidth is under 10kB. Restricting relay to recent tips whose proof\n    of work is comparable to the active chain makes stale-tip spam costly on\n    mainnet.</p>\n<p>[^rat-ignoreinvalid]: Ignoring rather than punishing allows nodes to apply\n    different limits for what stale tips they accept. If one node uses stricter\n    limits than its peers, this avoids the risk that honest announcements from\n    those peers will cause disconnection and potentially network partitions.\n    This is also consistent with how invalid headers are handled.</p>\n","toc":[{"id":"abstract","title":"Abstract","level":2},{"id":"motivation","title":"Motivation","level":2},{"id":"specification","title":"Specification","level":2},{"id":"the-staletip-message","title":"The staletip Message","level":3},{"id":"compressedheader-format","title":"CompressedHeader Format","level":4},{"id":"protocol-constants","title":"Protocol Constants","level":3},{"id":"staletip-feature-definition","title":"Staletip Feature Definition","level":3},{"id":"bip-434-negotiation","title":"BIP 434 Negotiation","level":3},{"id":"sending-staletip-messages","title":"Sending staletip Messages","level":3},{"id":"receiving-staletip-messages","title":"Receiving staletip Messages","level":3},{"id":"active-tip-announcements","title":"Active Tip Announcements","level":4},{"id":"reconstructing-headers","title":"Reconstructing Headers","level":4},{"id":"optionality","title":"Optionality","level":3},{"id":"rationale","title":"Rationale","level":2},{"id":"test-networks","title":"Test Networks","level":2},{"id":"signet","title":"Signet","level":3},{"id":"testnet3-and-testnet4","title":"Testnet3 and Testnet4","level":3},{"id":"backward-compatibility","title":"Backward Compatibility","level":2},{"id":"privacy-impact","title":"Privacy Impact","level":2},{"id":"reference-implementation","title":"Reference Implementation","level":2},{"id":"test-vectors","title":"Test Vectors","level":2},{"id":"signet-stale-branch-19-blocks","title":"Signet Stale Branch (19 blocks)","level":3},{"id":"bch-fork-headers-18-blocks","title":"BCH Fork Headers (18 blocks)","level":3},{"id":"copyright","title":"Copyright","level":2}],"history":{"totalCommits":1,"firstCommit":"2026-08-21T15:49:20-07:00","lastUpdated":"2026-08-21T15:49:20-07:00","events":[{"sha":"8aacefed1882e37e2e4daabedf721f1298a62530","date":"2026-08-21T15:49:20-07:00","author":"pseudoramdom","message":"Add BIP-332: Stale Tip Relay","url":"https://github.com/bitcoin/bips/commit/8aacefed1882e37e2e4daabedf721f1298a62530","kind":"edit"}]}}