Skip to content

v9.26.5: fix DigiDollar oracle startup hang + bury Taproot/DigiDollar/AlgoLock deployments#429

Merged
ycagel merged 5 commits into
developfrom
fix/dd-oracle-startup-scan-hang
Jul 23, 2026
Merged

v9.26.5: fix DigiDollar oracle startup hang + bury Taproot/DigiDollar/AlgoLock deployments#429
ycagel merged 5 commits into
developfrom
fix/dd-oracle-startup-scan-hang

Conversation

@DigiSwarm

Copy link
Copy Markdown

Summary

v9.26.5 — the first post-activation maintenance release. DigiDollar and AlgoLock activated on mainnet at block 23,869,440 (17 July 2026). This PR ships two changes plus their tests and docs:

  1. Fix the ~15-minute DigiDollar oracle startup hang (3ec927b8b4). The startup oracle-price scan re-ran the BIP9 threshold state machine from scratch through a throwaway VersionBitsCache on each of ~172,800 per-block gate checks, under cs_main, before RPC warmup — so mainnet nodes sat at RPC -28 / "Verifying blocks…" for ~15 minutes. The gate now uses the shared, memoized versionbits cache (the identical predicate ConnectBlock uses). Verified live on mainnet: ~15 min → ~3 s. Pure performance change; consensus-neutral, pinned by new unit + functional tests (restart == -reindex == pre-restart oracle state).

  2. Bury the Taproot, DigiDollar, and AlgoLock deployments (BIP90) (09bcb46aab). All three completed their BIP9 lifecycle and are ACTIVE on mainnet, so they become buried deployments with hardcoded per-network heights — the same housekeeping done previously for CSV/SegWit/ReserveAlgo/Odo:

    Deployment bit Mainnet Testnet26 Signet/Regtest
    Taproot 2 21,168,000 0 0
    DigiDollar 23 23,869,440 600 0
    AlgoLock 0 23,869,440 0 0

    Heights are the empirically verified BIP9 since values — read from live mainnet getdeploymentinfo and verified block-by-block on a fully synced testnet26 node (signaling 200–599, ACTIVE at exactly 600). Burying also removes the last per-block BIP9 recomputation from ConnectBlock (three throwaway-cache call sites, ~15 ms per DD-era block during sync) — every activation check is now one height comparison.

Consensus compatibility

A v9.26.5 node accepts/rejects exactly the same blocks as v9.26.4 for every block on the current mainnet and testnet chains. Deliberately unchanged: the static DD gates keep their historical floors (nDDActivationHeight = nOracleActivationHeight = nDigiDollarMuSig2Height = 23,627,520 mainnet / 600 testnet — the prune floor and oracle-P2P gates are untouched, so existing pruned nodes are unaffected); the AlgoLock nGroestlDeactivationHeight = 23,808,000 static backstop remains OR'd in; Taproot script flags were already unconditional, so its burial has zero script-consensus effect. MinBIP9WarningHeight rises to 23,909,760 / 800 so historical bit-2/23/0 signaling doesn't trigger "unknown new rules" warnings. Standard BIP90 caveat (deep-reorg hypothetical) applies as it did for Bitcoin's burials; no real chain is affected.

Breaking interface changes (details in doc/release-notes/release-notes-9.26.5.md)

  • getdigidollardeploymentinfo: new buried shape (enabled, type:"buried", status:"active"|"defined", activation_height); BIP9 signaling fields removed; also fixes the old back-scan's off-by-one activation_height.
  • getdeploymentinfo / REST /deploymentinfo: the three entries become {"type":"buried","active":…,"height":…} with no bip9 sub-object.
  • getblocktemplate: rules always lists taproot/digidollar/algolock once active (like csv); version never sets bits 2/23/0.
  • Regtest: -digidollaractivationheight=N now activates at exactly N; new -testactivationheight=taproot|digidollar|algolock@H; -vbparams for the three buried names is a startup error (CI-pinned).

Testing

  • Full unit suite: 3,413 test cases, no errors, including the new deployment_burial_tests suite (per-network height pins, window alignment, floor preservation, knob semantics, exact activation boundary).
  • Full functional test_runner.py suite: all pass. DigiDollar activation tests rewritten from BIP9 signaling ladders to buried-boundary lifecycles (pre-activation rejection incl. digidollar-not-active mempool reason, exact boundary flip, post-activation mint/send/redeem, reorg-below-activation purge, multinode/IBD/reindex parity, pruning). Unrepresentable BIP9 scenarios (timeout/FAILED, threshold math, signaling ladders) removed; generic BIP9 coverage remains via TESTDUMMY in versionbits_tests.cpp.
  • Live regtest smoke: buried rendering, knob exact-N activation, -testactivationheight split semantics, -vbparams rejection.
  • Independent adversarial review passes over the consensus diff, RPC/GBT surface, and test migration found no code defects (remaining doc/help nits fixed in 5cb7377769).

