BIP332: Stale Tip Relay #2241

pull pseudoramdom wants to merge 1 commits into bitcoin:master from pseudoramdom:staletip-bip changing 2 files +701 −0
  1. pseudoramdom commented at 4:45 PM on August 6, 2026: none

    This BIP is a specification for a new an opt-in P2P message,staletip, for relaying recent stale tips between peers. A proof-of-concept implementation is available at https://github.com/w0xlt/bitcoin/tree/staletip-v4.

    Mailing list post - https://gnusha.org/pi/bitcoindev/d92f1615-368b-4406-b326-a1799c72a555n@googlegroups.com/ Discussion - https://groups.google.com/g/bitcoindev/c/AwOPNxF15mU

    Feedback is welcome.

    LLM Disclosure: LLMs were used to help develop the proposal’s initial outline and to rephrase portions of the draft. The proposal’s technical content was reviewed and finalized by the authors.

  2. jonatack added the label New BIP on Aug 6, 2026
  3. in bip-staletip.md:80 in 5b30d29221
      75 | +
      76 | +### Staletip Feature Definition
      77 | +
      78 | +This BIP defines a new [BIP 434][BIP434] feature id ("the `staletip` feature"):
      79 | +
      80 | + * `https://github.com/w0xlt/bitcoin/tree/staletip-v4`
    


    edilmedeiros commented at 10:51 PM on August 10, 2026:

    This can be left to the reference implementation section.


    pseudoramdom commented at 5:43 PM on August 12, 2026:

    The URL here is the temporary experimental featureid, not just a reference implementation link. BIP 434 requires an unassigned experimental feature to use a globally unique identifier, such as its repository URL. I’ll clarify that distinction and move the standalone implementation link to the Reference Implementation section.

    For features published as a BIP, the featureid SHOULD be the assigned BIP number, eg "BIP434", or be based on the BIP number (eg, "BIP434v2" where the "v2" suffix covers versioning, or "BIP434.3" where the ".3" suffix covers part 3 of the BIP). For experimental features that do not (yet) have a BIP number assigned, some other unique identifier MUST be chosen, such as a URL to the repository where development is taking place, or the sha256 digest of some longer reference.

  4. in bip-staletip.md:84 in 5b30d29221
      79 | +
      80 | + * `https://github.com/w0xlt/bitcoin/tree/staletip-v4`
      81 | +
      82 | +If this specification is assigned a BIP number, the feature id SHOULD be updated
      83 | +to a BIP-number based identifier as recommended by BIP 434, for example
      84 | +`BIPxxx` or `BIPxxxv1`.
    


    edilmedeiros commented at 10:56 PM on August 10, 2026:

    It's weird to expect an specification proposal might not get a number… Why not specify BIPxxx for now an update when a number is assigned, without the conditional?


    edilmedeiros commented at 11:03 PM on August 10, 2026:

    Actually, this gets confusing here: the feature is called staletip everywhere, but the serialization in BIP434 featureid part of the message should be BIPxxx?


    pseudoramdom commented at 5:44 PM on August 12, 2026:

    staletip is the descriptive feature name and P2P message command. It isn’t the serialized BIP 434 featureid. The experimental implementation currently uses the repository URL as its globally unique featureid, and the published specification will the BIP number. I’ll make that distinction explicit.

  5. in bip-staletip.md:101 in 5b30d29221
      96 | +than `\x00` or `\x01`, MUST ignore that peer's `staletip` feature
      97 | +advertisement. They SHOULD NOT disconnect solely because the feature data is not
      98 | +understood.
      99 | +
     100 | +For future compatibility, nodes MUST ignore any additional feature data bytes
     101 | +after the first byte.
    


    edilmedeiros commented at 10:59 PM on August 10, 2026:

    Since the feature is versioned, this slack is really needed? I mean, why not accept EXACTLY one byte with the two expected values?


    pseudoramdom commented at 5:45 PM on August 12, 2026:

    Good point :). BIP 434’s versioned feature IDs give us a clean way to make future changes. I’ll update the specification to require one byte with value 0x00 or 0x01.

  6. in bip-staletip.md:105 in 5b30d29221
     100 | +For future compatibility, nodes MUST ignore any additional feature data bytes
     101 | +after the first byte.
     102 | +
     103 | +### BIP 434 Negotiation
     104 | +
     105 | +Nodes implementing this BIP MUST use BIP 434 to negotiate support before
    


    edilmedeiros commented at 10:59 PM on August 10, 2026:
    Nodes implementing this specification MUST use BIP 434 to negotiate support before
    
  7. in bip-staletip.md:21 in 5b30d29221
      16 | +
      17 | +## Abstract
      18 | +
      19 | +Bitcoin miners sporadically produce stale blocks: valid blocks or valid block
      20 | +headers that do not become part of the node's active chain. This BIP defines an
      21 | +optional peer-to-peer (P2P) feature for announcing recent stale chain tips to
    


    edilmedeiros commented at 11:04 PM on August 10, 2026:
    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
    
  8. in bip-staletip.md:123 in 5b30d29221
     118 | +Advertising the feature only signals willingness to receive `staletip` messages.
     119 | +It does not oblige a node to send them or to serve block data. A node MAY
     120 | +advertise the feature to receive stale tips while never sending any itself, for
     121 | +example a node that only collects stale tips for research.
     122 | +
     123 | +### The `staletip` Message
    


    edilmedeiros commented at 11:06 PM on August 10, 2026:

    I think your specification should start here, after all, the new message is the whole point of the specification. The subsections that came before are details (parametrization) or negotiation. Not only, the previous subsection makes mention to the staletip message, but it was never defined before this point which makes the text confusing.


    pseudoramdom commented at 6:07 PM on August 12, 2026:

    Fair enough. I’ll move the staletip message definition, including the CompressedHeader format, to the start of the Specification section.

  9. in bip-staletip.md:116 in 5b30d29221
     111 | +`staletip` feature. Nodes MAY disconnect peers that repeatedly send such
     112 | +messages without negotiation.
     113 | +
     114 | +Nodes advertising this feature SHOULD send the BIP 434 `feature` message with
     115 | +the `staletip` feature id and feature data defined above before sending their
     116 | +`verack` message.
    


    edilmedeiros commented at 11:10 PM on August 10, 2026:

    This is already required as per BIP434, AFAIK. Since BIP434 is required, I don't see the need to repeat it's mechanisms in this document.


    pseudoramdom commented at 8:48 PM on August 12, 2026:

    Removed it.

  10. in bip-staletip.md:125 in 5b30d29221
     120 | +advertise the feature to receive stale tips while never sending any itself, for
     121 | +example a node that only collects stale tips for research.
     122 | +
     123 | +### The `staletip` Message
     124 | +
     125 | +The `staletip` message is a post-handshake P2P message with the ASCII message
    


    edilmedeiros commented at 11:11 PM on August 10, 2026:

    "post-handshake" isn't obvious?

  11. in bip-staletip.md:131 in 5b30d29221
     126 | +type `staletip` and the following payload:
     127 | +
     128 | +| Type | Name | Description |
     129 | +| ---- | ---- | ----------- |
     130 | +| `uint256` | `fork_point` | The block hash used as the previous block hash of the first compressed header |
     131 | +| vector of `CompressedHeader` | `headers` | The stale branch headers after `fork_point`, ordered from oldest to newest |
    


    edilmedeiros commented at 11:16 PM on August 10, 2026:

    I think oldest to newest may by confusing here without a proper definition. I believe you meant something like "in chain order" (in the order they would appear if connected to the best chain). But even this can be confusing, e.g. what do I do if I have two different stale blocks that refer to the same previous block? I'm not sure the best solution, maybe order they lexicographically.


    edilmedeiros commented at 11:21 PM on August 10, 2026:

    It is defined in line 147, but the raised issue seems to still persist.


    pseudoramdom commented at 8:49 PM on August 12, 2026:

    Updated to in chain order

  12. in bip-staletip.md:135 in 5b30d29221
     130 | +| `uint256` | `fork_point` | The block hash used as the previous block hash of the first compressed header |
     131 | +| vector of `CompressedHeader` | `headers` | The stale branch headers after `fork_point`, ordered from oldest to newest |
     132 | +| `bool` | `have_block` | Whether the sender is willing to serve block data for the stale branch |
     133 | +
     134 | +The `fork_point` MUST be known by the receiver and MUST be the predecessor of
     135 | +the first reconstructed header. In the common case it is the block where the
    


    edilmedeiros commented at 11:17 PM on August 10, 2026:

    How can a remote node ensure that?


    pseudoramdom commented at 6:11 PM on August 12, 2026:

    The sender can determine this from its per-peer header state. A fork_point is known if it is an ancestor of a block previously announced by the receiver OR of a header the sender previously sent on the same peer. I can clarify this if that’s not clear in the spec.


    edilmedeiros commented at 8:42 PM on August 12, 2026:

    Aren't you assuming an internal implementation detail from Core? Other implementations like btcd, floresta or libbitcoin all maintain that information?


    pseudoramdom commented at 8:56 PM on August 12, 2026:

    That's a fair push back How's this then -

    Nodes sending a staletip message MUST have a basis for expecting the receiver to know the fork_point. How this is determined is implementation-specific. For example, the sender may use block or header announcements previously exchanged with the receiver on the same connection.


    ajtowns commented at 1:22 AM on August 13, 2026:

    Aren't you assuming an internal implementation detail from Core?

    I think this spec should state that that compliant nodes SHOULD always announce their latest tip to staletip peers, either by a CMPCTBLOCK message, a HEADERS message, or an INV message, even if that peer had already announced the same block; and should use this information when provided by a staletip peer to calculate the appropriate fork point.

  13. in bip-staletip.md:141 in 5b30d29221
     136 | +stale branch diverges from the receiver's active chain. It MAY be a later known
     137 | +stale-branch header if both peers are expected to already know that header; in
     138 | +that case the `headers` vector only contains the unknown suffix.
     139 | +
     140 | +The `headers` vector is serialized as normal: encode the length of the vector as
     141 | +a 1, 3, 5 or 9 byte `CompactSize`, then encode each member of the vector. Only
    


    edilmedeiros commented at 11:18 PM on August 10, 2026:
    a 1, 3, 5 or 9 byte `CompactSize`, then serialize each member of the vector. Only
    
  14. in bip-staletip.md:148 in 5b30d29221
     143 | +
     144 | +The `bool` serialization is a single byte `\x00` for false, and a single byte
     145 | +`\x01` for true. Other encodings are malformed.
     146 | +
     147 | +The `headers` field is ordered from oldest (the header immediately following
     148 | +`fork_point`) to newest (the stale tip itself).
    


    edilmedeiros commented at 11:22 PM on August 10, 2026:

    This is somewhat out of place: the previous and the following paragraphs both refer to the have_block field.

  15. in bip-staletip.md:160 in 5b30d29221
     155 | +makes no claim about stale-branch block-data availability. A sender SHOULD NOT
     156 | +set `have_block` to `true` unless it expects a request for the stale tip block to
     157 | +succeed.
     158 | +
     159 | +Because stale tips are very rare, this BIP does not reserve a 1-byte [BIP
     160 | +324][BIP324] message type ID for the `staletip` message.
    


    edilmedeiros commented at 11:24 PM on August 10, 2026:

    Should be very rare on mainnet, but I believe this should work well on the testnets as well. And there, stale blocks are quite frequent, so this is a bad assumption.


    pseudoramdom commented at 6:37 PM on August 12, 2026:

    This is a good point. @w0xlt @ajtowns - We'll probably have repeated announcements ion testnet if the feature is enabled. Do you think this warrants reserving a 1-byte ID in BIP 324. It would save 12 bytes per message.


    ajtowns commented at 1:24 AM on August 13, 2026:

    I have a different patchset to make BIP324 message type id assignment automatic and not need specifying/coordinating; https://github.com/ajtowns/bitcoin/tree/202605-bip324-id

  16. in bip-staletip.md:185 in 5b30d29221
     180 | +stale tips they are aware of. If so:
     181 | +
     182 | +- Nodes SHOULD send `staletip` messages advertising recent stale tips
     183 | +  that they are aware of to peers that support the `staletip` feature.
     184 | +- `staletip` messages MUST NOT be sent to peers that have not indicated
     185 | +  support for the `staletip` feature.
    


    edilmedeiros commented at 11:31 PM on August 10, 2026:

    Repetition of the negotiation section?

  17. in bip-staletip.md:235 in 5b30d29221 outdated
     230 | +  truncated data, invalid boolean values, or trailing bytes. Nodes MAY
     231 | +  disconnect peers for malformed payloads.
     232 | +- Nodes SHOULD reject (ignore) `staletip` messages where the `fork_point` is
     233 | +  not known, and MAY disconnect peers that repeatedly send unknown fork points. 
     234 | +  Sending such a message violates the requirement above that senders MUST NOT 
     235 | +  send `staletip` messages unless they are sure the receiver knows the 
    


    edilmedeiros commented at 11:32 PM on August 10, 2026:

    Again, I don't see how this can be asserted.

  18. in bip-staletip.md:252 in 5b30d29221
     247 | +- Nodes SHOULD ignore messages whose stale tip is not recent according to local
     248 | +  policy, for example more than `STALETIP_RECENT_WINDOW` blocks behind the
     249 | +  receiver's active tip.
     250 | +- Nodes MAY ignore messages that violate local denial-of-service checks, or MAY
     251 | +  partially process headers until local limits are reached. Nodes SHOULD NOT
     252 | +  disconnect or otherwise punish peers solely for exceeding local policy limits.
    


    edilmedeiros commented at 11:34 PM on August 10, 2026:

    Can't this create a DoS vector somehow?


    pseudoramdom commented at 8:58 PM on August 12, 2026:

    Yes. The intent was only to avoid punishing a peer for a one-off message that exceeds implementation-specific policy limits. Repeated messages can be treated as abuse, so I’ve clarified that implementations may rate-limit or disconnect peers that repeatedly exceed those limits.

  19. in bip-staletip.md:256 in 5b30d29221
     251 | +  partially process headers until local limits are reached. Nodes SHOULD NOT
     252 | +  disconnect or otherwise punish peers solely for exceeding local policy limits.
     253 | +- After receiving a `staletip` message that passes local denial-of-service
     254 | +  checks, nodes SHOULD reconstruct the block headers from the `CompressedHeader`
     255 | +  encoding, validate the headers, and add any new valid headers to their block
     256 | +  database.
    


    edilmedeiros commented at 12:27 PM on August 11, 2026:

    add any new valid headers to their block database.

    Isn't this an internal implementation detail? A specification for networking messages should specify the observable behavior between peers, not what they do internally with the messages.


    pseudoramdom commented at 8:59 PM on August 12, 2026:

    Removed that part of the sentence :)

  20. in bip-staletip.md:258 in 5b30d29221
     253 | +- After receiving a `staletip` message that passes local denial-of-service
     254 | +  checks, nodes SHOULD reconstruct the block headers from the `CompressedHeader`
     255 | +  encoding, validate the headers, and add any new valid headers to their block
     256 | +  database.
     257 | +- Nodes SHOULD ignore any headers found to be invalid, and SHOULD NOT disconnect
     258 | +  or otherwise punish peers for relaying invalid headers[^rat-ignoreinvalid].
    


    edilmedeiros commented at 12:28 PM on August 11, 2026:

    Can't this open a DoS vector?


    ajtowns commented at 1:26 AM on August 13, 2026:

    DoS vectors for p2p are for processing time or memory/disk usage or amplification attacks where we receive a small amount of garbage data and then send a large amount of garbage data to other peers as a result. The bandwidth it takes to send the message isn't a DoS vector, because there's a million ways to send data to a p2p node, and you can't prevent all of them.

  21. in bip-0332.md:260 in 5b30d29221 outdated
     255 | +  encoding, validate the headers, and add any new valid headers to their block
     256 | +  database.
     257 | +- Nodes SHOULD ignore any headers found to be invalid, and SHOULD NOT disconnect
     258 | +  or otherwise punish peers for relaying invalid headers[^rat-ignoreinvalid].
     259 | +- If `have_block` is `true`, nodes that prefer to collect the full block data
     260 | +  SHOULD request missing block data for the announced stale branch in the normal
    


    edilmedeiros commented at 12:30 PM on August 11, 2026:

    SHOULD request missing block data for the announced stale branch in the normal

    Sounds that MAY would work better here. What if I already have that specific stale block, SHOULD I request it again?


    pseudoramdom commented at 8:22 PM on August 12, 2026:

    What if I already have that specific stale block

    Spec says "SHOULD request missing block data". If the node already has that stale block, its data is not missing and should not be requested again.

  22. in bip-0332.md:668 in 5b30d29221 outdated
     663 | +    32 bytes per header (40%), as this field can be reconstructed from the
     664 | +    preceding headers in the message. This does not apply to the first header,
     665 | +    which is why the fork point must be included explicitly. This BIP does not
     666 | +    attempt to omit `nBits` or compress `nTime` or `nVersion`, because
     667 | +    reconstructing those fields is significantly more complicated for
     668 | +    comparatively much less potential gain.
    


    edilmedeiros commented at 12:37 PM on August 11, 2026:

    It assumes there's only one stale branch, which is probably not true in testnet and may even happen in mainnet. The design can deal with more than one competing branch?


    pseudoramdom commented at 5:58 PM on August 12, 2026:

    Each staletip message describes one linear branch. If there are multiple competing branches, the sender will have to send a separate message for each one. I’ll clarify this in the spec.

  23. in bip-0332.md:319 in 5b30d29221 outdated
     314 | +    }
     315 | +```
     316 | +
     317 | +Note that headers are reconstructed in order, from oldest (closest to the
     318 | +`fork_point`) to newest (the stale tip itself).
     319 | +
    


    edilmedeiros commented at 12:39 PM on August 11, 2026:

    Not sure this is necessary and potentially implies maintaining this code in the specification over time.


    pseudoramdom commented at 8:48 PM on August 12, 2026:

    I think having the reconstruction algorithm in the specification is helpful. That said, it doesn’t need to be C++, so I’ve replaced it with pseudocode.

  24. in bip-staletip.md:318 in 5b30d29221
     313 | +        prev_hash = headers.back().GetHash();
     314 | +    }
     315 | +```
     316 | +
     317 | +Note that headers are reconstructed in order, from oldest (closest to the
     318 | +`fork_point`) to newest (the stale tip itself).
    


    edilmedeiros commented at 12:39 PM on August 11, 2026:

    This clarification seems useful. See other comment about ordering.

  25. edilmedeiros commented at 12:42 PM on August 11, 2026: contributor

    Thanks for submitting your proposal. Gave a round of review, some comments are about the technique itself, but others are more editorial to try to improve and clarify the specification.

  26. 0xB10C commented at 8:56 AM on August 12, 2026: none

    Thanks for opening this here. Plan to review!

  27. pseudoramdom force-pushed on Aug 12, 2026
  28. pseudoramdom commented at 8:45 PM on August 12, 2026: none

    Thanks for submitting your proposal. Gave a round of review, some comments are about the technique itself, but others are more editorial to try to improve and clarify the specification. @edilmedeiros Thank you so much for taking the time to review.


    Rebased and added a commit (d055be18e2cc46b9cdbc128cbe03478b6b094f18) to address Edil's feedback. The question of whether to reserve a one-byte BIP 324 message ID remains open.

  29. in bip-0332.md:61 in 1f869b30a4 outdated
      56 | +Implementations of this BIP use the following limits unless otherwise noted:
      57 | +
      58 | +| Name | Value | Meaning |
      59 | +| ---- | ----- | ------- |
      60 | +| `MAX_STALETIP_HEADERS` | 20 | Recommended maximum number of `CompressedHeader` entries in one `staletip` message |
      61 | +| `STALETIP_RECENT_WINDOW` | 1000 blocks | Recommended maximum distance from the receiver's active tip |
    


    mzumsande commented at 1:22 PM on August 18, 2026:

    what is the reason for choosing 1000 blocks when Bitcoin Core, as the most used implementation, would reject any header more than ~144 blocks old (GetAntiDoSWorkThreshold)? With the current implementation, it seems that these older headers would be sent out but would not be accepted by the peer by default. Or does the BIP intend to recommend that participating peers accept headers to their database within STALETIP_RECENT_WINDOW, basically recommending to lower the 144 block threshold?


    ajtowns commented at 5:09 AM on August 19, 2026:

    Just a round number; more inspired by the average interval between stale blocks on mainnet (so that there's a decent chance a new node using this feature would see data fairly quickly) than anything else.


    mzumsande commented at 10:18 AM on August 19, 2026:

    ok, but I find it not ideal to ask for stale headers and then immediately discard them because they are too old to satisfy GetAntiDoSWorkThreshold.


    pseudoramdom commented at 3:59 PM on August 19, 2026:

    STALETIP_RECENT_WINDOW number is arbitrary. 1000 blocks gives monitoring nodes roughly seven days to discover staletip data.

    With the current implementation, it seems that these older headers would be sent out but would not be accepted by the peer by default.

    Good catch, this might've been an oversight in the PoC implementation. Staletip relay path should probably define a separate bounded acceptance policy.


    ajtowns commented at 1:29 AM on August 20, 2026:

    ok, but I find it not ideal to ask for stale headers and then immediately discard them because they are too old to satisfy GetAntiDoSWorkThreshold.

    Right, but that's a problem with the implementation, not the spec? (Beyond the spec noting the interaction to help implementers avoid pitfalls, anyway)

    I think 48 hours / 288 blocks hours is likely enough for a stale tip to propagate to 99% of interested nodes just via extra-block-relay-only connections if only 1% of listening nodes support stale tip relay, and 24 hours / 144 blocks is likely enough if ~2.5% of listening nodes support the feature. Those numbers are worse if the extra-block-relay-only connections fail frequently due to addrman poisoning, or better if there are enough stale tip relay nodes that they form connected components in the p2p network. Also better if non-listening nodes are participating. So to me, that says changing this to recommend 144 wouldn't be overly concerning, but I'd still lean towards 1000 to maximise propagation chance to anyone interested.

    def propogate(lnodes, support, blks):
        probes = blks*2 # 10min blks, 5min extra-b-r-o-conn
        have = [False]*int(lnodes*support)
        t = len(have)
        assert t > 1
        have[0] = True
        for _ in range(probes):
            for i in range(t): # each node makes a probe
                k = int(random.random() * lnodes) # who does i connect to?
                if k < t and (have[i] or have[k]): # staletip relay?
                    have[i] = have[k] = True
        return sum(have)/t
        
    sum(propogate(20000, 0.01, 144) for _ in range(20))/20
    # 0.542 -- 54% of staletip relay nodes see the stale tip with 1% sat over 144 blocks
    sum(propogate(20000, 0.01, 288) for _ in range(20))/20
    # 0.9894999999999999 -- 99% of staletip relay nodes see the stale tip with 1% sat over 288 blocks
    sum(propogate(20000, 0.025, 144) for _ in range(20))/20
    # 0.9994999999999999 -- 99% of staletip relay nodes see the stale tip with 2.5% sat over 144 blocks
    sum(propogate(20000, 0.003, 1000) for _ in range(20))/20
    # 0.9991666666666668 -- 99% of staletip relay nodes see the stale tip with 0.3% sat over 1000 blocks
    

    murchandamus commented at 8:38 PM on August 21, 2026:

    I was just thinking that in the context of the increased peer limit of Bitcoin Core, this could be even more reliable if blocks-only peers optionally participate in stale tip relay. Perhaps this BIP could make a recommendation whether stale tips should be announced on blocks-only connections or not.


    pseudoramdom commented at 9:32 PM on August 21, 2026:

    Right, keeping 1000 blocks in the spec gives stale tips a better chance to propagate while adoption is low. Implementations can still apply stricter local limits. I’ll add a note that implementations reusing their normal header-processing path should ensure its admission policy is compatible with its staletip recency window.


    ajtowns commented at 3:52 AM on August 23, 2026:

    blocks-only peers optionally participate in stale tip relay

    I don't think block-relay-only mode is documented in a BIP anywhere (treating it as a node configuration details rather than a standard), so it seems slightly weird to introduce it as a BIP-worthy concept here?

    I think stale tip relay/announcements should be done on block-relay-only connections, because that allows the extra-block-relay-only connections to make the p2p graph dynamic for the purposes of this message, making it more likely to achieve wide distribution.

  30. in bip-staletip.md:66 in d055be18e2
      61 | +| `uint256` | `fork_point` | The block hash used as the previous block hash of the first compressed header |
      62 | +| vector of `CompressedHeader` | `headers` | The headers forming a single stale branch after `fork_point`, in chain order |
      63 | +| `bool` | `have_block` | Whether the sender is willing to serve block data for the stale branch |
      64 | +
      65 | +The `fork_point` MUST be known by the receiver and MUST be the predecessor of
      66 | +the first reconstructed header. In the common case it is the block where the
    


    danielabrozzoni commented at 1:43 PM on August 20, 2026:

    In the common case it is the block where the stale branch diverges from the receiver's active chain.

    I think this might be slightly ambiguous. In the example

    A -> B -> C -> D    < best chain
              \
               D'                < stale branch
    

    it is clear from the definition above that the fork_point is C, but one could interpret this sentence as saying that the fork_point is D'.

    Instead, I would say:

    In the common case, it is the last common block before the stale branch diverges from the receiver's active chain.


    pseudoramdom commented at 9:40 PM on August 21, 2026:

    Thanks for taking the time to review.

    In the common case, it is the last common block before the stale branch diverges from the receiver's active chain.

    Agree this is clearer. I'll update it.

  31. in bip-staletip.md:127 in d055be18e2
     122 | +value of 20 is intended to cover the short stale branches useful for reorg
     123 | +detection while avoiding long-term tracking of persistent chain splits.
     124 | +
     125 | +`STALETIP_RECENT_WINDOW` is a resource-management limit[^rat-maxheight]. The
     126 | +1000-block window is about seven days and balances limiting resource usage with
     127 | +allowing stale tips to propagate. `MAX_RETAINED_STALETIPS` is also a
    


    danielabrozzoni commented at 1:56 PM on August 20, 2026:

    nit: I think MAX_RETAINED_STALETIPS should get its own paragraph, instead of being joned at the end of STALETIP_RECENT_WINDOW's paragraph. I would split in two paragraphs.

  32. in bip-0332.md:119 in d055be18e2 outdated
     114 | +
     115 | +| Name | Value | Meaning |
     116 | +| ---- | ----- | ------- |
     117 | +| `MAX_STALETIP_HEADERS` | 20 | Recommended maximum number of `CompressedHeader` entries in one `staletip` message |
     118 | +| `STALETIP_RECENT_WINDOW` | 1000 blocks | Recommended maximum distance from the receiver's active tip |
     119 | +| `MAX_RETAINED_STALETIPS` | 10 | Recommended maximum number of stale tips retained for later relay |
    


    danielabrozzoni commented at 2:45 PM on August 20, 2026:

    What happens when a node already has MAX_RETAINED_STALETIPS stale tips stored and receives a new one? Should it ignore the new tip, or evict an existing one?


    ajtowns commented at 1:30 AM on August 21, 2026:

    Should probably be called "ADVERTISED" (or similar) rather than "RETAINED" -- (except for pruning) core retains all the stale tips it has, eg. Advertising the 10 stale tips with most accumulated work, and in the case of a tie, dropping the oldest is probably reasonable for ensuring the tips most likely to be involved in a reorg are available.

    Choosing randomly would probably maximise the number of tips available to the network, particularly if it were re-randomized per connection.


    pseudoramdom commented at 10:17 PM on August 21, 2026:

    Should it ignore the new tip, or evict an existing one?

    I think the exact policy can remain local to the implementation. Agree with AJ, a reasonable policy would be to keep the tips with the most cumulative proof of work.

    • A new tip with more work would replace the lowest-work entry.
    • Among tips with equal work, the most recently learned tips would be retained.
    • A new tip with less work than every existing entry could be ignored.

    Should probably be called "ADVERTISED" (or similar) rather than "RETAINED"

    MAX_ADVERTISED_STALETIPS sounds more like a lifetime or per-peer announcement limit. How about MAX_STALETIP_RELAY_SET_SIZE?

    I can also include a line when expanding on that constant -

    A node's stale-tip relay set is the set of stale tips currently selected for later announcement.
    
  33. danielabrozzoni commented at 3:02 PM on August 20, 2026: member

    This was a very good read :) I left a question and a couple of nits.

  34. in bip-0332.md:485 in d055be18e2 outdated
     480 | +this BIP on testnet3 or testnet4:
     481 | +
     482 | + * Nodes SHOULD only advertise stale tips when the stale tip itself has a
     483 | +   difficulty greater than the minimum difficulty.
     484 | + * Nodes MAY advertise stale tips only when the stale tip itself has a
     485 | +   difficulty greater than some higher threshold, for example 1,000,000.
    


    murchandamus commented at 8:29 PM on August 21, 2026:

    Wouldn’t one of the interesting uses for this BIP be to map the many branches of testnet4?


    pseudoramdom commented at 10:31 PM on August 21, 2026:

    Good point. I can see mapping competing branches on testnet4 being useful. Do you think the BIP should avoid prescribing a difficulty threshold for testnet3 and testnet4 and leave it entirely to local policy? We could drop these two recommendations and rely on the general proof-of-work, rate-limiting, and resource-bound requirements instead.


    ajtowns commented at 3:47 AM on August 23, 2026:

    Difficulty one blocks can be created at a rate of ~200/s per TH/s, so a map that includes those doesn't seem very useful, and likely opens you up to a disk filling attack: make your diff-1 alternative blocks contain 1MvB worth of txs that conflict with the main chain. They're only listed as MAY and SHOULD requirements, so you could still do this while complying with the bip, though.

  35. murchandamus commented at 8:45 PM on August 21, 2026: member

    Thanks for the thorough work, this proposal was an excellent read and looks already very mature.

    I’m assigning BIP332 to this proposal. Please add a README entry, incorporate the BIP number into the preamble and set the Assigned header to 2026-08-21.

  36. murchandamus renamed this:
    BIP Draft: Stale Tip Relay
    BIP332: Stale Tip Relay
    on Aug 21, 2026
  37. Add BIP-332: Stale Tip Relay d0f608018a
  38. pseudoramdom force-pushed on Aug 21, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-08-23 23:10 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me