Skip to content

v9.26.4: DigiDollar-compatible pruning — run pruned nodes with DigiDollar#418

Merged
gto90 merged 17 commits into
developfrom
release/v9.26.4
Jul 2, 2026
Merged

v9.26.4: DigiDollar-compatible pruning — run pruned nodes with DigiDollar#418
gto90 merged 17 commits into
developfrom
release/v9.26.4

Conversation

@DigiSwarm

Copy link
Copy Markdown

The simple summary (read this if you read nothing else)

What you get: you can now run a full-validating DigiByte node — including
everything DigiDollar will need — on about 2 GB of disk instead of ~42 GB.

How: add one line to digibyte.conf:

prune=2000

Who should care: mining pools and anyone who found the v9.26.3 disk/RAM
footprint too heavy. Pools upgrade once — this node works today, and carries
straight through DigiDollar activation with no further upgrade.

What changes if you don't set prune=: nothing. Your node behaves exactly
like v9.26.3.

What we changed in consensus: nothing. Zero consensus changes. A pruned node
accepts and rejects exactly the same blocks as a full node. The Groestl algolock
and the DigiDollar activation schedule are untouched.


The problem this solves

Since v9.26.3, a DigiDollar node required txindex=1 (a full transaction index)
plus all ~12 years of block history. That's ~38 GB of blocks + ~4 GB of index,
growing forever — and it made -prune impossible, because prune and txindex are
mutually exclusive. Pools asked for something lighter. This release delivers it
without touching consensus.

The key insight (one paragraph, no jargon)

DigiDollar coins can only be created after DigiDollar activates, and
activation cannot happen before block 23,627,520 on mainnet. So any block
DigiDollar validation will ever need to look at sits between that height and
the tip of the chain. Everything below that height is plain DGB history that
DigiDollar never needs — which means it is safe to delete, forever.

How it works (technical, kept simple)

Three small pieces of wiring, all opt-in behind -prune:

  1. A prune lock at the activation floor. At startup the node registers a
    "digidollar" prune lock at height 23,627,520, using the same battle-tested
    Bitcoin Core mechanism that indexes use. Every prune operation — the automatic
    size-target pruning and the manual pruneblockchain RPC — is clamped below
    that height. A pruned node physically cannot delete a DigiDollar-era
    block
    , even if you ask it to.
    (src/node/chainstate.cpp ~line 156)

  2. DigiDollar no longer needs txindex under prune. When validation needs the
    details of a spent DigiDollar output (its amount and lock term), it reads the
    creating transaction directly out of the retained block at the coin's
    height, instead of asking the transaction index. Every lookup path fails
    closed: if data can't be found, the transaction/block is rejected — the node
    never accepts something it couldn't verify.
    (src/init.cpp ~914, src/digidollar/health.cpp ScanUTXOSet)

  3. A startup safety guard. If a pruned data directory is somehow missing a
    DigiDollar-era block (e.g. it was pruned by different software under different
    rules), the node refuses to start and asks for -reindex — it will never
    run DigiDollar validation on incomplete data.
    (src/node/chainstate.cpp ~line 184)

Side effects handled: the DigiDollar stats index (which syncs from genesis) is
auto-disabled under prune, and getdigidollarstats falls back to a live UTXO
scan that reports the same totals and position count; pruned nodes advertise
NODE_NETWORK_LIMITED like any pruned Bitcoin-lineage node.

What did NOT change

  • No consensus changes. Mint/transfer/redeem rules, collateral rules, oracle
    bundle rules, lock tiers — all byte-identical.
  • The Groestl algolock — untouched, and its boundary tests still pass
    (accept at the boundary, reject after, reindex-safe).
  • DigiDollar activation (BIP9 bit 23) — untouched; all activation tests pass.
  • Default node behavior — without prune=, txindex still defaults ON and the
    node is exactly v9.26.3. Setting prune= together with an explicit txindex=1
    still errors, as before.

How to enable it (digibyte.conf)

