1
0
mirror of https://github.com/bitcoin/bips.git synced 2026-09-14 19:01:38 +00:00

Add BIP-332: Stale Tip Relay

This commit is contained in:
pseudoramdom
2026-08-21 15:49:20 -07:00
parent 09e21036a4
commit 8aacefed18
2 changed files with 715 additions and 0 deletions

View File

@@ -1150,6 +1150,13 @@ users (see also: [https://en.bitcoin.it/wiki/Economic_majority economic majority
| Specification | Specification
| Draft | Draft
|- |-
| [[bip-0332.md|332]]
| Peer Services
| Stale Tip Relay
| Anthony Towns, w0xlt, Ram
| Specification
| Draft
|-
| [[bip-0337.mediawiki|337]] | [[bip-0337.mediawiki|337]]
| API/RPC | API/RPC
| Compressed Transactions | Compressed Transactions

708
bip-0332.md Normal file
View File

@@ -0,0 +1,708 @@
```
BIP: 332
Layer: Peer Services
Title: Stale Tip Relay
Authors: Anthony Towns <aj@erisian.com.au>
w0xlt <woxlt.dev@gmail.com>
Ram <pseudoramdom@proton.me>
Status: Draft
Type: Specification
Assigned: 2026-08-21
License: BSD-3-Clause
Discussion: 2026-07-29 https://gnusha.org/pi/bitcoindev/d92f1615-368b-4406-b326-a1799c72a555n@googlegroups.com/
Version: 0.1.0
Requires: 434
```
## 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.
## Motivation
Stale blocks are a potentially useful signal about the health of the network's
block-relay layer. The rate at which they occur tracks how quickly new blocks
reach the rest of the miners, and an elevated rate can be a symptom of a range
of network-related problems degraded or congested propagation, network
partitions, or adversarial mining behavior such as selfish mining.
An added benefit is that when a stale block has the same cumulative proof of
work as the active tip, the current active tip may be re-orged out in favor of a
child of the stale block; having the block already downloaded (and possibly already
validated) makes the reorg faster. The contents of stale blocks additionally
reveal differences in block-creation policy between mining pools.
These benefits are not essential to the operation of the Bitcoin network, so
this is proposed as an optional feature, with low performance demands and strict
resource limits.
## Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT",
"RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as
described in RFC 2119.
For the purposes of this document, "active chain" means the chain that a node
has selected as its current best chain. A "stale branch" is a sequence of valid
headers that descends from a known block but whose tip is not on the node's
active chain. The "stale tip" is the last header in that branch.
### The `staletip` Message
The `staletip` P2P message has the ASCII message type `staletip` and the
following payload:
| Type | Name | Description |
| ---- | ---- | ----------- |
| `uint256` | `fork_point` | The block hash used as the previous block hash of the first compressed header |
| vector of `CompressedHeader` | `headers` | The headers forming a single stale branch after `fork_point`, in chain order |
| `bool` | `have_block` | Whether the sender is willing to serve block data for the stale branch |
The `fork_point` MUST be known by the receiver and MUST be the predecessor of
the first reconstructed header. In the common case, it is the last common block
before the stale branch diverges from the receiver's active chain. It MAY be a
later known stale-branch header if both peers are expected to already know that
header; in that case the `headers` vector only contains the unknown suffix.
The `headers` field MUST describe a single linear branch. The first header
immediately follows `fork_point`, and each subsequent header immediately
follows the preceding header. The final header is the stale tip. A separate
`staletip` message is required for each competing branch that the sender
chooses to announce.
The `headers` vector is serialized as normal: encode the length of the vector as
a 1, 3, 5 or 9 byte `CompactSize`, then serialize each member of the vector.
Only minimally-encoded `CompactSize` values are supported.
The `bool` serialization is a single byte `\x00` for false, and a single byte
`\x01` for true. Other encodings are malformed.
`have_block` set to `true` means the sender currently has, and is willing to
serve through normal block download mechanisms, the full block data for the
stale tip, which is the final header in this `staletip` message. A sender that
sets `have_block` to `true` SHOULD additionally be willing to serve earlier
blocks on the announced stale branch that it has. `have_block` set to `false`
makes no claim about stale-branch block-data availability. A sender SHOULD NOT
set `have_block` to `true` unless it expects a request for the stale tip block to
succeed.
Because stale tips are very rare, this BIP does not reserve a 1-byte [BIP
324][BIP324] message type ID for the `staletip` message.
#### `CompressedHeader` Format
The `CompressedHeader` is a 48 byte structure equivalent to regular block header
serialization, but omits the previous block's hash[^rat-compressedheader]. The
fields are serialized exactly as the corresponding fields are serialized in a
Bitcoin block header:
| Size | Name | Type | Description |
| ---- | ---- | ---- | ----------- |
| 4 | `version` | `int32_t` | Block version information |
| 32 | `merkle_root` | `uint256` | The Merkle root of the block's transactions |
| 4 | `time` | `uint32_t` | The block's timestamp |
| 4 | `bits` | `uint32_t` | The calculated difficulty target being used for this block |
| 4 | `nonce` | `uint32_t` | The nonce used to generate this block |
### Protocol Constants
Implementations of this BIP use the following limits unless otherwise noted:
| Name | Value | Meaning |
| ---- | ----- | ------- |
| `MAX_STALETIP_HEADERS` | 20 | Recommended maximum number of `CompressedHeader` entries in one `staletip` message |
| `STALETIP_RECENT_WINDOW` | 1000 blocks | Recommended maximum distance from the receiver's active tip |
| `MAX_ADVERTISED_STALETIPS` | 10 | Recommended maximum number of previously known stale tips advertised to a peer |
`MAX_STALETIP_HEADERS` is a resource-management limit[^rat-maxforklen]. The
value of 20 is intended to cover the short stale branches useful for reorg
detection while avoiding long-term tracking of persistent chain splits.
`STALETIP_RECENT_WINDOW` is a resource-management limit[^rat-maxheight]. The
1000-block window is about seven days and balances limiting resource usage with
allowing stale tips to propagate.
`MAX_ADVERTISED_STALETIPS` is a bandwidth limit[^rat-denialofservice]. Advertising
up to 10 previously known stale tips, combined with the 20-header branch limit,
keeps informing a new peer of known stale tips under 10kB. It does not limit
how many stale tips a node retains, or how many newly learned tips it relays
over the lifetime of a connection. Implementations MAY use stricter limits. A
peer SHOULD NOT be punished solely because a single announcement exceeds local
policy limits.
### Staletip Feature Definition
This BIP defines a new [BIP 434][BIP434] feature id ("the `staletip` feature"):
* `BIP332`
The `staletip` feature data MUST contain exactly one byte. This byte is a
boolean, `prefers_blocks`, indicating whether the node advertising the feature
prefers to collect the full block data associated with stale tips:
* `\x00`: the node prefers header-only announcements and does not request that
announcements be delayed until block data is available.
* `\x01`: the node prefers announcements that include availability of block
data where practical.
Nodes receiving `staletip` feature data whose length is not exactly one byte,
or whose byte is not `\x00` or `\x01`, MUST ignore that peer's `staletip`
feature advertisement. They SHOULD NOT disconnect solely because the feature
data is not understood.
### BIP 434 Negotiation
Nodes implementing this specification MUST use BIP 434 to negotiate support
before sending any `staletip` messages.
Nodes MUST NOT send a `staletip` message to a peer unless that peer advertised a
valid `staletip` feature during BIP 434 feature negotiation. Nodes SHOULD ignore
`staletip` messages received from peers that did not advertise a valid
`staletip` feature. Nodes MAY disconnect peers that repeatedly send such
messages without negotiation.
Advertising the feature only signals willingness to receive `staletip` messages.
It does not oblige a node to send them or to serve block data. A node MAY
advertise the feature to receive stale tips while never sending any itself, for
example a node that only collects stale tips for research.
### Sending `staletip` Messages
Nodes implementing this BIP MAY send `staletip` messages to advertise recent
stale tips they are aware of. If so:
- Nodes SHOULD send `staletip` messages advertising recent stale tips
that they are aware of to peers that support the `staletip` feature.
- Nodes SHOULD NOT advertise stale tips that violate header consensus rules
(invalid version, invalid timestamps, invalid difficulty changes,
insufficient proof of work).
- Nodes MUST NOT send `staletip` messages if they are not sure that the
`fork_point` is a block that their peer is aware of. This can be
estimated by tracking the highest work blocks announced by the peer,
and assuming the peer is treating that block as its active tip. Provided
the `fork_point` is an ancestor of that highest work block, the peer
will be aware of the `fork_point`.
- The `fork_point` SHOULD be chosen to minimise the number of entries
in the `headers` vector, while still being a block that the peer already
has header information for.
- Nodes MAY choose a `fork_point` based on their own active chain,
even if that results in the `headers` vector repeating some headers
that the peer already knows.
- Nodes SHOULD NOT advertise stale tips whose tip height is more than
`STALETIP_RECENT_WINDOW` blocks behind the sender's active tip.
- Nodes SHOULD NOT advertise stale branches longer than `MAX_STALETIP_HEADERS`
headers.
- Nodes SHOULD apply proof-of-work or chainwork thresholds sufficient to avoid
using this message as a low-cost spam channel[^rat-denialofservice].
- When informing a peer of stale tips that were already known when stale-tip
relay with that peer began, nodes SHOULD advertise no more than
`MAX_ADVERTISED_STALETIPS` eligible tips. If more tips are eligible, nodes
SHOULD select those with the greatest chainwork, preferring the most recently
learned tip when chainwork is equal. Implementations MAY instead select a
different subset, for example by sampling independently for each peer to
increase the diversity of stale tips available across the network. This limit
does not prevent relaying newly learned stale tips to an existing peer.
- Nodes SHOULD avoid advertising the same tip to the same peer repeatedly via
multiple `staletip` messages.
- Nodes SHOULD respect the peer's `prefers_blocks` setting. If a peer sets
`prefers_blocks` to `\x00`, stale tips SHOULD be relayed to that peer
promptly, without waiting to obtain block data. Conversely, for a peer that
sets `prefers_blocks` to `\x01`, a node that does attempt to obtain block
data SHOULD defer sending the `staletip` message to that peer until it has
obtained the relevant block data, unless doing so would substantially delay
propagation.
- Nodes that have the block data corresponding to the tip block (and will
provide that data to peers that request it) SHOULD set `have_block`
as `true`. A sender that sets `have_block` to `true` SHOULD additionally be
willing to serve earlier blocks on the announced stale branch that it has.
Nodes without the full tip block data, or that are unwilling to relay the data
to peers, SHOULD set `have_block` as `false`.
### Receiving `staletip` Messages
Nodes implementing this BIP MAY process `staletip` messages from peers to gain
more knowledge about stale tips. If so:
- Nodes SHOULD reject messages whose payload cannot be parsed exactly as a
`staletip` payload, including non-minimally encoded `CompactSize` values,
truncated data, invalid boolean values, or trailing bytes. Nodes MAY
disconnect peers for malformed payloads.
- Nodes SHOULD reject (ignore) `staletip` messages where the `fork_point` is
not known, and MAY disconnect peers that repeatedly send unknown fork points.
Sending such a message violates the requirement above that senders MUST NOT
send `staletip` messages unless they are sure the receiver knows the
`fork_point` block.
- Nodes SHOULD reject messages where the `headers` vector is empty, and MAY
disconnect the sending peer if this occurs.
- When processing a `staletip` message, nodes MUST bound the resources they
devote to it so that a denial-of-service vector is not
created[^rat-denialofservice]. Enforcing the `MAX_STALETIP_HEADERS` and
`STALETIP_RECENT_WINDOW` limits, together with implementation-specific storage
limits, is one way to satisfy this requirement; nodes MAY apply stricter
limits.
- Nodes MAY ignore messages where the `headers` vector contains more than
`MAX_STALETIP_HEADERS` entries, or MAY partially process headers until local
limits are reached.
- Nodes SHOULD ignore messages whose stale tip is not recent according to local
policy, for example more than `STALETIP_RECENT_WINDOW` blocks behind the
receiver's active tip.
- Nodes MAY ignore messages that violate local denial-of-service checks, or MAY
partially process headers until local limits are reached. Nodes SHOULD NOT
disconnect or otherwise punish a peer solely because a single message exceeds
local policy limits. Nodes MAY rate-limit or disconnect peers that repeatedly
send messages exceeding those limits.
- After receiving a `staletip` message that passes local denial-of-service
checks, nodes SHOULD reconstruct the block headers from the `CompressedHeader`
encoding and validate the headers.
- Nodes SHOULD ignore any headers found to be invalid, and SHOULD NOT
disconnect or otherwise punish a peer solely for a single invalid
announcement. Nodes MAY rate-limit or disconnect peers that repeatedly
announce invalid headers[^rat-ignoreinvalid].
- If `have_block` is `true`, nodes that prefer to collect the full block data
SHOULD request missing block data for the announced stale branch in the normal
way, for example by walking back from the reconstructed stale tip toward a
known block and sending `getdata` messages for missing blocks.
- Nodes that receive a new stale tip SHOULD announce that tip to their peers
that negotiated the `staletip` feature, subject to local relay policy.
Nodes SHOULD NOT announce the same stale tip to the same peer more than
once, and SHOULD NOT announce a stale tip to a peer from which they have
already received an announcement of that tip.
Nodes in initial block download SHOULD NOT announce stale tips, and MAY ignore
received `staletip` messages, until they are close enough to the active network
tip for stale-tip recency checks to be meaningful.
If a received `staletip` branch has more cumulative proof of work than the
receiver's current active chain, the receiver SHOULD process the reconstructed
headers through its normal header-processing logic. Such a branch may cease to
be stale from the receiver's perspective.
#### Active Tip Announcements
In order for peers to send useful `staletip` messages, they must be able to
track which block is your active tip in order to deduce whether you already know
the fork point corresponding to new stale tips.
Currently, many node implementations make a small optimisation in their block
announcement handling that conflicts with this requirement: they will not
announce a block to a peer if they have already processed an announcement for
the same block from that peer at the time they validate the block. That said,
due to delays in obtaining block data and validating the block, it is usually
the case that two well-connected peers will announce the same new block to each
other.
Nodes implementing this BIP SHOULD always announce their new active tip to all
peers. To minimise additional bandwidth, they MAY do so via an `inv` message
rather than a `headers` or compact block message.
#### Reconstructing Headers
Headers may be reconstructed from a `staletip` message via the following
algorithm:
```
prev_hash = staletip.fork_point
for compressed_header in staletip.headers:
header = BlockHeader(
version = compressed_header.version,
previous_block_hash = prev_hash,
merkle_root = compressed_header.merkle_root,
time = compressed_header.time,
bits = compressed_header.bits,
nonce = compressed_header.nonce
)
prev_hash = hash(header)
```
Headers are reconstructed in chain order, beginning with the header immediately
following `fork_point` and ending with the stale tip.
### Optionality
Node software implementing this BIP SHOULD provide a configuration option to
disable it entirely. Implementations MAY provide separate options for relaying
stale headers and for requesting or serving stale block data.
## Rationale
This proposal treats stale-tip relay as a negotiated peer service because stale
tips are useful but not required for normal participation in the Bitcoin network.
A node can fully validate the active chain, relay blocks, and serve peers without
ever learning about stale branches that did not become part of its active chain.
Negotiating support avoids imposing bandwidth, storage, privacy, or
implementation complexity on nodes that do not use this information.
The feature uses BIP 434 negotiation instead of a protocol-version bump or
opportunistic transmission of a new message. A protocol-version bump would
unnecessarily coordinate this feature with unrelated P2P changes and would imply
broader capability than is actually needed. Sending a new post-handshake message
without feature-specific negotiation would rely on every peer safely ignoring
unknown messages. BIP 434 provides a narrower capability signal: peers only send
`staletip` messages after the receiver has advertised support for this specific
feature.
A separate `staletip` message is used instead of reusing `headers`, `inv`, or
`block` relay. Existing relay messages are primarily active-chain mechanisms and
do not communicate the extra information needed here: that the branch is believed
to be stale, which known block should be used as the reconstruction base, and
whether the sender expects stale branch block data to be available. Reusing
existing messages would either overload their meaning or require receivers to
infer stale-tip intent from context. A dedicated message keeps the behavior
explicit and allows implementations to apply separate resource, privacy, and
relay policies.
The protocol is announcement-based rather than request-based. An alternative
would be to define messages for requesting known stale tips from a peer, but that
would add more state, more peer-specific behavior, and a possible query
amplification surface for a feature whose events are expected to be rare. Simple
announcements are sufficient for the main use cases: faster preparation for
near-tip reorgs and passive measurement of stale-block propagation.
The message includes a `fork_point` followed by compressed headers because the
previous-block hash in each header is redundant once the fork point is known.
This saves 32 bytes per header while preserving the normal block-header fields
needed for validation. More aggressive compression of `nVersion`, `nTime`,
`nBits`, or `nonce` was not chosen because those fields are consensus-relevant
and chain-dependent. Compressing them would add implementation complexity and
increase the risk of divergent reconstruction behavior for relatively small
additional savings.
The `fork_point` is required to be known by the receiver so that the message can
be processed without an extra round trip. Allowing the fork point to be either an
active-chain block or an already-known stale-branch header lets senders avoid
resending headers the peer is expected to know. Requiring the fork point to be
the exact divergence point from the active chain would be simpler conceptually,
but less efficient when both peers already know part of the stale branch.
The branch length limit of 20 headers is a deliberate resource bound. Stale
branches relevant to near-tip reorg handling are expected to be short, and longer
branches can be handled by normal header synchronization if they become important.
The limit keeps each message small, bounds validation work, and prevents
`staletip` from becoming a cheap bulk-header relay mechanism.
The recency and advertised-tip limits are recommendations rather than strict wire
requirements because different nodes have different goals. A monitoring node may
advertise more stale tips for measurement, while a resource-constrained node may
advertise fewer or ignore them entirely. Selecting the greatest-chainwork tips
prioritizes branches most likely to become relevant to a reorganization, while
randomizing the selection independently for each peer can improve network-wide
availability when more tips are eligible than can be advertised. Treating these
limits as local policy allows implementations to differ without making honest
peers appear malformed or misbehaving.
Invalid or locally unacceptable stale-tip announcements are generally ignored
rather than punished. Stale-tip relay is inherently race-prone: peers may have
different active tips, different retained stale headers, or different local
resource policies. Disconnecting peers for these cases would risk penalizing
honest nodes during exactly the network conditions where stale tips are most
likely to appear.
The `have_block` flag is a compact signal that block data for the announced stale
branch may be available from the sender. The tip block is the minimum object
described by `have_block`, because it is the final header in the announcement and
the block most immediately useful for reorg preparation and block-policy
analysis. Receivers may also request earlier missing blocks on the announced
branch. Describing availability for every block in the branch would make the
message larger and more complex; peers can instead request missing blocks through
normal block download and retry with other announcers if requests fail.
No one-byte BIP 324 message type is assigned for `staletip`. BIP 324 short
message identifiers save the 12-byte ASCII command overhead for messages that are
sent frequently. Stale-tip announcements are expected to be rare, and each
announcement already carries at least a 32-byte fork point and one 48-byte
compressed header, so the bandwidth saved by replacing the `staletip` command
name with a one-byte identifier would be negligible. Since the one-byte identifier
space is limited, it is better reserved for messages that are common enough for
the per-message saving to matter.
The test-network guidance is stricter because test networks have weaker
anti-spam properties than mainnet. Signet headers cannot be fully validated
without the block data containing the signature, and testnet difficulty rules can
make low-work stale headers cheap to produce. The specification therefore leaves
room for implementations to disable the feature on test networks or apply
stronger relay policy there than they would on mainnet.
## Test Networks
Making this feature available on test networks raises additional concerns, as
they are less protected by proof of work, and thus may have a larger attack
surface for denial of service issues. As a result, node software implementing
this BIP MAY choose to do so only for mainnet.
### Signet
The default signet is expected to reorg roughly once per day, with a branch of
random length. Stale tips are therefore common on signet rather than rare, and a
stale branch may be longer than `MAX_STALETIP_HEADERS`. Such a branch will not be
relayed via `staletip`; nodes fall back to normal header synchronisation for it,
and implementations MAY raise the limit on signet.
The signet network is designed around the assumption that all valid blocks are
signed, and thus much lower proof of work is needed. However, because the block
signatures are included in the coinbase transaction, they cannot be verified if
only the block's headers are available. As such, when implementing this BIP for
signet:
* Nodes MUST NOT send `staletip` messages when they do not have the
corresponding block data needed to verify the signet signature.
* Nodes MAY disconnect peers that send a `staletip` message with `have_block`
set to `false`.
* Nodes SHOULD set `prefers_blocks` to `\x01` when negotiating the feature, as
they will always need to download the full block data for a stale tip to
validate the signature.
In addition, when the difficulty is low, it is possible that varying the `nBits`
field of a valid block will generate another valid block (see [#33266][#33266]).
As such:
* Nodes SHOULD consider variant headers, where the previous block and Merkle
root are both the same, to be duplicates, and only advertise the first seen
variant as a stale tip.
While nodes MAY also deduplicate such variant headers when receiving them via
`staletip` messages, if they do not perform the same deduplication logic when
receiving such headers via standard `headers` or `block` relay, that provides
little protection. Deduplicating when sending `staletip` messages is primarily
aimed at avoiding nodes implementing this BIP from being used to amplify
attacks.
Only variant tips need to be deduplicated: it is possible, though unlikely, that
valid signed blocks may be descendants of variant blocks. These blocks will not
themselves be variants, as they will have different previous blocks.
### Testnet3 and Testnet4
While testnet3 and testnet4 blocks are proof-of-work based like mainnet, they
both allow for low difficulty blocks:
* testnet3 allows minimum difficulty blocks when a block's timestamp is 20
minutes after the previous block's timestamp, and resets the difficulty on an
ongoing basis if this occurs for the last block in a retarget period.
* testnet4 updates these rules, as specified in [BIP 94][BIP94], basing the
difficulty calculation for the first block in a new retarget period on the
difficulty of the first block in the previous retarget period, rather than
the previous block, avoiding a reset.
As a result, nodes could see many valid stale tips with minimum difficulty,
either due to such tips being created with long timestamps, or, on testnet3,
because the difficulty has been reset. To avoid this scenario, when implementing
this BIP on testnet3 or testnet4:
* Nodes SHOULD only advertise stale tips when the stale tip itself has a
difficulty greater than the minimum difficulty.
* Nodes MAY advertise stale tips only when the stale tip itself has a
difficulty greater than some higher threshold, for example 1,000,000.
## Backward Compatibility
This BIP introduces a new P2P message (`staletip`) and relies on [BIP 434][BIP434]
for feature negotiation to avoid impacting nodes that do not support this BIP.
Nodes MUST NOT send `staletip` messages to peers that have not negotiated the
`staletip` feature.
Nodes that do not support this BIP may still see more frequent notifications of
when their peers update to a new active tip, if implementations choose to make
active-tip `inv` announcements to all peers rather than only to peers that
negotiated the `staletip` feature.
## Privacy Impact
Sharing stale tips raises several potential privacy concerns.
The first concern is fingerprinting. A node's behaviour when negotiating and
sharing stale tips may make it easier for peers to distinguish that node from
others on the network, perhaps allowing attackers to relate a node's onion
address with its IPv4 address. The BIP 434 feature advertisement itself, the
`prefers_blocks` value, active-tip announcement behaviour, and stale-tip relay
timing are all potentially fingerprintable. Since stale tips are expected to
propagate fairly efficiently and consistently, and to remain rare, this is not
expected to be a significant problem in practice. For users that remain
concerned, disabling stale tip relay entirely is probably the best approach.
The second concern is for miners with private transaction pools. In that case,
miners have a selection of transactions that they wish to mine themselves
without giving other miners the opportunity to compete with them, only
publishing them when they construct a block with valid proof-of-work that
includes them. Relaying those transactions via `staletip` announcements will
largely be harmful to a private transaction pool operator, as it will not help
them win the stale block race. It will only allow nodes to reorg slightly faster
if they do win, but if they lose the stale block race it will make their private
transactions available to competing miners. This is a particular concern if the
reason for the block being stale is due to inefficiencies in updating ASIC
miners to a new work target, in which case there is almost no chance of winning
the stale block race. As such, miners operating private transaction pools should
probably disable stale tip relay entirely on the nodes they use for processing
their mined blocks.
## Reference Implementation
A prototype implementation is available at:
* Bitcoin Core branch: https://github.com/w0xlt/bitcoin/tree/staletip-v4
## Test Vectors
### Signet Stale Branch (19 blocks)
This serialization test vector is from signet, where a 19-block stale branch
occurred at height 287767-287785. Because this vector has `have_block` set to
`false`, it is not an example of a Signet `staletip` message that conforming
nodes should relay under the Signet-specific rules above.
| Field | Value |
| ----- | ----- |
| Network | Signet |
| Fork point height | 287766 |
| Fork point hash | `00000012602fde2eaf33a90523f42fb07ca854c1d26108782dc4592a80507e1c` |
| Stale tip height | 287785 |
| Stale tip hash | `0000000024ff924ff932668d497bba7da9157559a68d9c87d2f28d22e5e4a001` |
| Branch length | 19 |
| `have_block` | `false` |
Serialized `staletip` payload (946 bytes):
```
1c7e50802a59c42d780861d2c154a87cb02ff42305a933af2ede2f60120000001300000020
67aa2937b235e209f1ad40f37b17dcad8ca02d1fb9cf4c4928902eab3ff853a6cda16e6916
58151d81ad7b0400000020721ab6f36042842bab4583f98e15181b646c47c7b9dfb07a204c
4e5152e54e97fda46e691658151dd2dcec1800000020ddc6b628101b404f78d446591a623e
12bb7e7cd2ae1593d0cc6ef082e19fa81014a66e691658151d46070e10000000208be5b960
7b20ac0163b885881aa6018ef60a5637b93a9cd85981515a169a31c36aab6e691658151de4
e10600000000206c974ef806b2e6a21cc9a53fedcbd5d5e203b4929a1bd7c5859ec77d0a75
5c1d64ae6e691658151d2a2e130e0000002039505fa2ee91f5e5b2540b4a9a7f02ed58d79f
c387412cf2520a39ee16bf435f6eb16e691658151db29c830a00000020411bce69cda2b239
c74faea1f0cfaceabd90377114dc0ace14e2a58858cd4f9a91b46e691658151d4c2d3f0500
00002096464a2ec42fea8419a5c6adcc41ca7dc3f53461cd0afc880f76498aa8eedbf993b5
6e691658151d5e99700b00000020c3cad611ee3c7bffec3f80fc83f68192ca0a86e8c89050
f8353480310d080caf08b76e691658151dbc61a6150000002079e36b5a06bb5a524a2f5bad
6725451613c354cbf64f9a0283f77fe90782d59fcbba6e691658151d6c669300000000204c
69fd467818994e7ff885181e7451d23869a7fd19447dad7a95398077ed7774dcba6e691658
151dc9c7ff02000000208ea9a871b12cd45226ab6df280ec46b8c9ca196b98dc8adc27718c
ab333e19a50abc6e691658151df6bae902000000209c182979424a5c85d55d5bddbf3b9421
b01b7810edf95ad2529552dd2eec80848ebd6e691658151d439e5c0000000020cfb9f6d164
3613dfdd23a6e1eabadac38c392f985f5dc05b00ef682c428922c96cbe6e691658151d278b
4d1400000020eea983b8d64947368de4f7626531a3134d18f20a8d87462ba011a4f56a53b6
ac4fbf6e691658151ddae8640800000020140bc70b1a3282fbebf9d48ff40a9b73ab7c76ad
522799cee73315e9c21aab50dcc06e691658151dedaae90f000000209b91a056fe089429f2
ba4298e764acb4177249ce7d68e6883836f70d1d9218b355c16e691658151d28a432060000
0020b26e80c6a12cc4595556d2318fda564ced5d2a526c7b77f57a9897a56c10539056c16e
691658151dc64fb50000000020369d36495749c96d36260d839abfa703a611aad7b3016342
ef05d0412f42221e6ac56e691658151d8f8bfb0f00
```
Payload breakdown:
* Bytes 0-31: `fork_point` (`uint256`, little-endian)
* Byte 32: vector length (`0x13` = 19)
* Bytes 33-944: 19 `CompressedHeader` entries (48 bytes each)
* Byte 945: `have_block` (`0x00` = `false`)
### BCH Fork Headers (18 blocks)
This test vector uses mainnet headers from the BCH fork. Blocks 478559-478576
have valid Bitcoin headers meeting proof-of-work requirements, but are invalid
Bitcoin blocks because block 478559 exceeds the 1MB block size limit. Block
478577 changed difficulty under BCH's Emergency Difficulty Adjustment rules,
making it an invalid Bitcoin header as well.
| Field | Value |
| ----- | ----- |
| Network | Mainnet |
| Fork point height | 478558 |
| Fork point hash | `0000000000000000011865af4122fe3b144e2cbeea86142e8ff2fb4107352d43` |
| Stale tip height | 478576 |
| Stale tip hash | `000000000000000001416af072f8989829f4c60a1a9658e1cec08411798e4ffa` |
| Branch length | 18 |
| `have_block` | `false` |
Serialized `staletip` payload (898 bytes):
```
432d350741fbf28f2e1486eabe2c4e143bfe2241af65180100000000000000001200000020
abaa4bd8a48c1c6bc08ee39b66065e5e9484304cab8b56d5eed3e40b1ac996c899c4805935
47011822ca4ae80000002082afc8ef7eb41a4ecac1fea46983742e491f804ad662e3745ab9
c6c4297d8a0862c980593547011840a772cb0200002058874e50628fdf83aeea4e8cbc7ade
946e9ba14bcb1d8ffb28c3daf8ade84df65fca805935470118e2f5100300000020111b85f9
d3b969a1f7ff3d50af08893c500edfc5623b96dbeab6daf16a5164a40ace805935470118c4
f4240a0000002040a045063b551b61d6a1c9db6d3231e2d7403185bbb2332ae1f66db24aac
7fa288d8805935470118f15dd76b0000002070cb14529e8757c359c2e8b1e987f6eee6fbc4
472ee9ad4a2e5df6905c19d6d70bed80593547011885ae00d0000000209653314c1d73e463
0bb485fb25ce7a2583cec7c3ccfc27a6d24163be1e9fb19530f4805935470118f17ad2c500
000020cdf48b8e7ac6bf3a51d1878ee3ff7e6fd0022926dd69cc5cc8d9126e77c4dba809f5
8059354701188114e83600000020890cf1dc60edbf0fd4fb667f28ac785849c031d8b24d5e
5a0af56ee2bd8a739bf51081593547011812f32a9600000020ff5244613ad20fdc39b7ee6f
4fbc7016432d2dbf45c2a950c59665b39c3954b5b525815935470118aa790d6600000020ae
ed520e7c1693de5cfe7531e7d3e73dff7858b09cb6e1ec29229a75c3da2b92453e81593547
011830a4314d000000202e4a4054e64c3f5810c23ec0144d9793aab2d5a7d77d1660eee24d
3d55e8b715b543815935470118da1c00e00000002073152af68778a98fd984a158aeb29d28
e094e23c3a7dff02260c345791e52498c3fb8159354701188d5abed90000002022606e744a
29f9d4a67ff1fcd2f0e31300ddbd145f8f1db8a68270bfbde77dd88fff8159354701186af1
75f8000000209f5db27969fecc0ef71503279069b2df981ba545592a7b425f353b5060e77f
3e7e13825935470118da70378e000000202f0d316b08350f5cd998c6a11762d10adb9f951b
5f79ce2a073f8187c05f561f1b1c8259354701184834c62300000020cf8fc3bad8dad139a3
dd6a30481d87e1f760122573168002cc9ef7a58fc53ad387848259354701188a3b54f70000
00200eae92d9b46d81a011a79726a802d4eb195a7af8b70a09b0e115c391968c50d51c8a82
5935470118cd786d1300
```
Payload breakdown:
* Bytes 0-31: `fork_point` (`uint256`, little-endian)
* Byte 32: vector length (`0x12` = 18)
* Bytes 33-896: 18 `CompressedHeader` entries (48 bytes each)
* Byte 897: `have_block` (`0x00` = `false`)
## Copyright
This BIP is licensed under the 3-clause BSD license.
[BIP94]: https://github.com/bitcoin/bips/blob/master/bip-0094.mediawiki
[BIP324]: https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki
[BIP434]: https://github.com/bitcoin/bips/blob/master/bip-0434.md
[#33266]: https://github.com/bitcoin/bitcoin/issues/33266
[#19858]: https://github.com/bitcoin/bitcoin/pull/19858
[^rat-compressedheader]: Omitting the previous block hash from each header saves
32 bytes per header (40%), as this field can be reconstructed from the
preceding headers in the message. This does not apply to the first header,
which is why the fork point must be included explicitly. This BIP does not
attempt to omit `nBits` or compress `nTime` or `nVersion`, because
reconstructing those fields is significantly more complicated for
comparatively much less potential gain.
[^rat-maxheight]: A window of 1000 blocks (about 7 days) provides a
reasonable balance between limiting resource usage, and maximising
propagation potential. By limiting the distance to the tip to being no
older than the previous retarget period, we ensure that the difficulty
of creating a stale block is comparable to creating a new tip, making
it uneconomical to use this as a spamming mechanism. By choosing
a longer period, we provide the opportunity for nodes interested in
stale block data for research purposes plenty of opportunity to obtain
the data. In particular, many nodes will tend to discover it through
the periodic blocks-only connections (see Bitcoin Core PR [#19858]).
[^rat-maxforklen]: Limiting the branch length to 20 headers avoids nodes
being burdened with tracking deliberate chain splits. When
incompatible consensus rules are enforced, the majority chain need not
continue sharing the minority's fork beyond a short window. Twenty
blocks is sufficient to capture the short-term stale blocks useful
for reorg detection, while avoiding long-term tracking of persistent
chain splits.
[^rat-denialofservice]: Limiting stale branches to 20 headers bounds the
per-message size to under 1kB. Combining this with a default limit of 10
previously known stale tips advertised to a peer ensures that the initial
relay bandwidth is under 10kB. Restricting relay to recent tips whose proof
of work is comparable to the active chain makes stale-tip spam costly on
mainnet.
[^rat-ignoreinvalid]: Ignoring rather than punishing allows nodes to apply
different limits for what stale tips they accept. If one node uses stricter
limits than its peers, this avoids the risk that honest announcements from
those peers will cause disconnection and potentially network partitions.
This is also consistent with how invalid headers are handled.