Skip to content

Commit c7fbc99

Browse files
authored
Merge pull request #418 from DigiByte-Core/release/v9.26.4
v9.26.4: DigiDollar-compatible pruning — run pruned nodes with DigiDollar
2 parents b5ece42 + 5bcd3a8 commit c7fbc99

47 files changed

Lines changed: 3228 additions & 43 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DIGIDOLLAR_ARCHITECTURE.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,9 @@ DigiDollar implements true network-wide tracking by scanning the **entire blockc
711711
void SystemHealthMonitor::ScanUTXOSet(CCoinsView* view,
712712
CCoinsView* validation_view,
713713
const node::BlockManager* blockman,
714-
const CTxMemPool* mempool)
714+
const CTxMemPool* mempool,
715+
const CChain* chain,
716+
const Consensus::Params* consensus)
715717
{
716718
// Create cursor to iterate ALL UTXOs (similar to gettxoutsetinfo)
717719
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
@@ -724,8 +726,14 @@ void SystemHealthMonitor::ScanUTXOSet(CCoinsView* view,
724726
// Find DigiDollar vault outputs (P2TR with value > 0 at output 0)
725727
if (key.n == 0 && coin.out.scriptPubKey[0] == OP_1 && coin.out.nValue > 0) {
726728

727-
// Fetch FULL transaction from block storage
728-
CTransactionRef tx = node::GetTransaction(nullptr, mempool, txid,
729+
// v9.26.4: coins created below the DigiDollar activation floor can
730+
// never be DD vaults — skip them (their blocks may be pruned away)
731+
732+
// Fetch FULL transaction: txindex when available, otherwise read it
733+
// straight from the retained block at the coin's height (works on
734+
// pruned nodes, which keep every block at/above the DD floor)
735+
const CBlockIndex* creating_block = chain ? (*chain)[coin.nHeight] : nullptr;
736+
CTransactionRef tx = node::GetTransaction(creating_block, mempool, txid,
729737
hashBlock, *blockman);
730738

731739
// Validate DD mint structure:

V9.26.4_MAINNET_VALIDATION.md

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# v9.26.4 Mainnet Prune Validation — Live Test Record
2+
3+
Date: 2026-07-02 (UTC) · Binary: `DigiByte version v9.26.4 (release build)` from
4+
branch `release/v9.26.4` · Host: Jared's workstation, live mainnet P2P.
5+
6+
## Purpose
7+
8+
Prove on **real mainnet data** — not just regtest — that a v9.26.4 node with
9+
`prune=N` and **no txindex**:
10+
11+
1. boots, registers the DigiDollar prune lock at the activation floor
12+
(23,627,520), and prunes the ~12-year pre-DigiDollar history;
13+
2. keeps validating and following the live chain while pruned;
14+
3. can never prune a DigiDollar-era block, even when explicitly asked to;
15+
4. reports correct DigiDollar deployment state pre-activation.
16+
17+
Context: mainnet tip was ~23,774,300 during the test — already **past** the
18+
minimum activation height (23,627,520) while DigiDollar BIP9 is still
19+
signaling (`status: started`). So the retained DigiDollar-era window is live
20+
and real: the lock protects blocks that exist today.
21+
22+
## Setup (repeatable)
23+
24+
```bash
25+
# 1. Cleanly stop the primary node (Qt has no mainnet RPC in this config):
26+
kill -TERM <qt-pid> # wait for "Shutdown: done" in ~/.digibyte/debug.log
27+
28+
# 2. Copy chain data to a disposable test datadir (~39 GB, a few minutes).
29+
# The untouched ~/.digibyte is the pristine backup — re-copy to reset the
30+
# test bed for reindex/rescan experiments at any time.
31+
mkdir -p ~/.digibyte-prunetest
32+
cp -a ~/.digibyte/blocks ~/.digibyte-prunetest/blocks
33+
cp -a ~/.digibyte/chainstate ~/.digibyte-prunetest/chainstate
34+
# deliberately NOT copied: indexes/ (txindex + DD stats index — the pruned
35+
# node must not need them) and wallets/ (test runs walletless).
36+
37+
# 3. Test-node config (~/.digibyte-prunetest/digibyte.conf) — distinct ports
38+
# so it can coexist with the primary node:
39+
# prune=2000
40+
# server=1
41+
# rpcport=14022
42+
# port=12124
43+
# rpcbind=127.0.0.1
44+
# rpcallowip=127.0.0.1
45+
# dbcache=1024
46+
# # no txindex line on purpose: prune must auto-disable the default txindex
47+
48+
# 4. Launch the release Qt against it:
49+
DISPLAY=:1 src/qt/digibyte-qt -datadir=$HOME/.digibyte-prunetest -splash=0
50+
51+
# 5. Query it:
52+
src/digibyte-cli -datadir=$HOME/.digibyte-prunetest -rpcport=14022 <cmd>
53+
```
54+
55+
## Observed results
56+
57+
### Startup (debug.log)
58+
59+
```
60+
DigiByte version v9.26.4 (release build)
61+
InitParameterInteraction: parameter interaction: -prune set -> setting -txindex=0
62+
InitParameterInteraction: parameter interaction: -prune set -> setting -digidollarstatsindex=0
63+
Prune configured to target 2000 MiB on disk for block and undo files.
64+
DigiDollar: pruning enabled; retaining all blocks at/above height 23627520 (DigiDollar activation floor)
65+
Oracle: DigiDollar not yet active (BIP9) at height 23774290, skipping price loading
66+
```
67+
68+
All four wiring points fired on mainnet: txindex softset off, DD stats index
69+
softset off, prune lock registered at the activation floor, and the
70+
BIP9-gated oracle startup skip. Startup includes one long single-core step
71+
(~8 minutes) after "Pruning blockstore…": the DigiDollar health seeding scan
72+
over the ~24M-entry mainnet UTXO set (present since v9.26.3; not
73+
prune-specific).
74+
75+
### Pruned steady state
76+
77+
| Check | Result |
78+
|---|---|
79+
| `getblockchaininfo.pruned` | `true` |
80+
| Disk (`size_on_disk`) after initial prune | **2.07 GB** (from 38 GB blocks + 4.2 GB indexes) |
81+
| First `pruneheight` | 21,195,663 — below the floor, because the 2000 MiB size target was satisfied before the lock had to bind |
82+
| Chain following | tip advanced live with the network while pruned (23,774,274 → 23,774,383+) |
83+
| `getnetworkinfo.localservicesnames` | `['WITNESS', 'NETWORK_LIMITED']` — no `NODE_NETWORK`, correct for a pruned node |
84+
| `getindexinfo` | `{}` — no txindex, nothing else |
85+
| `getdigidollardeploymentinfo` | `status: started`, bit 23, `min_activation_height: 23627520`, live signaling tally (4,130 of 28,224 threshold at test time), MuSig2 session telemetry present |
86+
87+
### The DigiDollar prune lock, proven on mainnet
88+
89+
```
90+
$ digibyte-cli pruneblockchain 23774000 # deliberately above the floor
91+
23511221 # clamped — refused to cross the lock
92+
```
93+
94+
- Requested prune height 23,774,000 (near tip, far **above** the floor).
95+
- Actual prune stopped at **23,511,221** — the last whole block-file boundary
96+
below the lock (`floor − PRUNE_LOCK_BUFFER − 1` = 23,627,509; the block file
97+
containing the floor spans ~116k blocks and is retained whole).
98+
- Floor block 23,627,520: **still readable** (`getblock` succeeds).
99+
- Block 23,000,000 (below the floor): pruned, `getblock` errors cleanly.
100+
- Disk after the aggressive prune: **0.21 GB**.
101+
102+
This is the release guarantee in one command: a pruned v9.26.4 node **cannot**
103+
be talked into deleting a DigiDollar-era block — automatic pruning and the
104+
`pruneblockchain` RPC both clamp below the activation floor.
105+
106+
### getdigidollarstats on a pruned mainnet node
107+
108+
Pre-activation it returns instantly with `DigiDollar is not yet active on
109+
this blockchain` — the standard activation gate at the top of every
110+
DigiDollar RPC. The no-index UTXO-scan fallback (and its "don't poll it
111+
tightly" caveat from the release notes) only comes into play after
112+
activation.
113+
114+
### First-prune cost, and restart with m_have_pruned=true
115+
116+
- The **first** prune of a 23.7M-block chain took ~8 minutes of one core
117+
between "Pruning blockstore…" and "Done loading": pruning ~21 million
118+
blocks marks each one's block-index entry as data-less and flushes that to
119+
LevelDB once. (The primary node's unpruned startups show no such gap, and
120+
neither does the pruned node afterward.)
121+
- **Restart after pruning**: total boot ~112 s (block-index load, same as an
122+
unpruned node), "Pruning blockstore… → Done loading" in the same second.
123+
The data-availability guard (`CheckBlockDataAvailability` from tip down to
124+
the floor, `m_have_pruned=true`) ran against the pruned datadir, found the
125+
DigiDollar-era window intact, and booted normally with the prune lock
126+
re-registered. (The refuse-to-start path for a *damaged* window is pinned
127+
by `feature_digidollar_pruning.py` F9 on regtest — it cannot be triggered
128+
with a v9.26.4 binary because the lock prevents creating the damage.)
129+
130+
## Reset / reindex / rescan procedure
131+
132+
- **Reset the test bed** (fresh un-pruned copy): stop test Qt, then
133+
`rm -rf ~/.digibyte-prunetest/{blocks,chainstate}` and re-copy from
134+
`~/.digibyte` (the pristine backup). ~3 minutes.
135+
- **Reindex test**: with the copy in place, start with `-reindex` to rebuild
136+
the chainstate from local block files. On a *pruned* copy, `-reindex`
137+
re-downloads missing history from the network and re-prunes (this is the
138+
recovery path the startup guard prescribes; exercised end-to-end in
139+
`feature_digidollar_pruning.py` F9 on regtest).
140+
- **Rescan test**: wallet rescans work within the retained window and fail
141+
cleanly with "Can't rescan beyond pruned data" below it (pinned in
142+
`feature_digidollar_pruning.py` F6).
143+
- The primary node's datadir `~/.digibyte` was never modified by this test.
144+
145+
## Re-validation on the tagged binary (consensus fix included)
146+
147+
Re-run on the recompiled `v9.26.4` tag (`c3c785443f`, which adds the redeem
148+
collateral floor gate) against a fresh 42 GB copy of `~/.digibyte`
149+
(`~/.dgb-mainprune`, `prune=2000`, no txindex):
150+
151+
- **Boot:** both softsets fired; `DigiDollar: pruning enabled; retaining all
152+
blocks at/above height 23627520`; DigiDollar status `started` (mainnet not yet
153+
active); `pruned=true`; services `['WITNESS','NETWORK_LIMITED']`; `getindexinfo`
154+
empty. Initial prune to the 2000 MiB target: `size_on_disk` 1.77 GB.
155+
- **Clamp:** `pruneblockchain(23778861)` returned **23,511,221** (below the
156+
floor), floor block 23,627,520 remained readable, block 23,000,000 was gone
157+
(`error -1`), and `size_on_disk` fell to **209,887,284 bytes (0.21 GB)**.
158+
- **Restart:** the data-availability guard passed, `pruned=true`, the floor block
159+
was still available, and the node resumed syncing the live chain.
160+
161+
Identical to the original run — the consensus change does not affect mainnet
162+
(DigiDollar is not active yet, so no redeem path runs) and does not change the
163+
prune/lock/guard mechanics.
164+
165+
## Verdict
166+
167+
Every mainnet observation — on both the pre-fix and the tagged binary — matched
168+
the regtest functional suite (`feature_digidollar_pruning.py`, 15 phases), the
169+
live testnet26 battery, and the code review. The pool config in the release
170+
notes (`prune=2000`, no txindex) runs today on mainnet, keeps the full
171+
DigiDollar-era window, and will carry a pool through DigiDollar activation
172+
without a further upgrade.

V9.26.4_PRUNING_EXPLAINER.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# DigiByte v9.26.4 — DigiDollar-Compatible Pruning: how it works, and how we proved it 🍃
2+
3+
*— DigiSwarm, DGB AI dev team*
4+
5+
**The problem.** Since v9.26.3, a DigiDollar node needed `txindex=1` and the full
6+
12-year block history (~38 GB blocks + ~4 GB index, growing). Pools said that's too
7+
heavy. `-prune` was impossible: prune and txindex are mutually exclusive, and
8+
DigiDollar validation used the txindex to resolve the amount/lock-term of spent DD
9+
outputs.
10+
11+
**The insight.** A DigiDollar output can only be *created* once DigiDollar activates,
12+
and activation cannot happen below the deployment's minimum activation height: block
13+
**23,627,520**. So every block DigiDollar validation will *ever* need lives between
14+
that height and the tip. Everything below is plain DGB history — safe to delete.
15+
16+
**How v9.26.4 does it** (one narrowly scoped consensus rule — the redeem collateral
17+
floor gate that keeps pruned and full nodes in agreement — everything else opt-in
18+
behind `-prune`):
19+
20+
1. **Prune lock at the floor.** Startup registers a "digidollar" prune lock at
21+
23,627,520. Automatic pruning *and* the `pruneblockchain` RPC are clamped below
22+
it — a pruned node physically cannot delete a DigiDollar-era block.
23+
2. **No txindex needed.** DD lookups read the creating transaction straight from the
24+
retained block at the coin's height. Every lookup path fails *closed* — reject,
25+
never accept.
26+
3. **Startup guard.** A pruned datadir missing a DD-era block refuses to start and
27+
asks for `-reindex`, rather than validating with incomplete data.
28+
29+
Nodes that don't set `prune=` behave exactly like v9.26.3 — txindex still defaults
30+
ON, and `-prune` + `-txindex=1` still errors.
31+
32+
**Testing (TDD).** 3,404 unit tests green; full 380-test functional suite green,
33+
including the Groestl algolock boundary (accept at 600 / reject at 601, reindex-safe)
34+
and every DigiDollar activation test. New 14-phase `feature_digidollar_pruning.py`:
35+
36+
- a pruned node crosses BIP9 activation, **mines a DD block a full node accepts**,
37+
runs mint → send → redeem, prunes, and restarts to identical stats;
38+
- the prune lock is proven to be the *binding* constraint (not the generic
39+
keep-the-last-288 window);
40+
- a fresh pruned node cold-syncs the entire DD-era chain over P2P;
41+
- a full node migrates in place to pruned (the exact pool path);
42+
- reorgs across DD blocks on the pruned node;
43+
- a deliberately damaged datadir triggers the guard, then recovers with `-reindex`.
44+
45+
**The real-world mainnet test.** We copied a synced mainnet datadir and ran the
46+
release v9.26.4 Qt on **live mainnet** with `prune=2000` and no txindex:
47+
48+
- Log: `DigiDollar: pruning enabled; retaining all blocks at/above height 23627520`
49+
while DD is still *signaling* (bit 23, `started`, ~14.6%). Mainnet's tip
50+
(~23,774,400) is already past the floor, so the retained DD window is live
51+
**today**.
52+
- Disk fell **38 GB → 2.1 GB** at the prune target. We then deliberately ran
53+
`pruneblockchain 23774000` (above the floor): it **clamped to 23,511,221**, the
54+
floor block stayed readable, everything older was cleanly gone — final footprint
55+
**0.21 GB**.
56+
- The node kept validating live mainnet blocks while pruned, advertised
57+
`NODE_NETWORK_LIMITED`, and a restart passed the data-availability guard and booted
58+
normally in ~2 min (the first prune only has a one-time ~8-min step marking 21M old
59+
block-index entries).
60+
61+
Full test record: `V9.26.4_MAINNET_VALIDATION.md`. Design + test plan:
62+
`V9.26.4_PRUNING_PLAN.md`.
63+
64+
**For pools — upgrade once. How to set up `digibyte.conf`:**
65+
66+
Existing v9.26.3 node (the pool upgrade path):
67+
68+
1. Shut the node down.
69+
2. Edit `digibyte.conf`:
70+
- **add**: `prune=2000`
71+
- **remove** any `txindex=1` line (prune and txindex are incompatible; with no
72+
txindex line, v9.26.4 auto-disables it under prune)
73+
3. Replace the binaries with v9.26.4 and start. The node prunes **in place** — no
74+
resync, no reindex. The first start spends a few extra minutes (one-time)
75+
marking the old blocks as pruned; confirm in debug.log:
76+
77+
```
78+
DigiDollar: pruning enabled; retaining all blocks at/above height 23627520 (DigiDollar activation floor)
79+
```
80+
81+
Fresh node: same conf — it syncs from the network and prunes as it goes.
82+
83+
Notes:
84+
85+
- `prune=N` is a disk target in **MiB** (minimum 550); 2000 gives comfortable
86+
headroom. `prune=1` selects manual mode (pruning only via the `pruneblockchain`
87+
RPC).
88+
- Wallets and `getblocktemplate` mining work normally. Don't set `txindex=1` or
89+
`-reindex-chainstate` together with prune (both error, by design).
90+
- To go back to an archival node later: remove `prune=`, set `txindex=1`, restart
91+
with `-reindex` (the node re-downloads the full chain).
92+
93+
That node validates today, automatically keeps every DigiDollar-era block, and
94+
carries you through DigiDollar activation with no further upgrade.
95+
96+
*Fine print: pruned nodes don't serve deep history to new peers (archival nodes
97+
still do that); `getrawtransaction` for pre-DD history needs an archival node; and
98+
after activation the retained DD-era window grows with the chain — `prune=N` deletes
99+
the 12 years of history, it isn't a permanent size cap.*

0 commit comments

Comments
 (0)