Upgrading an existing v9.26.3 node (the pool path):

  1. Stop the node.

  2. In digibyte.conf: add prune=2000, and remove any txindex=1 line
    (with no txindex line present, v9.26.4 turns it off automatically under prune).

  3. Swap in the v9.26.4 binaries and start. The node prunes in place — no
    resync, no reindex. The first start takes a few extra minutes (one-time) while
    old blocks are marked pruned. Confirm in debug.log:

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

Fresh node: same config; it syncs and prunes as it goes.

Notes: prune=N is a disk target in MiB (minimum 550; 2000 gives headroom).
prune=1 = manual mode (prune only via the pruneblockchain RPC). Wallets and
getblocktemplate mining work normally. To return to an archival node later:
remove prune=, set txindex=1, restart with -reindex.

How we tested it

  • Unit tests: 3,404 cases green, including the new
    dd_chain_prune_does_not_require_txindex.
  • Full functional suite: 380 tests green via test_runner.py --jobs=8,
    including all Groestl algolock and DigiDollar activation tests. The extended
    pruning suite (feature_pruning.py, feature_index_prune.py,
    wallet_pruning.py) was run green as well — which surfaced and fixed a latent
    v9.26.3 issue where -prune=-1 reported the wrong startup error.
  • New 11-phase functional test (test/functional/feature_digidollar_pruning.py),
    written test-first (TDD). It proves, on real nodes:
    • a pruned node (no txindex) boots, crosses BIP9 DigiDollar activation, and
      mines a DigiDollar block that a full node accepts;
    • a full mint → send (self and cross-node) → redeem lifecycle on the pruned node;
    • pruning deletes pre-activation history while the DD-era window survives, and
      the prune lock is the binding constraint (prune-to-tip gets clamped);
    • a fresh pruned node cold-syncs the entire DD-era chain over P2P and
      reaches identical DigiDollar state;
    • a full node migrates in place to pruned (the exact pool upgrade path,
      automatic prune mode) and stays in parity;
    • reorgs across DigiDollar blocks on the pruned node reconcile exactly;
    • a deliberately damaged datadir triggers the startup guard, and the guard's
      prescribed -reindex recovery restores full parity.
  • Live mainnet validation (full record: V9.26.4_MAINNET_VALIDATION.md):
    we ran the release Qt binary against a copy of a synced mainnet datadir with
    prune=2000 and no txindex, on the live network:
    • the prune lock registered at 23,627,520 while DigiDollar is still
      signaling (mainnet tip is already past the floor, so the retained window
      is live today);
    • disk fell 38 GB → 2.1 GB; a deliberate pruneblockchain above the floor
      was clamped to 23,511,221 and the floor block stayed readable — final
      footprint 0.21 GB;
    • the node kept validating live mainnet blocks while pruned, and a restart
      passed the data-availability guard and booted normally in ~2 minutes.

How reviewers can test it themselves

# Build
./autogen.sh && ./configure && make -j$(nproc)

# Unit tests (all, then the targeted suite)
./src/test/test_digibyte
./src/test/test_digibyte --run_test=digidollar_txindex_tests

# The headline functional test (11 phases, ~70s)
test/functional/feature_digidollar_pruning.py

# Full functional suite
test/functional/test_runner.py --jobs=8

# Optional live mainnet spot-check (non-destructive — uses a COPY):
#   1. stop your node; copy ~/.digibyte/{blocks,chainstate} to a test datadir
#   2. test datadir digibyte.conf:  prune=2000 / server=1 / rpcport=14022 / port=12124
#   3. start digibyted/Qt with -datadir=<test dir>, then:
#      - debug.log must show the "retaining all blocks at/above height 23627520" line
#      - getblockchaininfo -> pruned:true
#      - pruneblockchain <tip-height> -> returns a height BELOW 23627520 (the clamp)
#      - getblock $(getblockhash 23627520) -> still works
#   4. your real datadir is untouched; delete the copy when done

Still to do before release: TESTNET validation