Commits

  • 3ec927b8b4 fix: eliminate ~15-min DigiDollar oracle startup hang (shared versionbits cache)
  • d26a3d2469 v9.26.5 (version bump)
  • 09bcb46aab consensus: bury Taproot, DigiDollar, and AlgoLock deployments (BIP90)
  • 5cb7377769 review: fix burial docs/help accuracy, add interface-contract regression tests
  • 05b50e229d doc: add RELEASE_v9.26.5.md root release notes

Review guide

Start with src/consensus/params.h (enum moves + DeploymentHeight), then src/kernel/chainparams.cpp (per-network heights, regtest option plumbing), then the five call-site rewrites (consensus/digidollar.cpp, init.cpp, oracle/bundle_manager.cpp, rpc/digidollar.cpp, qt/digidollartab.cpp). Everything else reaches the buried predicates automatically via the existing DeploymentActiveAfter/DeploymentActiveAt overloads. RELEASE_v9.26.5.md (root) has the plain-language summary.

…bits cache)

WHAT HAPPENED
On startup the node rebuilds its in-memory oracle price + volatility cache by
scanning the last 172,800 blocks (~30 days). For every block it re-evaluated the
BIP9 DigiDollar-activation gate through
IsDigiDollarEnabled(pprev, const Consensus::Params&), which allocates a THROWAWAY
VersionBitsCache and recomputes the deployment's threshold-state machine from
scratch (an O(nPeriod) walk) on each call. With nothing memoized across ~172,800
iterations this took ~15 minutes, run synchronously under LOCK(cs_main) BEFORE
SetRPCWarmupFinished() -- so RPC returned -28 and P2P stalled the whole time, and
the GUI sat on the stale "Verifying blocks..." label. It also emitted 150k+
per-block "Skipping pre-activation" log lines.

THE FIX (one safe change)
The startup per-block gate now evaluates the IDENTICAL BIP9 predicate through the
shared, memoized versionbits cache via a new
ShouldLoadStartupOraclePriceForBlock(int, const CBlockIndex*, const ChainstateManager&)
overload (-> IsDigiDollarEnabled(pprev, chainman)) -- O(1) amortized, exactly the
lookup ConnectBlock already uses. Per-block skip logging is replaced by a single
aggregate count.

This is a PURE performance change: byte-identical to v9.26.4 (no consensus rule
changes). New unit tests prove the two IsDigiDollarEnabled overloads and the
startup gate compute the same activation boolean at every height on a real chain.
Verified live on mainnet: the oracle startup scan dropped from ~15 min to ~3 s
(163,850 pre-activation blocks skipped, one aggregate log line instead of 150k),
and the node came up healthy and caught up.

DELIBERATELY NOT SHIPPED: the durable on-disk price snapshot ("L3"). As designed
it persisted the live-throttled volatility deque and would diverge from a
-reindex node once the rolling 30-day window slides past the activation height
(~28 days out), splitting the chain via the consensus freeze gate. That
underlying anchor-dependence of the freeze-brake throttle is a PRE-EXISTING
v9.26.4 issue; the durable instant-startup optimization and the root throttle fix
are separate, deliberate follow-ups and are not required for this startup-speed
fix.

TESTS
- src/test/digidollar_oracle_startup_tests.cpp: the two IsDigiDollarEnabled
  overloads agree, and the startup gate == the BIP9 ConnectBlock predicate, at
  every height on a real connected chain.
- test/functional/digidollar_oracle_startup_consensus.py: restart == -reindex ==
  pre-restart reconstructed oracle state (consensus-neutral), and no durable
  snapshot file is written.
All three BIP9 deployments have completed their lifecycle and are ACTIVE
on mainnet, so this converts them to buried deployments with hardcoded
per-network activation heights — the same housekeeping DigiByte/Bitcoin
already performed for CSV/SegWit/ReserveAlgo/Odo:

                  mainnet     testnet26   signet/regtest
  Taproot       21,168,000        0             0
  DigiDollar    23,869,440      600             0
  AlgoLock      23,869,440        0             0

The heights are the empirically verified BIP9 'since' values: read from
live mainnet getdeploymentinfo (tip 23,882,878) and confirmed block-by-
block on a fully-synced testnet26 node (bit-23 signaling in 200-599,
ACTIVE at exactly 600).

WHY
- The BIP9 state machine serves no further purpose for these
  deployments: they can never re-signal, time out, or fail.
- Burying removes every remaining BIP9 threshold-state walk from the
  hot paths. ConnectBlock previously evaluated the DigiDollar gate via
  the throwaway-VersionBitsCache IsDigiDollarEnabled overload at three
  call sites (dd_bip9_active, CheckMuSig2OracleBundleVersion,
  ValidateBlockOracleData) at ~5 ms each per DD-era block; all of that
  — and the startup-scan cost class fixed by 3ec927b — collapses to
  a single O(1) height comparison.
- Blocks stop signaling bits 2/23/0, so GBT version fields are clean
  for SHA256D pool stacks (structurally resolves the bit-23
  version-rolling interference some pools reported).

