Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b0d02ed
net: give the header peer a block no other peer has
niteshbalusu11 Oct 20, 2026
5a83f59
net: retire an IBD header candidate whose block fails consensus
niteshbalusu11 Oct 20, 2026
18f00e9
net: keep a failed connect invalid across restart
niteshbalusu11 Oct 20, 2026
06d5961
net: ask each IBD block peer for headers once
niteshbalusu11 Oct 20, 2026
4aaa1e2
net: close the gaps review found in the IBD retire
niteshbalusu11 Oct 20, 2026
c6a8122
net: keep a peer's newest announced chain and settle its split
niteshbalusu11 Oct 20, 2026
8a6eaa2
net: a header peer that shows a block is kept from it only by one tha…
niteshbalusu11 Oct 20, 2026
96c81a0
net: write a failure before the retired walk, and never show a left path
niteshbalusu11 Oct 20, 2026
7e38500
net: save no walk while a failure is not on disk, and let a notfound …
niteshbalusu11 Oct 20, 2026
812f44c
net: probe a peer from the confirmed tip, and rewind onto a fork belo…
niteshbalusu11 Oct 21, 2026
490c446
net: keep an open probe's lane, and keep a rewound peer on the chain …
niteshbalusu11 Oct 21, 2026
a372775
net: refuse only a linked header batch on a rejected block
niteshbalusu11 Oct 21, 2026
552a4b8
net: clear a peer's split once it builds on this chain above it
niteshbalusu11 Oct 21, 2026
07e3124
net: resume a saved walk past stored failures below its base
niteshbalusu11 Oct 21, 2026
39ec677
net: wait on a block whose time is too far ahead instead of halting IBD
niteshbalusu11 Oct 21, 2026
8bebe6d
net: ask an IBD block peer only for blocks its chain showed
niteshbalusu11 Oct 21, 2026
7be776a
net: re-probe a peer whose record a rewind left stale
niteshbalusu11 Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions COMPAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ Full `/tx/:txid` JSON still has `vin[]`. Electrum has no outspend-vin surface.
| IBD empty-headers EOF | Empty `headers` to **our locator** + idle path latches `headers_done` even if a peer advertises a taller less-work height / junk `version.start_height`. A proven header walk at the tip that every taller peer failed to extend also ends IBD | Core header sync follows most-work; advertised `start_height` is not a remaining header count |
| BIP94 | Not implemented. There is no testnet4 chain and no `-test=bip94`. A retarget multiplies the parent, and a difficulty-boundary timestamp only has to be after the median of ancestor timestamps. This does not fork mainnet, testnet3, signet, or default regtest | On testnet4, and on regtest only with `-test=bip94`, a retarget multiplies the first block of the period (that parent may be a min-difficulty exception) and a difficulty-boundary timestamp more than 600 seconds before its parent is `time-timewarp-attack` |
| Milestone script skip | A height-only `--milestone HEIGHT` skips scripts at and below that height with no anchor hash, including when the header lookup is empty. The default anchor skips once two height-to-hash lookups match: this block, and the anchor hash at the anchor height. It does not walk parents, does not read `min_work_be`, and does not require two weeks of burial. Prevouts, double spends, maturity, and fees still run. An output whose script starts with `OP_RETURN`, or is longer than 10,000 bytes, is still not a coin under the skip | Core runs scripts unless the assumevalid block is an ancestor, the best header is on that chain, chain work meets `nMinimumChainWork`, and the proof-equivalent time from the assumevalid block to the best header is more than two weeks |
| Invalid marks across restart | A block that failed connect on a consensus rule stays invalid across restart (`store/block.invalid`, the newest 4096). An operator `invalidateblock` does not: it lasts until the process exits | Both live in the block index (`BLOCK_FAILED_VALID`) and survive restart |
| Equal-work fork choice | A connected tip is replaced only by strictly greater chain work, or by `preciousblock` at equal work. After `invalidateblock`, equal work prefers precious, then the earlier held tip. Header ranking before either chain is connected also stays with the chain already chosen when work is equal ([`docs/architecture.md`](./docs/architecture.md) layer 1, “equal work keeps the first seen”). A block that was stored earlier but was not yet connectable does not take the tip later | When work is equal, Core prefers the block seen first (`nSequenceId`). A block stored earlier but not yet connectable becomes the tip once it can be connected, even if a later equal-work block is already the tip. Neither rule makes a block valid on one side and invalid on the other |

Compact reconstruct fills short-ids from the live mempool graph, the
Expand Down
15 changes: 14 additions & 1 deletion SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ itself changed.
meta # store magic + schema version
header.body / header.head # Class A headers + hash index (overflow: header.head.gN)
header.adopt # IBD checkpoint list + milestone hash (not a header-chain copy)
block.invalid # blocks that failed connect, kept across restart
txout.body / create.loc / create.off / create.loc.ovf # Class A outs (hot loc)
seqsigwit.body / seqsigwit.loc / seqsigwit.off / seqsigwit.loc.ovf # Class A inputs+witness (cold loc)
seqsigwit.prune # optional: u32 LE pruneheight sidecar (`--prune-seqsigwit`; missing = off)
Expand Down Expand Up @@ -392,7 +393,19 @@ then milestone hash [u8; 32] (zeros if none)

Each checkpoint is 285 bytes. The tail is 364 bytes. Length is exact: `12 + count × 285 + 364`. An older or shorter file does not parse, and the script skip stays off.

Each checkpoint is the hash, height, and total work at the end of one look-ahead reply past the download queue, plus that reply's last header, up to 11 timestamps ending there, and the difficulty period after that header. `ntimes` is how many of those 11 slots are live, oldest first. The milestone hash is the header at the anchored milestone height when the walk passed it. The base is the stored header the checkpoints were built on. On restart, if that height already has a different hash, the file is ignored. A reorg below the base deletes the file. The per-checkpoint header, timestamps, and period-start header let the next look-ahead, a fork from that hash, or a rewind onto it check `nBits` and median time without a row in `header.body`. Full-difficulty height and bits are the last header in the period whose `nBits` are not the minimum-difficulty limit, so a restart can walk testnet difficulty back. The same pair is stored again for the base, for when every checkpoint has been rewound. The tail's period fields are the walk tip. Headers between the queue and that tip are not in this file.
Each checkpoint is the hash, height, and total work at the end of one look-ahead reply past the download queue, plus that reply's last header, up to 11 timestamps ending there, and the difficulty period after that header. `ntimes` is how many of those 11 slots are live, oldest first. The milestone hash is the header at the anchored milestone height when the walk passed it. The base is the stored header the checkpoints were built on. On restart, if that height already has a different hash, the file is ignored. A reorg below the base deletes the file. While `block.invalid` lists a block above the saved base, or one whose height is not found within 64 headers of the best chain, a saved candidate above the confirmed tip is not restored: it may build on that block, so the walk starts again from the tip. A listed block at or below the base is on no chain that walk can take. The per-checkpoint header, timestamps, and period-start header let the next look-ahead, a fork from that hash, or a rewind onto it check `nBits` and median time without a row in `header.body`. Full-difficulty height and bits are the last header in the period whose `nBits` are not the minimum-difficulty limit, so a restart can walk testnet difficulty back. The same pair is stored again for the base, for when every checkpoint has been rewound. The tail's period fields are the walk tip. Headers between the queue and that tip are not in this file.

### `block.invalid`

Sidecar of blocks that failed connect on a consensus rule, so a restart does not adopt a stored header chain through them again. Not a schema bump: missing is none. A file that does not parse is none and is replaced at the next mark. The whole file is written again (synced temp file, rename, directory sync) for each new failure and when `reconsiderblock` clears one. An IBD failure is written here before the retired walk goes to `header.adopt`. While a failure is not on disk, no walk is saved to `header.adopt` and an existing one is removed, and every later mark tries the write again. A restart while the write still fails has no record of that block and validates it again.

```text
0..8 magic b"rbtcinv1"
8..12 u32 LE count (at most 4096)
repeat block hash [u8; 32]
```

Length is exact: `12 + count × 32`. Oldest first. Past 4096 the oldest hash gives way, so the newest failure survives restart. Mutated bodies, a header timestamp too far in the future, store and engine faults, and operator `invalidateblock` are not written. A descendant is invalid through its ancestor: the startup header scan and the IBD resume seed do not walk through a listed hash.

---

Expand Down
Loading
Loading