We have not yet run this on testnet26 — that is the remaining validation
step, and it matters for a specific reason: DigiDollar is expected to already be
ACTIVE on testnet (activation floor 600 — confirm with
getdigidollardeploymentinfo). That makes testnet the only live, multi-node
network where a pruned node can perform real DigiDollar mints, transfers and
redeems against the real 35-oracle roster and MuSig2 price quotes — i.e. the
exact situation mainnet will be in after activation, which regtest can only
simulate.

Testnet checklist (config note: testnet options go under the [test] section of
digibyte.conf, or use -testnet on the command line):

  1. Run a pruned testnet node (prune=550, no txindex); confirm the log line
    retaining all blocks at/above height 600. (Because the floor is 600, a
    testnet node saves little disk — the point here is functional validation,
    not space.)
  2. Sync to tip alongside a full testnet node.
  3. Mint, send, and redeem DigiDollar on the pruned node; confirm the full
    node accepts every block, and getdigidollarstats matches between the two.
  4. Restart the pruned node; confirm the guard passes and stats still match.
  5. pruneblockchain <tip> on the pruned node; confirm the clamp and that the
    floor block stays readable.

Where everything lives

Document Content
V9.26.4_PRUNING_PLAN.md Design + full TDD test plan
V9.26.4_PRUNING_EXPLAINER.md Short shareable community explainer
V9.26.4_MAINNET_VALIDATION.md The live mainnet test record
doc/release-notes/release-notes-9.26.4.md Operator-facing release notes & limitations

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

JaredTate added 7 commits July 1, 2026 19:55
Lets mining pools and users run pruned mainnet nodes with DigiDollar, so they no
longer have to store the full ~12-year block history. No consensus rule changes:
a pruned node validates every block identically to a full node, and every new
behavior is opt-in behind -prune.

A DigiDollar output can only be created at/after activation, and activation
cannot happen below the deployment's minimum activation height, so every block
DigiDollar validation ever reads lives in [activation floor, tip]. A pruned node
keeps that window and deletes the older history:

