Commit 753832b
committed
dash(embedded): E2d daemonless MN-set seed — pinned checkpoint + forward replay (#738)
c2pool could not mine DASH without an external dashd because it could not
obtain the payout-bearing masternode set on its own. The P2P Simplified MN
List omits scriptPayout and nLastPaidHeight, and neither is committed in
merkleRootMNList, so no payout-bearing set can be header-authenticated. The
only cold-start source was dashd RPC `protx list valid true` (E2c); the no-RPC
arm printed "seed UNAVAILABLE" and every template kept routing to the dashd
fallback. That was the last structurally daemon-dependent input on the
daemonless path.
This lands the settled design: a release-pinned masternode-set checkpoint
compiled into the binary, replayed forward to the tip through the existing
block-connect ingest. The fully trustless DIP-3-height replay (~1.5M block
bodies) stays available as a later opt-in verify-mode and is NOT built here.
THE COST IS A TRUST ANCHOR, AND IT IS DOCUMENTED WHERE USERS WILL SEE IT.
A node cold-starting from the checkpoint trusts the release build for the set
contents at the anchor height. That is stated in README ("DASH daemonless
masternode-set checkpoint — trust anchor"), in KNOWN_ISSUES, in CHANGELOG, in
src/impl/dash/coin/checkpoints/README.md, and at runtime on every load — not
only in a source comment.
What the node still verifies for itself, with no trust:
* chain position — the anchor's blockhash must match our own X11-PoW + DGW
validated header chain at the anchor height;
* integrity — a SHA-256 digest over every non-comment line (an integrity
check on the file, NOT a signature; it catches accident, not malice);
* forward consistency — every replayed block re-derives the projected payee
and compares it with that block's real coinbase, so a wrong anchor is
falsified within a few blocks.
FAIL CLOSED, LOUDLY. Missing, unpinned, corrupt, wrong-network, wrong chain
position, staler than --embedded-mn-bridge-max (default 20000 blocks, ~34
days), a replay gap, or a replay payee-desync: log at ERROR, refuse to serve
embedded templates, keep routing to the dashd fallback. A wrong payee is a
coinbase the network rejects — a lost block. Refusing is always cheaper.
Design note — why the bridge runs on a PRIVATE MnStateMachine rather than
publishing the anchor and letting the maintainer fold forward: apply_block is
forward-contiguous, and during a cold start the live feed is simultaneously
delivering blocks at the CURRENT tip. Publishing at the anchor height would
arm MN-readiness against a queue thousands of blocks stale, then trip
gap_detected on the first live block — a served bad-cb-payee window followed by
a latched-dead arm. The lane instead folds only the exact next height, ignores
live tip blocks mid-bridge, and publishes ONCE, at the tip, through the same
leg-4 event the RPC seed uses.
Also closes an anti-mint hole that only becomes reachable now that the arm can
run without an RPC: with no seed the payee set starts empty and the apply
cursor is 0, so the first block carrying a ProRegTx would register one
masternode, flip MN-readiness, and serve a template whose entire payment queue
was that single accidental registration. CoinStateMaintainer now supports an
opt-in latch requiring an authoritative height-stamped snapshot; main_dash
enables it for the embedded arm. Default off, so no existing construction site
changes behaviour.
Re-pinning is a scripted release step, not manual surgery:
tools/dash/gen_mn_checkpoint.py pin (height-stable protx fetch bracketed by
getblockcount, chain cross-check, deterministic ordering, prints the provenance
block for the release notes) and ... verify PATH for the release checklist.
The shipped mainnet and testnet anchors are UNPINNED in this change — pinning
needs RPC access to a synced dashd, which is a release-time step. Until pinned,
daemonless DASH fails closed, which is the intended safe default.
Lock trace: the lane holds no lock and is io-thread confined. Its pump() calls
self-locking HeaderChain reads and is driven from HeaderChain's on_tip_changed
callback, which is dispatched with m_mutex RELEASED (add_header/add_headers
copy the pending tip change out inside the lock scope, close it, then invoke) —
so the callee is reachable, not dead code. The pre-existing
tip_advance_from_chain(*hc, ...) call in the same lambda already depends on
that property and is proven live.
Tests fold into the EXISTING allowlisted test_dash_node_reception_wire target
(a new add_executable would silently report "Not Run"). No #ifdef guards in the
new TU, so every registered case actually executes.1 parent 1e66d7f commit 753832b
13 files changed
Lines changed: 2353 additions & 16 deletions
File tree
- src
- c2pool
- impl/dash/coin
- checkpoints
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
3 | 33 | | |
4 | 34 | | |
5 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
8 | 46 | | |
9 | 47 | | |
10 | 48 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
154 | 154 | | |
155 | 155 | | |
156 | 156 | | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
157 | 207 | | |
158 | 208 | | |
159 | 209 | | |
| |||
0 commit comments