Require real proof of work in headers and link every advance to the tip (B-74, ELC-11, ELC-12, ELC-22, ELC-23)
A header only had to meet the target it declared for itself, so a fabricated one with `bits = 0x207fffff` passed half the time, a jump of several blocks was accepted without being linked to the tip (a fake header at a huge height locked `tip_height` and froze every draw), and an ordinary one-block reorganisation closed the session on every server. - draw.py: MAINNET_POW_LIMIT (00000000ffff...ffff); header_meets_its_own_target also refuses a claimed target easier than POW_LIMIT. - ElectrumListener._apply_header is async and takes the active client. A single block must chain by prev_hash; a jump downloads the headers in between (blockchain.block.headers, at most 2016 per request, ElectrumClient.get_block_headers) and each must carry valid work and chain, otherwise HeaderValidationError ends the session. - The listener keeps the last 10 headers (`_window`), built from the process's first header and its 9 predecessors (which must chain into it). A header that does not join the tip is walked back to its fork point inside the window and the server's branch is followed instead of ending the session; a fork older than the window, or a branch that does not end at the announced header, still ends it. The audit row and the pause for deep reorgs are B-85; a competing header at the tip's own height is still ignored until then. - _header_at also serves older heights from the window. Tests changed because the spec changed: the mainnet powLimit cannot be mined in a test, so tests/unit/conftest.py swaps in a regtest limit at import time (the new tests put the real one back); the _apply_header tests are async; the two _run_once tests serve the first header's 9 predecessors; test_apply_header_skips_linkage_check_across_a_height_gap is replaced by the jump tests, since that behaviour is exactly what the finding removed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a3c51719bd
commit
ca50ef62f3
16 files changed
+494
-159
No files matched your search
@@ -124,19 +124,6 @@ quorum to agree (on disagreement wait and re-ask for the same height). Seed =
|
||||
mod n. Reveal `server_seed` once the winner is persisted, and expose the full proof
|
||||
at `GET /rounds/{id}/proof`.
|
||||
|
||||
### B-74 — Headers are accepted without a minimum-difficulty check, and a jump skips the linkage check
|
||||
|
||||
`header_meets_its_own_target` never compares `bits` against the chain's powLimit, so
|
||||
a header with `bits=0x207fffff` passes with no work. A multi-block jump is not
|
||||
linked to the current tip. A fake header at a huge height locks `tip_height` (it
|
||||
never regresses) and freezes every draw. Conversely an ordinary 1-block reorg kills
|
||||
the session on every server until the next block.
|
||||
|
||||
Fix: reject `bits` easier than powLimit; on any advance of more than one block fetch
|
||||
the intermediate headers (`blockchain.block.headers`) and validate the chain; on a
|
||||
linkage mismatch walk back up to 10 headers to find the fork point and follow the
|
||||
reorg instead of ending the session (reorg handling: B-85).
|
||||
|
||||
### B-79 — A payout normally has no change output, so it cannot be fee-bumped
|
||||
|
||||
The pool normally holds exactly the round's bet outputs, the payout spends them all
|
||||
|
||||
Reference in new issue
Block a user