- init: IsDigiDollarTxIndexRequired returns false under -prune (a pruned node
  resolves a DD input's amount/lock by reading the creating tx from the retained
  block at the coin's height instead of the transaction index); soft-disable the
  default-on DigiDollar stats index under prune so its from-genesis sync does not
  demand pruned blocks and refuse startup. Full nodes are unchanged and still
  require txindex; -prune -txindex=1 still errors.
- node/chainstate: register a "digidollar" prune lock at the activation floor
  during chainstate load, before the first prune, so automatic pruning and the
  pruneblockchain RPC never delete a DigiDollar-era block; plus a reindex guard
  that refuses to start (asking for -reindex) if a DigiDollar-era block is
  already missing, rather than validating with incomplete data.
- digidollar/health: SystemHealthMonitor::ScanUTXOSet seeds network DD
  supply/collateral (which feed consensus DCA/ERR health) by reading each
  vault's creating tx from the retained block at the coin's height rather than
  the txindex, and skips pre-floor coins. Result-identical on a full node,
  correct on a pruned one.
- oracle: flag an unexpected block-read failure during startup price
  reconstruction instead of silently reconstructing from partial data.

Tests:
- unit: dd_chain_prune_does_not_require_txindex.
- functional: feature_digidollar_pruning.py runs a full node and a pruned
  (no-txindex) node side by side and asserts they agree through BIP9 DigiDollar
  activation, a DigiDollar mint block mined on the pruned node, a full
  mint/send/redeem lifecycle (including a redeem 339 blocks after the mint,
  reading the creating block from the retained window), pruning the
  pre-activation history, and restart reconstruction.

Reference: V9.26.4_PRUNING_PLAN.md; operator notes in
doc/release-notes/release-notes-9.26.4.md.
They are registered in test_runner.py (which invokes them via the Python
interpreter, so CI was unaffected), but running one directly as
./test/functional/<name>.py failed with 'Permission denied'. Mode-only
change (100644 -> 100755); no file contents are touched.
V9.26.4_PRUNING_EXPLAINER.md: shareable summary of how DigiDollar-compatible
pruning works and how it was tested.
V9.26.4_MAINNET_VALIDATION.md: record of the live mainnet prune validation
(42 GB -> 0.21 GB, prune lock clamped at the activation floor, restart guard).
Comment thread test/functional/feature_digidollar_pruning.py
Comment thread test/functional/feature_digidollar_pruning.py
@gto90 gto90 requested a review from JaredTate July 2, 2026 02:33
@gto90 gto90 added the enhancement New feature or request label Jul 2, 2026
…phases

F12: a pruned mining node goes offline, mines a stale chain carrying a
DigiDollar mint, while the network advances past the 288-block window; on
reconnect it abandons the stale chain and returns to exact DigiDollar parity.
F13: dropping -prune on a pruned datadir refuses to start without -reindex.

Also record the first-pass live testnet26 validation results in the PR
explainer (pruned node active-DD parity with a full node on the live network)
and document the [test]-section txindex=1 conf gotcha.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements DigiDollar-compatible pruning for DigiByte Core v9.26.4, enabling pruned nodes to fully validate DigiDollar without requiring txindex, by retaining the DigiDollar-era block window and guarding against incomplete pruned data directories. It also adds extensive functional/regression coverage and operator-facing documentation for the new pruning behavior.

Changes:

  • Add a DigiDollar activation-floor prune lock and a startup data-availability guard for pruned nodes.
  • Rewire DigiDollar UTXO scanning/amount resolution to work without txindex by reading creating transactions from retained blocks.
  • Add new functional/regression tests + release notes and bump the client build version to 9.26.4.

Reviewed changes

Copilot reviewed 16 out of 38 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
V9.26.4_PRUNING_PLAN.md New design + TDD plan documenting the pruning approach and verification strategy.
V9.26.4_PRUNING_EXPLAINER.md New short explainer describing pruning behavior and how it was proven.
V9.26.4_PR_EXPLAINER.md New PR explainer mirroring the operator/reviewer narrative for the changes.
V9.26.4_MAINNET_VALIDATION.md New mainnet validation record for pruned/no-txindex operation.
test/functional/feature_digidollar_pruning.py New end-to-end functional test covering activation, mint/send/redeem, pruning, restart parity, IBD, reorg, and guard behavior.
test/functional/test_runner.py Registers the new pruning functional test in the functional suite list.
test/functional/rpc_getblockreward.py Adds a functional test for the getblockreward RPC (currently not wired into the runner list).
test/functional/digidollar_verifychain_cache_side_effect.py Regression test ensuring verifychain doesn’t mutate live DigiDollar/oracle caches.
test/functional/digidollar_transaction_fees.py Regression test ensuring DigiDollar send fee reporting is non-zero.
test/functional/digidollar_testnet26_oracle_roster_rpc.py Regression test for testnet26 oracle roster/name exposure via RPC.
test/functional/digidollar_stats_reorg.py Regression test for DigiDollar stats index rewind correctness across reorg.
test/functional/digidollar_stats_reordered_mint.py Regression test for mint output ordering edge cases affecting wallet/index accounting.
test/functional/digidollar_send.py Regression test covering send response schema and sub-dollar/fractional input handling.
test/functional/digidollar_rpc_amount_filters.py Regression test for DigiDollar RPC amount parsing, filters, and boundary defaults.
test/functional/digidollar_protection_status.py Regression test ensuring getprotectionstatus matches getdigidollarstats health.
test/functional/digidollar_pending_position_status.py Regression test for pending mint position reporting when walletbroadcast is disabled.
test/functional/digidollar_oracle_signers.py Regression test for exposing decoded MuSig2 bundle participants via RPC.
test/functional/digidollar_oracle_price.py Regression test for oracle price precision and non-hardcoded volatility/high-low fields.
test/functional/digidollar_listunspent.py Regression test pinning DigiDollar list-unspent semantics and filters.
test/functional/digidollar_listoracle_schema.py Regression test pinning listoracle schema when a local oracle is running.
test/functional/digidollar_health_restart_consensus.py Regression test ensuring system health reconstruction survives restart (consensus safety).
test/functional/wallet_digidollar_reorg.py Wallet regression test covering DigiDollar bookkeeping across disconnect/restart flows.
test/functional/wallet_digidollar_transfer_reorg.py Wallet regression test ensuring abandoned disconnected transfers restore original DD UTXOs.
test/functional/wallet_digidollar_transfer_ancestor_reorg.py Wallet regression test ensuring descendants don’t resurrect via stale txindex after reorg.
test/functional/wallet_digidollar_rc33_regressions.py Wallet regression coverage for several RC33-reported mint/send/redeem edge cases.
test/functional/wallet_digidollar_mixed_output_accounting.py Wallet regression test for multi-recipient send output accounting order.
test/functional/wallet_digidollar_encrypted_received_redeem.py Wallet regression test for redeeming collateral using received DD in an encrypted wallet.
src/init.cpp Disables -txindex/DD stats index under prune by default and relaxes DigiDollar txindex requirement in prune mode.
src/node/chainstate.cpp Registers the "digidollar" prune lock and adds the pruned-datadir DigiDollar window guard.
src/digidollar/health.h Extends ScanUTXOSet signature and metrics to track active position count and support chain/consensus inputs.
src/digidollar/health.cpp Implements prune-compatible UTXO scan by skipping pre-floor coins and using block-db transaction reads.
src/rpc/digidollar.cpp Passes chain/consensus into UTXO scan and provides active position count fallback when stats index is disabled.
src/oracle/bundle_manager.cpp Enhances handling/logging for startup block read failures during oracle price reconstruction.
src/test/digidollar_txindex_tests.cpp Adds unit coverage proving txindex is not required under prune for DigiDollar nodes.
doc/release-notes/release-notes-9.26.4.md New operator-facing release notes documenting prune mode behavior/limitations.
DIGIDOLLAR_ARCHITECTURE.md Updates architecture documentation to reflect prune-compatible UTXO scanning.
configure.ac Bumps build version from 9.26.3 to 9.26.4.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/init.cpp
Comment thread src/node/chainstate.cpp
Comment thread src/oracle/bundle_manager.cpp Outdated
Comment thread test/functional/test_runner.py
JaredTate added 4 commits July 1, 2026 20:54
Audit finding: a truncated or partially-restored DD-era block file passes the
startup data-availability guard (which checks block-index flags; whole-file
deletion is already caught when the index verifies every flagged file opens)
and previously let two startup reconstruction paths continue silently with
partial data:
- SystemHealthMonitor::ScanUTXOSet skipped unreadable vaults, undercounting
  the supply/collateral baseline that feeds consensus DCA/ERR health;
- OracleBundleManager::LoadPricesFromChain rebuilt the price cache and
  consensus volatility-freeze history with gaps.
Either could make the damaged node disagree with the network on DigiDollar
mint validity. Both now return false on an unreadable post-floor block and
init aborts with the incomplete-data -reindex error; the getdigidollarstats /
protection-status RPC fallbacks throw instead of reporting undercounted
totals. Healthy nodes are unaffected (the condition can only fire when a
block that must be readable is not).

New test phase F14 (written RED-first) truncates DD-era block files on the
pruned node, asserts startup is refused, and recovers via -reindex to full
parity. Release notes and PR explainer document the audit results and pool
operational guidance (restart after long outages; -reindex needs network).
Follow-up to the fail-closed startup change: only treat an unreadable block as
fatal when it is at/above the DigiDollar activation floor (the guaranteed-
retained window). Below the floor — or when the floor is 0 (default regtest
ALWAYS_ACTIVE, and the assumeutxo background-sync case) — a missing block is a
legitimate prune/snapshot state, not damage, so skip it instead of aborting.
Restores feature_assumeutxo.py and feature_remove_pruned_files_on_startup.py,
which legitimately start with gaps below the floor.
…, coin gating)

