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:
@@ -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
708
bip-0332.md
Normal 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.
|
||||||
Reference in New Issue
Block a user