CONSENSUS COMPATIBILITY
A v9.26.5 node accepts/rejects exactly the same blocks as v9.26.4 for
every block on the current mainnet and testnet chains. Deliberately
unchanged: the static DD gates (nDDActivationHeight =
nOracleActivationHeight = nDigiDollarMuSig2Height = 23,627,520 mainnet /
600 testnet / 650-650-0 regtest) keep their historical floor role for
prune locks, oracle P2P gating and the pre-floor collateral gate;
EarliestActivationFloor(params) = min(nDDActivationHeight,
DeploymentHeight(DIGIDOLLAR)) is value-preserving on every network; the
AlgoLock nGroestlDeactivationHeight=23,808,000 static backstop remains
OR'd with the deployment check; Taproot script flags were already set
unconditionally so its burial has zero script-consensus effect.
MinBIP9WarningHeight rises to 23,909,760 (mainnet) / 800 (testnet) so
the historical signaling periods do not trigger "unknown new rules"
warnings. Standard BIP90 caveat: only a reorg below the burial heights
onto a differently-signaled chain could be judged differently — no real
chain is affected.

INTERFACE CHANGES (see doc/release-notes/release-notes-9.26.5.md)
- getdeploymentinfo/getblockchaininfo: the three entries become
  {"type":"buried","active":...,"height":...} with no bip9 sub-object.
- getdigidollardeploymentinfo: new buried shape (enabled, type, status
  active|defined, activation_height = exact burial height); BIP9
  signaling fields removed. This also fixes the old back-scan's
  off-by-one activation_height report.
- getblocktemplate: rules always lists taproot/digidollar/algolock once
  active (hardcoded like "csv"); vbavailable drops them; version never
  sets bits 2/23/0.
- Regtest: -digidollaractivationheight=N now activates DigiDollar at
  exactly N (buried height + static gates; previously first 144-block
  boundary >= max(432, N)); new -testactivationheight=taproot@H/
  digidollar@H/algolock@H moves only the buried height;
  -vbparams=taproot/digidollar/algolock is now a startup error.

TESTS
- New src/test/deployment_burial_tests.cpp pins the per-network burial
  heights, window alignment, floor preservation, knob semantics and the
  exact activation boundary.
- Unit/functional suites migrated off BIP9 signaling: state-machine/
  threshold/timeout cases that are unrepresentable post-burial were
  removed (generic BIP9 remains covered via TESTDUMMY in
  versionbits_tests.cpp); everything else was converted to
  buried-boundary equivalents preserving intent, including the wave12
  constraint that startup oracle-price reconstruction follows the same
  activation predicate as block connection.
- Full unit suite: 3,413 test cases, no errors. Full functional
  test_runner.py suite: all passed. Verified live on regtest:
  buried rendering, knob=432 exact activation, testactivationheight
  split semantics, and -vbparams rejection.

Docs updated (CLAUDE.md, DIGIDOLLAR_ACTIVATION_EXPLAINER.md marked
historical + burial section, ARCHITECTURE/REPO_MAP surgical fixes) and
release notes added for v9.26.5.
…ion tests

Follow-ups from the 3-agent adversarial review of 09bcb46 (no code
defects found; all findings were documentation accuracy or test polish):

- Correct the -testactivationheight=digidollar@H claim in the help text,
  the kernel/chainparams.cpp switch comment, CLAUDE.md, and the release
  notes: the static DD/oracle gates keep their defaults, but
  nDigiDollarMuSig2Height is DERIVED as min(nDDActivationHeight,
  DigiDollarHeight) and so follows H below 650 (verified live:
  digidollar@300 reports musig2_format_activation_height=300).
- Release notes: drop the getblockchaininfo mention — that RPC carries
  no deployment information in this v26-lineage codebase; only
  getdeploymentinfo (and REST /deploymentinfo) render the buried
  entries.
- init.cpp: replace the stale "-digidollaractivationheight ... overrides
  BIP9 activation" help text with the buried exact-height semantics.
- rpc_blockchain.py: pin the documented startup-error contracts in CI —
  -vbparams=digidollar/taproot/algolock now assert "Invalid deployment",
  and an out-of-range -digidollaractivationheight asserts its error
  (previously these were only smoke-tested manually).
Plain-language v9.26.5 release notes in the root-directory style of
RELEASE_v9.26.2.md / RELEASE_v9.26.3.md: first post-activation release
context (DigiDollar live at 23,869,440), the oracle startup-hang fix,
the BIP90 burial of Taproot/DigiDollar/AlgoLock with the verified
heights, breaking RPC/GBT/regtest interface changes, upgrade guidance,
and testing coverage.
@ycagel
ycagel requested review from JaredTate, gto90 and ycagel July 20, 2026 05:53
@Shen

Shen commented Jul 20, 2026

Copy link
Copy Markdown

Please check Gitter and github issues. There are still some other important bugs that should be fixed in v9.26.5.

@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 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 merged commit 1615931 into develop Jul 23, 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants