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
d5bc68c
DigiDollar-compatible pruning: run pruned nodes with DigiDollar
JaredTate Jul 2, 2026
3376d03
build: bump version to v9.26.4
JaredTate Jul 1, 2026
ddb4aaa
qt: update v9.26.4 DigiDollar wallet image
JaredTate Jul 2, 2026
0bf321d
test: add missing executable bit on 21 functional test scripts
JaredTate Jul 2, 2026
a9e46d3
doc: add v9.26.4 pruning explainer and mainnet validation record
JaredTate Jul 2, 2026
f1a5fa0
doc: add digibyte.conf pruning setup steps to the v9.26.4 explainer
JaredTate Jul 2, 2026
51920fd
doc: add v9.26.4 PR explainer (summary, testing guide, testnet checkl…
JaredTate Jul 2, 2026
0c5453d
test: add offline-miner reorg (F12) and un-prune guard (F13) pruning …
JaredTate Jul 2, 2026
30a1658
pruning: fail closed on unreadable DigiDollar-era block data at startup
JaredTate Jul 2, 2026
eec8423
pruning: make LoadPricesFromChain floor-aware (fix assumeutxo/pre-floor)
JaredTate Jul 2, 2026
f2cefff
fuzz: add DigiDollar pruning fuzz targets (block-db extraction, floor…
JaredTate Jul 2, 2026
4464ced
test: F15 — a pruned node can run a DigiDollar oracle
JaredTate Jul 2, 2026
c3c7854
consensus: gate redeem collateral classification on the activation floor
JaredTate Jul 2, 2026
da958b6
doc: v9.26.4 release notes + live mainnet/testnet prune validation re…
JaredTate Jul 2, 2026
596bd1f
review: disclose the redeem collateral floor gate, unify the activati…
gto90 Jul 2, 2026
73b5779
Revert "review: disclose the redeem collateral floor gate, unify the …
gto90 Jul 2, 2026
5bcd3a8
review: disclose the redeem collateral floor gate, unify the activati…
gto90 Jul 2, 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
14 changes: 11 additions & 3 deletions DIGIDOLLAR_ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,9 @@ DigiDollar implements true network-wide tracking by scanning the **entire blockc
void SystemHealthMonitor::ScanUTXOSet(CCoinsView* view,
CCoinsView* validation_view,
const node::BlockManager* blockman,
const CTxMemPool* mempool)
const CTxMemPool* mempool,
const CChain* chain,
const Consensus::Params* consensus)
{
// Create cursor to iterate ALL UTXOs (similar to gettxoutsetinfo)
std::unique_ptr<CCoinsViewCursor> pcursor(view->Cursor());
Expand All @@ -724,8 +726,14 @@ void SystemHealthMonitor::ScanUTXOSet(CCoinsView* view,
// Find DigiDollar vault outputs (P2TR with value > 0 at output 0)
if (key.n == 0 && coin.out.scriptPubKey[0] == OP_1 && coin.out.nValue > 0) {

// Fetch FULL transaction from block storage
CTransactionRef tx = node::GetTransaction(nullptr, mempool, txid,
// v9.26.4: coins created below the DigiDollar activation floor can
// never be DD vaults — skip them (their blocks may be pruned away)

// Fetch FULL transaction: txindex when available, otherwise read it
// straight from the retained block at the coin's height (works on
// pruned nodes, which keep every block at/above the DD floor)
const CBlockIndex* creating_block = chain ? (*chain)[coin.nHeight] : nullptr;
CTransactionRef tx = node::GetTransaction(creating_block, mempool, txid,
hashBlock, *blockman);

// Validate DD mint structure:
Expand Down
172 changes: 172 additions & 0 deletions V9.26.4_MAINNET_VALIDATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# v9.26.4 Mainnet Prune Validation — Live Test Record

Date: 2026-07-02 (UTC) · Binary: `DigiByte version v9.26.4 (release build)` from
branch `release/v9.26.4` · Host: Jared's workstation, live mainnet P2P.

## Purpose

Prove on **real mainnet data** — not just regtest — that a v9.26.4 node with
`prune=N` and **no txindex**:

1. boots, registers the DigiDollar prune lock at the activation floor
(23,627,520), and prunes the ~12-year pre-DigiDollar history;
2. keeps validating and following the live chain while pruned;
3. can never prune a DigiDollar-era block, even when explicitly asked to;
4. reports correct DigiDollar deployment state pre-activation.

Context: mainnet tip was ~23,774,300 during the test — already **past** the
minimum activation height (23,627,520) while DigiDollar BIP9 is still
signaling (`status: started`). So the retained DigiDollar-era window is live
and real: the lock protects blocks that exist today.

## Setup (repeatable)

```bash
# 1. Cleanly stop the primary node (Qt has no mainnet RPC in this config):
kill -TERM <qt-pid> # wait for "Shutdown: done" in ~/.digibyte/debug.log

# 2. Copy chain data to a disposable test datadir (~39 GB, a few minutes).
# The untouched ~/.digibyte is the pristine backup — re-copy to reset the
# test bed for reindex/rescan experiments at any time.
mkdir -p ~/.digibyte-prunetest
cp -a ~/.digibyte/blocks ~/.digibyte-prunetest/blocks
cp -a ~/.digibyte/chainstate ~/.digibyte-prunetest/chainstate
# deliberately NOT copied: indexes/ (txindex + DD stats index — the pruned
# node must not need them) and wallets/ (test runs walletless).

# 3. Test-node config (~/.digibyte-prunetest/digibyte.conf) — distinct ports
# so it can coexist with the primary node:
# prune=2000
# server=1
# rpcport=14022
# port=12124
# rpcbind=127.0.0.1
# rpcallowip=127.0.0.1
# dbcache=1024
# # no txindex line on purpose: prune must auto-disable the default txindex

# 4. Launch the release Qt against it:
DISPLAY=:1 src/qt/digibyte-qt -datadir=$HOME/.digibyte-prunetest -splash=0

# 5. Query it:
src/digibyte-cli -datadir=$HOME/.digibyte-prunetest -rpcport=14022 <cmd>
```

## Observed results

### Startup (debug.log)

```
DigiByte version v9.26.4 (release build)
InitParameterInteraction: parameter interaction: -prune set -> setting -txindex=0
InitParameterInteraction: parameter interaction: -prune set -> setting -digidollarstatsindex=0
Prune configured to target 2000 MiB on disk for block and undo files.
DigiDollar: pruning enabled; retaining all blocks at/above height 23627520 (DigiDollar activation floor)
Oracle: DigiDollar not yet active (BIP9) at height 23774290, skipping price loading
```

All four wiring points fired on mainnet: txindex softset off, DD stats index
softset off, prune lock registered at the activation floor, and the
BIP9-gated oracle startup skip. Startup includes one long single-core step
(~8 minutes) after "Pruning blockstore…": the DigiDollar health seeding scan
over the ~24M-entry mainnet UTXO set (present since v9.26.3; not
prune-specific).

### Pruned steady state

| Check | Result |
|---|---|
| `getblockchaininfo.pruned` | `true` |
| Disk (`size_on_disk`) after initial prune | **2.07 GB** (from 38 GB blocks + 4.2 GB indexes) |
| First `pruneheight` | 21,195,663 — below the floor, because the 2000 MiB size target was satisfied before the lock had to bind |
| Chain following | tip advanced live with the network while pruned (23,774,274 → 23,774,383+) |
| `getnetworkinfo.localservicesnames` | `['WITNESS', 'NETWORK_LIMITED']` — no `NODE_NETWORK`, correct for a pruned node |
| `getindexinfo` | `{}` — no txindex, nothing else |
| `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 |

### The DigiDollar prune lock, proven on mainnet

```
$ digibyte-cli pruneblockchain 23774000 # deliberately above the floor
23511221 # clamped — refused to cross the lock
```

- Requested prune height 23,774,000 (near tip, far **above** the floor).
- Actual prune stopped at **23,511,221** — the last whole block-file boundary
below the lock (`floor − PRUNE_LOCK_BUFFER − 1` = 23,627,509; the block file
containing the floor spans ~116k blocks and is retained whole).
- Floor block 23,627,520: **still readable** (`getblock` succeeds).
- Block 23,000,000 (below the floor): pruned, `getblock` errors cleanly.
- Disk after the aggressive prune: **0.21 GB**.

This is the release guarantee in one command: a pruned v9.26.4 node **cannot**
be talked into deleting a DigiDollar-era block — automatic pruning and the
`pruneblockchain` RPC both clamp below the activation floor.

### getdigidollarstats on a pruned mainnet node

Pre-activation it returns instantly with `DigiDollar is not yet active on
this blockchain` — the standard activation gate at the top of every
DigiDollar RPC. The no-index UTXO-scan fallback (and its "don't poll it
tightly" caveat from the release notes) only comes into play after
activation.

### First-prune cost, and restart with m_have_pruned=true

- The **first** prune of a 23.7M-block chain took ~8 minutes of one core
between "Pruning blockstore…" and "Done loading": pruning ~21 million
blocks marks each one's block-index entry as data-less and flushes that to
LevelDB once. (The primary node's unpruned startups show no such gap, and
neither does the pruned node afterward.)
- **Restart after pruning**: total boot ~112 s (block-index load, same as an
unpruned node), "Pruning blockstore… → Done loading" in the same second.
The data-availability guard (`CheckBlockDataAvailability` from tip down to
the floor, `m_have_pruned=true`) ran against the pruned datadir, found the
DigiDollar-era window intact, and booted normally with the prune lock
re-registered. (The refuse-to-start path for a *damaged* window is pinned
by `feature_digidollar_pruning.py` F9 on regtest — it cannot be triggered
with a v9.26.4 binary because the lock prevents creating the damage.)

## Reset / reindex / rescan procedure

- **Reset the test bed** (fresh un-pruned copy): stop test Qt, then
`rm -rf ~/.digibyte-prunetest/{blocks,chainstate}` and re-copy from
`~/.digibyte` (the pristine backup). ~3 minutes.
- **Reindex test**: with the copy in place, start with `-reindex` to rebuild
the chainstate from local block files. On a *pruned* copy, `-reindex`
re-downloads missing history from the network and re-prunes (this is the
recovery path the startup guard prescribes; exercised end-to-end in
`feature_digidollar_pruning.py` F9 on regtest).
- **Rescan test**: wallet rescans work within the retained window and fail
cleanly with "Can't rescan beyond pruned data" below it (pinned in
`feature_digidollar_pruning.py` F6).
- The primary node's datadir `~/.digibyte` was never modified by this test.

## Re-validation on the tagged binary (consensus fix included)

Re-run on the recompiled `v9.26.4` tag (`c3c785443f`, which adds the redeem
collateral floor gate) against a fresh 42 GB copy of `~/.digibyte`
(`~/.dgb-mainprune`, `prune=2000`, no txindex):

- **Boot:** both softsets fired; `DigiDollar: pruning enabled; retaining all
blocks at/above height 23627520`; DigiDollar status `started` (mainnet not yet
active); `pruned=true`; services `['WITNESS','NETWORK_LIMITED']`; `getindexinfo`
empty. Initial prune to the 2000 MiB target: `size_on_disk` 1.77 GB.
- **Clamp:** `pruneblockchain(23778861)` returned **23,511,221** (below the
floor), floor block 23,627,520 remained readable, block 23,000,000 was gone
(`error -1`), and `size_on_disk` fell to **209,887,284 bytes (0.21 GB)**.
- **Restart:** the data-availability guard passed, `pruned=true`, the floor block
was still available, and the node resumed syncing the live chain.

Identical to the original run — the consensus change does not affect mainnet
(DigiDollar is not active yet, so no redeem path runs) and does not change the
prune/lock/guard mechanics.

## Verdict

Every mainnet observation — on both the pre-fix and the tagged binary — matched
the regtest functional suite (`feature_digidollar_pruning.py`, 15 phases), the
live testnet26 battery, and the code review. The pool config in the release
notes (`prune=2000`, no txindex) runs today on mainnet, keeps the full
DigiDollar-era window, and will carry a pool through DigiDollar activation
without a further upgrade.
99 changes: 99 additions & 0 deletions V9.26.4_PRUNING_EXPLAINER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# DigiByte v9.26.4 — DigiDollar-Compatible Pruning: how it works, and how we proved it 🍃

*— DigiSwarm, DGB AI dev team*

**The problem.** Since v9.26.3, a DigiDollar node needed `txindex=1` and the full
12-year block history (~38 GB blocks + ~4 GB index, growing). Pools said that's too
heavy. `-prune` was impossible: prune and txindex are mutually exclusive, and
DigiDollar validation used the txindex to resolve the amount/lock-term of spent DD
outputs.

**The insight.** A DigiDollar output can only be *created* once DigiDollar activates,
and activation cannot happen below the deployment's minimum activation height: block
**23,627,520**. So every block DigiDollar validation will *ever* need lives between
that height and the tip. Everything below is plain DGB history — safe to delete.

**How v9.26.4 does it** (one narrowly scoped consensus rule — the redeem collateral
floor gate that keeps pruned and full nodes in agreement — everything else opt-in
behind `-prune`):

1. **Prune lock at the floor.** Startup registers a "digidollar" prune lock at
23,627,520. Automatic pruning *and* the `pruneblockchain` RPC are clamped below
it — a pruned node physically cannot delete a DigiDollar-era block.
2. **No txindex needed.** DD lookups read the creating transaction straight from the
retained block at the coin's height. Every lookup path fails *closed* — reject,
never accept.
3. **Startup guard.** A pruned datadir missing a DD-era block refuses to start and
asks for `-reindex`, rather than validating with incomplete data.

Nodes that don't set `prune=` behave exactly like v9.26.3 — txindex still defaults
ON, and `-prune` + `-txindex=1` still errors.

**Testing (TDD).** 3,404 unit tests green; full 380-test functional suite green,
including the Groestl algolock boundary (accept at 600 / reject at 601, reindex-safe)
and every DigiDollar activation test. New 14-phase `feature_digidollar_pruning.py`:

- a pruned node crosses BIP9 activation, **mines a DD block a full node accepts**,
runs mint → send → redeem, prunes, and restarts to identical stats;
- the prune lock is proven to be the *binding* constraint (not the generic
keep-the-last-288 window);
- a fresh pruned node cold-syncs the entire DD-era chain over P2P;
- a full node migrates in place to pruned (the exact pool path);
- reorgs across DD blocks on the pruned node;
- a deliberately damaged datadir triggers the guard, then recovers with `-reindex`.

**The real-world mainnet test.** We copied a synced mainnet datadir and ran the
release v9.26.4 Qt on **live mainnet** with `prune=2000` and no txindex:

- Log: `DigiDollar: pruning enabled; retaining all blocks at/above height 23627520` —
while DD is still *signaling* (bit 23, `started`, ~14.6%). Mainnet's tip
(~23,774,400) is already past the floor, so the retained DD window is live
**today**.
- Disk fell **38 GB → 2.1 GB** at the prune target. We then deliberately ran
`pruneblockchain 23774000` (above the floor): it **clamped to 23,511,221**, the
floor block stayed readable, everything older was cleanly gone — final footprint
**0.21 GB**.
- The node kept validating live mainnet blocks while pruned, advertised
`NODE_NETWORK_LIMITED`, and a restart passed the data-availability guard and booted
normally in ~2 min (the first prune only has a one-time ~8-min step marking 21M old
block-index entries).

Full test record: `V9.26.4_MAINNET_VALIDATION.md`. Design + test plan:
`V9.26.4_PRUNING_PLAN.md`.

**For pools — upgrade once. How to set up `digibyte.conf`:**

Existing v9.26.3 node (the pool upgrade path):

1. Shut the node down.
2. Edit `digibyte.conf`:
- **add**: `prune=2000`
- **remove** any `txindex=1` line (prune and txindex are incompatible; with no
txindex line, v9.26.4 auto-disables it under prune)
3. Replace the binaries with v9.26.4 and start. The node prunes **in place** — no
resync, no reindex. The first start spends a few extra minutes (one-time)
marking the old blocks as pruned; confirm in debug.log:

```
DigiDollar: pruning enabled; retaining all blocks at/above height 23627520 (DigiDollar activation floor)
```

Fresh node: same conf — it syncs from the network and prunes as it goes.

Notes:

- `prune=N` is a disk target in **MiB** (minimum 550); 2000 gives comfortable
headroom. `prune=1` selects manual mode (pruning only via the `pruneblockchain`
RPC).
- Wallets and `getblocktemplate` mining work normally. Don't set `txindex=1` or
`-reindex-chainstate` together with prune (both error, by design).
- To go back to an archival node later: remove `prune=`, set `txindex=1`, restart
with `-reindex` (the node re-downloads the full chain).

That node validates today, automatically keeps every DigiDollar-era block, and
carries you through DigiDollar activation with no further upgrade.

*Fine print: pruned nodes don't serve deep history to new peers (archival nodes
still do that); `getrawtransaction` for pre-DD history needs an archival node; and
after activation the retained DD-era window grows with the chain — `prune=N` deletes
the 12 years of history, it isn't a permanent size cap.*
Loading
Loading