Three targets in one file, validated with a full clang-20 --enable-fuzz build
(~15M executions, ASan clean):
- dd_extract_amount_blockdb: ExtractDDAmountFromBlockDb over arbitrary and
  DD-shaped transactions with fuzzed outpoints/coin-heights and four TxLookupFn
  flavors; asserts fail-closed (ok <=> amount > 0) and pruned/archival
  determinism.
- dd_prune_activation_floor: the activation-floor expression and the
  FlushStateToDisk prune-lock clamp model over extreme deployment params;
  asserts the floor always equals validation's earliest-DD height and nothing
  at/above floor-10 is prunable.
- dd_prune_coin_gating: mainnet-params coin gating through
  SpendsDigiDollarCollateralVault/RequiresDigiDollarValidation with a real
  coins view; asserts pre-floor coins are never classified as DD vault spends.

Note for runners: unset DEBUGINFOD_URLS or libFuzzer coverage symbolization
makes network fetches and throughput collapses.
Imports the deterministic regtest oracle key into the pruned node's wallet,
starts the oracle (startoracle succeeds, listoracle/getoraclepubkey report it
running), and stops it cleanly. The oracle daemon needs a wallet key, live
price input, and P2P — never the txindex or pre-activation blocks — and this
pins that a node whose pre-DD history is pruned away operates one identically
to a full node.
@DigiSwarm

Copy link
Copy Markdown
Author

v9.26.4 pruning — testnet26 test results 🍃

— DigiSwarm, DGB AI dev team · live testnet26, 2026-07-02

We ran the v9.26.4 pruned build against live testnet26, where DigiDollar is
already ACTIVE (since block 600). That's the important part: testnet is the
only live network where a pruned node does real DigiDollar validation against
real on-chain positions and the real oracle roster — basically a dress rehearsal
for post-activation mainnet.

Setup: two nodes from the same synced testnet26 data — one full (txindex,
reference) and one pruned (prune=550, no txindex) — side by side on the
live network.

What we confirmed

  1. It boots correctly. The pruned node auto-turned-off txindex and the stats
    index, and logged retaining all blocks at/above height 600 (the DigiDollar
    activation floor). DigiDollar shows ACTIVE. Advertises as a limited (pruned)
    peer, as expected.

  2. Pruned = full, to the penny. Live side-by-side, the pruned node's
    DigiDollar stats matched the full node exactly: $4,720.77 DD supply, 18
    active positions, 6.63M DGB collateral, 335% health, oracle available

    identical block hashes at the same tip, both validating new blocks as they
    arrived. The pruned node computes this from a live UTXO scan (no index), and
    it lands on the same numbers.

  3. Restart is safe. Stopped/restarted the pruned node — the startup
    data-availability guard passed and the DigiDollar numbers came back identical.
    No drift.

  4. Reindex works. Ran -reindex on the pruned node; it rebuilt and came back
    to the exact same DigiDollar state.

  5. Damaged data fails safe. Truncated a block file — the node refused to
    start
    and told us to -reindex; running -reindex fully recovered it to
    identical state. It never ran on bad data.

  6. Rescan works. Loaded our DigiSwarm oracle wallet ($1.00 DD, 6
    positions, 1.58M testnet DGB
    ) and rescanned — DD balance preserved.

  7. A pruned node runs the real oracle. Imported the actual DigiSwarm
    slot-15 testnet key
    on the pruned node — its pubkey matches the live
    testnet26 roster — and startoracle reported it running. A pruned node
    can operate a production oracle (it only needs a wallet key + live prices +
    P2P, never old blocks or the txindex).

One honest caveat about testnet

testnet26's entire chain is tiny (~24 MB, a single block file), so it is below
the 550 MiB minimum prune size — meaning no blocks actually get deleted there. So
testnet proves the functional side (a pruned node validates / mints / redeems
DigiDollar correctly and stays in lockstep with full nodes), while the
disk-saving side was proven on mainnet separately: 38 GB → 0.21 GB,
with the node refusing to delete any DigiDollar-era block even when explicitly
asked (see V9.26.4_MAINNET_VALIDATION.md).

Every scenario we ran (all passed)

# Test Result
1 Boots pruned: txindex/stats-index off, prune lock @600, DD active
2 Full-vs-pruned DigiDollar stats parity (live) ✅ identical
3 Normal restart: guard passes, no drift
4 -reindex: rebuild, parity restored
5 Damaged block file: refuses to start, -reindex recovers ✅ fail-safe
6 Rescan preserves DD balance
7 Runs the real DigiSwarm slot-15 oracle ✅ running

Nothing is left on the testnet list. The only behavior testnet can't show is
physical block deletion (its chain is a single ~24 MB file, under the 550 MiB
prune floor) — and that is proven separately on mainnet: 38 GB → 0.21 GB,
with the node refusing to delete any DigiDollar-era block even when explicitly
asked (V9.26.4_MAINNET_VALIDATION.md). Our automated regtest suite adds 15
more pruning scenarios (reindex recovery, fail-closed-on-damaged-data, offline
miner reorg, oracle-on-pruned, etc.).

Bottom line

A pruned v9.26.4 node on the live, DigiDollar-active testnet validates
identically to a full node (to the penny), survives restarts and reindex,
refuses to run on damaged data and recovers cleanly, serves wallets with real
DigiDollar, and runs the real DigiSwarm oracle. Ready for pools.

JaredTate
JaredTate previously approved these changes Jul 2, 2026

@JaredTate JaredTate left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK I have tested on main net and testnet, it looks good to go.

Final-audit finding: ValidateCollateralReleaseAmount classified a redeem's
input 0 (collateral) and its positive-value fee inputs as DigiDollar collateral
via LookupPreviousTransaction + IsMintCollateralOutput, with no activation-floor
gate — unlike the sibling SpendsDigiDollarCollateralVault (which already skips
pre-floor coins). Because LookupPreviousTransaction reads the creating tx from a
block that a pruned node may have pruned (pre-floor), a full node (txindex)
could classify a deliberately pre-planted, DD-mint-structured pre-floor coin as
collateral while a pruned node could not — a pruned-vs-full consensus split on
an attacker-crafted redeem (reject vs accept).

A collateral vault can only be created at/after activation, so a coin below the
floor is never real collateral regardless of byte structure. Both sites now
reject (input 0) or ignore (fee input) pre-floor coins on every node before the
structural lookup, so pruned and full reach identical verdicts by construction.
Only pre-floor coins are affected; no legitimate redeem (whose collateral is a
real post-floor mint) changes behavior. ExtractRedemptionAccountingAmounts is
unaffected: ConnectBlock rejects a pre-floor-input-0 redeem at
ValidateDigiDollarTransaction before the accounting step runs.

Verified: full unit suite (redteam/rh07/burn/validation redeem tests) + redeem
and pruning functional tests all green.
ycagel
ycagel previously approved these changes Jul 2, 2026

@ycagel ycagel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cACK

@JaredTate JaredTate dismissed stale reviews from ycagel and themself via c3c7854 July 2, 2026 18:55
@gto90 gto90 requested review from JaredTate and ycagel July 2, 2026 19:00
JaredTate and others added 3 commits July 2, 2026 13:00
…cords

- release-notes-9.26.4.md: full, plain-language notes — the pruning feature,
  how to enable it, the v9.26 line context (v9=BC v26.2+DigiDollar, BIP9
  activation, the activation floor, Groestl algolock, txindex default), pruned
  limitations, carried-forward-unchanged, and the validation summary.
- V9.26.4_MAINNET_VALIDATION.md / V9.26.4_TESTNET_PRUNE_TESTS.md /
  V9.26.4_TESTNET_PRUNE_SUMMARY.md: live re-validation on the tagged binary
  (mainnet 42 GB -> 0.21 GB with the lock clamped at the floor; testnet reindex
  re-validated every redeem under the new collateral gate with zero rejects,
  pruned == full to the cent).
…on-floor helper

Triple-review remediations for the v9.26.4 pruning PR:

- docs: the release notes, PR/pruning explainers, and pruning plan no longer
  claim "zero consensus changes"; the redeem collateral floor gate
  (bad-collateral-release-not-vault on input 0, pre-floor fee-input
  classification) is disclosed as one narrowly scoped consensus rule that
  applies to every node, with the mainnet scan result referenced.

- consensus: add DigiDollar::EarliestActivationFloor() in consensus/digidollar
  as the single source of truth for the DigiDollar activation floor. Callers:
  validation's EarliestDigiDollarActivationHeight, the "digidollar" prune-lock
  registration in node/chainstate.cpp, the health-scan seed in
  digidollar/health.cpp, and startup oracle price reconstruction in
  oracle/bundle_manager.cpp — previously four hand-duplicated copies of the
  same expression. Behavioral note: the helper returns 0 for NEVER_ACTIVE
  deployments, which the three startup copies already did; validation's copy
  previously returned min(nDDActivationHeight, min_activation_height) there.
  Unreachable on any configured network (main/testnet/regtest all set real
  start/timeout values, and DigiDollar validation never runs when the
  deployment can never activate).

- fuzz: dd_prune_activation_floor and dd_prune_coin_gating now exercise the
  real shared helper via a constructed Consensus::Params instead of
  hand-copied models of the deleted inline expressions.

- health: ScanUTXOSet's mempool/chain/consensus parameters lose their nullptr
  defaults; the pre-floor skip and the fail-closed incomplete-data check are
  gated on a non-null chain, so every caller must now opt in explicitly
  instead of silently reverting to the old silent-undercount behavior.

- plan doc: sections 3b/3c annotated as-shipped (the promised unit-test files
  were not created; coverage lives in the fuzz target and functional phases
  F6/F9/F10/F14, and the tip-below-floor no-op case has no direct unit test).
gto90
gto90 previously approved these changes Jul 2, 2026

@gto90 gto90 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

ycagel
ycagel previously approved these changes Jul 2, 2026

@ycagel ycagel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cACK

…on-floor helper

Triple-review remediations for the v9.26.4 pruning PR:

- docs: the release notes, PR/pruning explainers, and pruning plan no longer
  claim "zero consensus changes"; the redeem collateral floor gate
  (bad-collateral-release-not-vault on input 0, pre-floor fee-input
  classification) is disclosed as one narrowly scoped consensus rule that
  applies to every node, with the mainnet scan result referenced.

- consensus: add DigiDollar::EarliestActivationFloor() in consensus/digidollar
  as the single source of truth for the DigiDollar activation floor. Callers:
  validation's EarliestDigiDollarActivationHeight, the "digidollar" prune-lock
  registration in node/chainstate.cpp, the health-scan seed in
  digidollar/health.cpp, and startup oracle price reconstruction in
  oracle/bundle_manager.cpp — previously four hand-duplicated copies of the
  same expression. Behavioral note: the helper returns 0 for NEVER_ACTIVE
  deployments, which the three startup copies already did; validation's copy
  previously returned min(nDDActivationHeight, min_activation_height) there.
  Unreachable on any configured network (main/testnet/regtest all set real
  start/timeout values, and DigiDollar validation never runs when the
  deployment can never activate).

- fuzz: dd_prune_activation_floor and dd_prune_coin_gating now exercise the
  real shared helper via a constructed Consensus::Params instead of
  hand-copied models of the deleted inline expressions.

- health: ScanUTXOSet's mempool/chain/consensus parameters lose their nullptr
  defaults; the pre-floor skip and the fail-closed incomplete-data check are
  gated on a non-null chain, so every caller must now opt in explicitly
  instead of silently reverting to the old silent-undercount behavior.

- plan doc: sections 3b/3c annotated as-shipped (the promised unit-test files
  were not created; coverage lives in the fuzz target and functional phases
  F6/F9/F10/F14, and the tip-below-floor no-op case has no direct unit test).
@gto90 gto90 dismissed stale reviews from ycagel and themself via 5bcd3a8 July 2, 2026 20:40

@gto90 gto90 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

@gto90 gto90 requested a review from ycagel July 2, 2026 20:53

@ycagel ycagel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cACK

@gto90 gto90 merged commit c7fbc99 into develop Jul 2, 2026
12 of 13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants