From 3ec927b8b4262a3c219c6b5bb2fb4b7e38f7621c Mon Sep 17 00:00:00 2001 From: DigiSwarm <13957390+JaredTate@users.noreply.github.com> Date: Sat, 18 Jul 2026 21:37:42 -0600 Subject: [PATCH 1/5] fix: eliminate ~15-min DigiDollar oracle startup hang (shared versionbits 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. --- src/Makefile.test.include | 1 + src/oracle/bundle_manager.cpp | 35 ++++- src/oracle/bundle_manager.h | 5 + src/test/digidollar_oracle_startup_tests.cpp | 89 +++++++++++++ .../digidollar_oracle_startup_consensus.py | 123 ++++++++++++++++++ test/functional/test_runner.py | 1 + 6 files changed, 250 insertions(+), 4 deletions(-) create mode 100644 src/test/digidollar_oracle_startup_tests.cpp create mode 100755 test/functional/digidollar_oracle_startup_consensus.py diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 640bded12a..9a5263f3a1 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -75,6 +75,7 @@ DIGIBYTE_TESTS =\ test/base64_tests.cpp \ test/digidollar_activation_tests.cpp \ test/digidollar_activation_wave12_tests.cpp \ + test/digidollar_oracle_startup_tests.cpp \ test/digidollar_wave26_compat_tests.cpp \ test/digidollar_wave13_parity_tests.cpp \ test/digidollar_wave14_reorg_replay_tests.cpp \ diff --git a/src/oracle/bundle_manager.cpp b/src/oracle/bundle_manager.cpp index b40a041a21..d0bb9ce868 100644 --- a/src/oracle/bundle_manager.cpp +++ b/src/oracle/bundle_manager.cpp @@ -1820,6 +1820,7 @@ bool OracleBundleManager::LoadPricesFromChain(ChainstateManager& chainman) int scan_depth = std::min(std::max(ORACLE_VALIDITY_BLOCKS, VOLATILITY_HISTORY_BLOCKS), tip_height); const int price_cache_start_height = std::max(0, tip_height - ORACLE_VALIDITY_BLOCKS + 1); int prices_found = 0; + int skipped_pre_activation = 0; std::vector volatility_prices; LogPrintf("Oracle: Scanning last %d blocks for oracle prices (height %d to %d)...\n", @@ -1830,10 +1831,15 @@ bool OracleBundleManager::LoadPricesFromChain(ChainstateManager& chainman) CBlockIndex* block_index = chainman.ActiveChain()[height]; if (!block_index) continue; - if (!ShouldLoadStartupOraclePriceForBlock(height, block_index, consensus)) { - LogPrint(BCLog::DIGIDOLLAR, - "Oracle: Skipping pre-activation price cache load at height %d\n", - height); + // L1 (startup-hang fix): evaluate the BIP9 DigiDollar-activation gate through + // the SHARED, memoized versionbits cache (via chainman) instead of allocating a + // throwaway VersionBitsCache on every one of the up-to ~172,800 iterations. It + // computes the IDENTICAL activation boolean ConnectBlock uses — a pure, + // O(1)-amortized performance change with no consensus effect (pinned by + // digidollar_oracle_startup_tests.cpp). Pre-activation blocks are counted and + // reported once after the loop instead of logged per block. + if (!ShouldLoadStartupOraclePriceForBlock(height, block_index, chainman)) { + ++skipped_pre_activation; continue; } @@ -1891,6 +1897,12 @@ bool OracleBundleManager::LoadPricesFromChain(ChainstateManager& chainman) } } + if (skipped_pre_activation > 0) { + LogPrint(BCLog::DIGIDOLLAR, + "Oracle: skipped %d pre-activation blocks during startup price load\n", + skipped_pre_activation); + } + DigiDollar::Volatility::VolatilityMonitor::ReconstructFromBlockData( volatility_prices, static_cast(tip_height)); @@ -1921,6 +1933,21 @@ bool OracleBundleManager::ShouldLoadStartupOraclePriceForBlock(int height, const return height >= activation_height; } +bool OracleBundleManager::ShouldLoadStartupOraclePriceForBlock(int height, const CBlockIndex* block_index, const ChainstateManager& chainman) +{ + if (block_index && block_index->pprev) { + // L1: shared, memoized versionbits cache (via chainman). Computes the + // IDENTICAL BIP9 activation predicate as the const-Params& overload — exactly + // what ConnectBlock uses — but without rebuilding a throwaway VersionBitsCache + // (an O(nPeriod) walk) on each of the up-to ~172,800 startup-scan iterations. + return DigiDollar::IsDigiDollarEnabled(block_index->pprev, chainman); + } + + // Genesis / null-pprev fallback (never hit for real in-chain blocks): reuse the + // const-Params& height logic via the chain's consensus params. + return ShouldLoadStartupOraclePriceForBlock(height, block_index, chainman.GetConsensus()); +} + void OracleBundleManager::Clear() { // Clear all state for test isolation diff --git a/src/oracle/bundle_manager.h b/src/oracle/bundle_manager.h index 6a45b837b1..d5dba02ede 100644 --- a/src/oracle/bundle_manager.h +++ b/src/oracle/bundle_manager.h @@ -217,7 +217,12 @@ class OracleBundleManager //! not be read (incomplete/damaged block data) — the caller must abort //! startup rather than reconstruct price/volatility state from partial data. static bool LoadPricesFromChain(ChainstateManager& chainman); + //! Startup price-scan per-block gate = the BIP9 DigiDollar-activation predicate for + //! block_index. Production uses the ChainstateManager overload (shared, memoized + //! versionbits cache — O(1) amortized, the fix for the ~15-minute startup hang); the + //! Consensus::Params overload is the non-memoized path retained for tests/edge callers. static bool ShouldLoadStartupOraclePriceForBlock(int height, const CBlockIndex* block_index, const Consensus::Params& params); + static bool ShouldLoadStartupOraclePriceForBlock(int height, const CBlockIndex* block_index, const ChainstateManager& chainman); //! Clear all state (for testing) void Clear(); diff --git a/src/test/digidollar_oracle_startup_tests.cpp b/src/test/digidollar_oracle_startup_tests.cpp new file mode 100644 index 0000000000..936bd6c466 --- /dev/null +++ b/src/test/digidollar_oracle_startup_tests.cpp @@ -0,0 +1,89 @@ +// Copyright (c) 2026 The DigiByte Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +// +// Consensus-safety tests for the DigiDollar startup oracle price-cache scan +// (OracleBundleManager::LoadPricesFromChain / ShouldLoadStartupOraclePriceForBlock). +// +// The per-block startup gate was changed to evaluate the BIP9 DigiDollar +// activation predicate through the SHARED, memoized versionbits cache +// (chainman.m_versionbitscache) instead of allocating a throwaway +// VersionBitsCache on every one of the up-to ~172,800 iterations. That is a +// pure performance change: it MUST compute the identical activation boolean, so +// it can neither load a price from a block that would previously have been +// skipped (a bypass) nor skip a block that would previously have been loaded (a +// consensus divergence). These tests pin exactly that invariant. +// +// End-to-end loader correctness (a real MuSig2 v0x03 coinbase bundle -> price +// cache) is already covered by rh66_startup_oracle_price_loading_tests in +// rh61_coinbase_price_cache_poisoning_tests.cpp; the exhaustive BIP9 State() +// machine is covered by versionbits_tests.cpp. Here we prove only that the +// startup gate tracks that predicate exactly, on a real connected chain. + +#include +#include +#include +#include +#include +#include + +#include + +#include + +BOOST_FIXTURE_TEST_SUITE(digidollar_oracle_startup_tests, TestChain100Setup) + +// The two DigiDollar::IsDigiDollarEnabled overloads — the shared-cache one +// (const ChainstateManager&) the fix switches TO, and the throwaway-cache one +// (const Consensus::Params&) it switches AWAY from — must return the SAME +// activation boolean for every block on a real chain. Any divergence here would +// be a consensus divergence hiding behind a "performance" change. +BOOST_AUTO_TEST_CASE(startup_predicate_overloads_agree_on_real_chain) +{ + // Extend the pre-mined 100-block regtest chain so the scan spans a + // substantial run of genuinely-connected blocks. + mineBlocks(600); + + const Consensus::Params& consensus = m_node.chainman->GetConsensus(); + + LOCK(cs_main); + const CChain& chain = m_node.chainman->ActiveChain(); + BOOST_REQUIRE(chain.Height() > 200); + + for (int h = 0; h <= chain.Height(); ++h) { + const CBlockIndex* index = chain[h]; + BOOST_REQUIRE(index != nullptr); + const bool shared = DigiDollar::IsDigiDollarEnabled(index, *m_node.chainman); + const bool throwaway = DigiDollar::IsDigiDollarEnabled(index, consensus); + BOOST_CHECK_MESSAGE(shared == throwaway, + "IsDigiDollarEnabled overloads diverged at height " << h + << " (shared=" << shared << " throwaway=" << throwaway << ")"); + } +} + +// The startup per-block gate must equal the BIP9 activation predicate applied to +// the block's parent — exactly the predicate ConnectBlock uses. Proving equality +// proves there is NO bypass (it never accepts a price from an un-activated block) +// and NO over-skip (it never drops an activated block's price). +BOOST_AUTO_TEST_CASE(should_load_startup_matches_bip9_predicate) +{ + mineBlocks(600); + + LOCK(cs_main); + const CChain& chain = m_node.chainman->ActiveChain(); + BOOST_REQUIRE(chain.Height() > 200); + + for (int h = 1; h <= chain.Height(); ++h) { + const CBlockIndex* index = chain[h]; + BOOST_REQUIRE(index != nullptr); + BOOST_REQUIRE(index->pprev != nullptr); + const bool gate = OracleBundleManager::ShouldLoadStartupOraclePriceForBlock( + h, index, *m_node.chainman); + const bool predicate = DigiDollar::IsDigiDollarEnabled(index->pprev, *m_node.chainman); + BOOST_CHECK_MESSAGE(gate == predicate, + "ShouldLoadStartupOraclePriceForBlock diverged from the BIP9 predicate at height " + << h << " (gate=" << gate << " predicate=" << predicate << ")"); + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/functional/digidollar_oracle_startup_consensus.py b/test/functional/digidollar_oracle_startup_consensus.py new file mode 100755 index 0000000000..abc0998d58 --- /dev/null +++ b/test/functional/digidollar_oracle_startup_consensus.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Consensus-neutrality regression for the DigiDollar startup oracle price scan. + +The startup price/volatility reconstruction (OracleBundleManager::LoadPricesFromChain) +was changed to evaluate the per-block BIP9 DigiDollar-activation gate through the +SHARED, memoized versionbits cache (chainman) instead of allocating a throwaway +VersionBitsCache on every one of the up-to ~172,800 iterations. That is the fix for +the ~15-minute "Verifying blocks..." startup hang. It is a PURE performance change: +it computes the identical activation boolean, so the reconstructed oracle price + +volatility-freeze state MUST be byte-identical to a full rescan / -reindex, and MUST +match the pre-restart state. + +This test proves exactly that invariant end to end. It builds real on-chain oracle +bundles + DD mints + a volatility move, then asserts the reconstructed oracle state +is IDENTICAL: + (before) the live state before any restart + (A) restart no reindex -> windowed rescan rebuild + (B) restart -reindex -> full block-replay rebuild (ground truth) + +It also asserts the node does NOT write a durable oracle snapshot: the durable +persist/replay optimization was intentionally NOT shipped here (it must be made +byte-identical to a full rescan first, before it can be consensus-safe), so a +stray oracle/oracleprices.dat would be a regression. +""" + +import os + +from test_framework.test_framework import DigiByteTestFramework +from test_framework.util import assert_equal + + +class DigiDollarOracleStartupConsensusTest(DigiByteTestFramework): + def set_test_params(self): + self.num_nodes = 1 + self.setup_clean_chain = True + self.extra_args = [["-digidollar=1", "-txindex=1", "-dandelion=0"]] + + def add_options(self, parser): + self.add_wallet_options(parser) + + def skip_test_if_missing_module(self): + self.skip_if_no_wallet() + + def advance(self, seconds, blocks=2): + """Advance mock time and mine oracle-bundle blocks so the price history + records the current oracle price at the new timestamps.""" + self.t += seconds + self.nodes[0].setmocktime(self.t) + self.nodes[0].generate(blocks) + + def reconstructed_state(self): + """The reconstruction fingerprint LoadPricesFromChain rebuilds from the + chain. getoracleprice()['price_micro_usd'] reflects the LIVE regtest + mock-oracle shim (resets on restart) so it is excluded; last_update_height + / last_update_time / volatility are the on-chain reconstruction that a + restart and a -reindex must reproduce identically.""" + p = self.nodes[0].getoracleprice() + return { + "last_update_height": p.get("last_update_height"), + "last_update_time": p.get("last_update_time"), + "volatility": p.get("volatility"), + } + + def run_test(self): + node = self.nodes[0] + self.t = 1700000000 + node.setmocktime(self.t) + + # --- Build on-chain DD supply + oracle price history + volatility event --- + node.generate(150) # past coinbase maturity + DD activation + node.setmockoracleprice(50000) # ~$0.50 / DGB + self.advance(60, blocks=2) + for _ in range(3): + res = node.mintdigidollar(100000, 4) # $1000 each, tier 4 + node.generate(1) + assert res["txid"] in node.getblock(node.getbestblockhash())["tx"] + self.advance(60, blocks=1) + node.setmockoracleprice(9000) # sharp move engages the volatility state + for _ in range(4): + self.advance(300, blocks=2) + + before = self.reconstructed_state() + self.log.info(f"Pre-restart reconstructed state: {before}") + assert before["last_update_height"] and before["last_update_height"] > 100 + + snapshot = os.path.join(node.chain_path, "oracle", "oracleprices.dat") + + # --- (A) Restart WITHOUT reindex -> windowed rescan rebuild --- + self.log.info("Restart A: no reindex (windowed rescan reconstruction) ...") + self.restart_node(0, extra_args=self.extra_args[0]) + node.setmocktime(self.t) + with open(node.debug_log_path, "r", encoding="utf-8") as fh: + log_after_a = fh.read() + # The L1 fix keeps the windowed rescan; it must NOT persist/reload a durable + # oracle snapshot (that optimization is deferred until it is proven + # byte-identical to a rescan). + assert not os.path.exists(snapshot), \ + "unexpected durable oracle snapshot written (L3 must stay reverted)" + assert "loaded price snapshot" not in log_after_a, \ + "startup took a durable-snapshot fast path (L3 must stay reverted)" + assert "Scanning last" in log_after_a, \ + "startup did not run the windowed oracle price rescan" + after_restart = self.reconstructed_state() + self.log.info(f"Post-restart (rescan) state: {after_restart}") + assert_equal(before, after_restart) + + # --- (B) Restart WITH -reindex -> full block replay = ground truth --- + self.log.info("Restart B: -reindex (authoritative full block replay) ...") + self.restart_node(0, extra_args=self.extra_args[0] + ["-reindex"]) + node.setmocktime(self.t) + after_reindex = self.reconstructed_state() + self.log.info(f"Post-reindex state: {after_reindex}") + + # Restart (windowed rescan) must reconstruct EXACTLY what the authoritative + # full block replay does, or a restarted node could diverge in consensus. + assert_equal(after_restart, after_reindex) + assert_equal(before, after_reindex) + self.log.info("PASS: restart (rescan) == full-reindex == pre-restart state; " + "L1 cache swap is consensus-neutral, no durable snapshot written.") + + +if __name__ == '__main__': + DigiDollarOracleStartupConsensusTest().main() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index dafb07c784..862d6e5df3 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -282,6 +282,7 @@ 'digidollar_redemption_amounts.py', 'digidollar_protection.py', 'digidollar_health_restart_consensus.py', + 'digidollar_oracle_startup_consensus.py', 'digidollar_activation.py', 'digidollar_basic.py', 'digidollar_tx_amounts_debug.py', From d26a3d24695a3069d93a46185eca7482dee21a89 Mon Sep 17 00:00:00 2001 From: DigiSwarm <13957390+JaredTate@users.noreply.github.com> Date: Sun, 19 Jul 2026 12:53:13 -0600 Subject: [PATCH 2/5] v9.26.5 --- configure.ac | 2 +- src/qt/res/icons/digibyte_wallet.png | Bin 18686 -> 18747 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6636a346c7..2632ff681d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.69]) define(_CLIENT_VERSION_MAJOR, 9) define(_CLIENT_VERSION_MINOR, 26) -define(_CLIENT_VERSION_BUILD, 4) +define(_CLIENT_VERSION_BUILD, 5) define(_CLIENT_VERSION_RC, 0) define(_CLIENT_VERSION_SUFFIX, []) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/src/qt/res/icons/digibyte_wallet.png b/src/qt/res/icons/digibyte_wallet.png index 2a00f22761b6480937b69c59110ca22ca957f166..e26e14db464eab5325110451328e13509fb175d4 100644 GIT binary patch delta 18643 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 delta 18589 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 RD|Y|@002ovPDHLkV1oD2sviIV From 09bcb46aab69589864917426daa25f1bac9243d3 Mon Sep 17 00:00:00 2001 From: DigiSwarm <13957390+JaredTate@users.noreply.github.com> Date: Sun, 19 Jul 2026 18:54:59 -0600 Subject: [PATCH 3/5] consensus: bury Taproot, DigiDollar, and AlgoLock deployments (BIP90) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 3ec927b8b4 — 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. --- ARCHITECTURE.md | 12 +- CLAUDE.md | 24 +- DIGIDOLLAR_ACTIVATION_EXPLAINER.md | 93 ++-- DIGIDOLLAR_ARCHITECTURE.md | 2 +- DIGIDOLLAR_ORACLE_ARCHITECTURE.md | 48 +- REPO_MAP.md | 6 +- REPO_MAP_DIGIDOLLAR.md | 20 +- doc/release-notes/release-notes-9.26.5.md | 179 +++++++ src/Makefile.test.include | 1 + src/chainparams.cpp | 21 +- src/chainparamsbase.cpp | 2 +- src/consensus/digidollar.cpp | 21 +- src/consensus/params.h | 27 +- src/deploymentinfo.cpp | 24 +- src/digidollar/digidollar.cpp | 10 +- src/init.cpp | 6 +- src/kernel/chainparams.cpp | 144 +++-- src/oracle/bundle_manager.cpp | 12 +- src/qt/digidollartab.cpp | 23 +- src/rpc/digidollar.cpp | 78 +-- src/rpc/mining.cpp | 6 + src/test/deployment_burial_tests.cpp | 219 ++++++++ src/test/digidollar_activation_tests.cpp | 493 +++++------------- .../digidollar_activation_wave12_tests.cpp | 292 ++++------- src/test/digidollar_redteam_tests.cpp | 190 +++---- .../digidollar_rh31_consensus_fork_tests.cpp | 116 +++-- ...idollar_rh47_consensus_fork_deep_tests.cpp | 172 +++--- src/test/digidollar_txindex_tests.cpp | 11 +- src/test/fuzz/digidollar_prune_blockdb.cpp | 51 +- src/test/musig2_activation_tests.cpp | 28 +- src/test/rh51_checkphase3_v1_split_tests.cpp | 19 +- test/functional/digidollar_activation.py | 322 +++++++----- .../digidollar_activation_boundary.py | 153 +++--- .../digidollar_activation_multinode.py | 160 +++--- .../digidollar_collateral_spend_guards.py | 2 +- test/functional/digidollar_gbt_optin.py | 3 +- .../digidollar_mempool_miner_parity.py | 2 +- .../digidollar_oracle_block_rules_relay.py | 3 +- .../digidollar_oracle_bundle_reject_matrix.py | 3 +- .../digidollar_oracle_gbt_stale_cache.py | 3 +- .../digidollar_oracle_reorg_cache.py | 3 +- test/functional/digidollar_rpc_deployment.py | 105 ++-- test/functional/digidollar_rpc_gating.py | 75 +-- ...igidollar_verifychain_cache_side_effect.py | 3 +- .../digidollar_wave14_multinode_ibd_reorg.py | 29 +- .../digidollar_wave18_rpc_matrix.py | 9 +- .../digidollar_wave20_oracle_p2p.py | 39 +- .../digidollar_wave26_mixed_node_compat.py | 126 ++--- .../feature_digibyte_groestl_deactivation.py | 4 +- test/functional/feature_digidollar_pruning.py | 27 +- test/functional/rpc_blockchain.py | 42 +- 51 files changed, 1789 insertions(+), 1674 deletions(-) create mode 100644 doc/release-notes/release-notes-9.26.5.md create mode 100644 src/test/deployment_burial_tests.cpp diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c4518f2cbe..86d536d457 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -241,7 +241,7 @@ struct Params { int OdoHeight = 9112320; // Odocrypt activation (BuriedDeployment) // DigiDollar / Oracle - int nDDActivationHeight{0}; // BIP9 alignment height + int nDDActivationHeight{0}; // Static DD floor gate (historical BIP9 floor) int nOracleActivationHeight{std::numeric_limits::max()}; // Live-feed activation height int nOracleEpochLength{1440}; // Blocks per oracle epoch (default: 1440 = 24 hours) int nOracleRequiredMessages{1}; // Off-chain quorum threshold @@ -253,7 +253,7 @@ struct Params { }; ``` -`Consensus::DEPLOYMENT_DIGIDOLLAR` (bit 23) is the BIP9 deployment that gates `SCRIPT_VERIFY_DIGIDOLLAR`; production `min_activation_height`, `nDDActivationHeight`, `nOracleActivationHeight`, and `nDigiDollarMuSig2Height` are aligned in `src/kernel/chainparams.cpp` (mainnet 23627520, testnet26 600). Testnet26 uses default P2P port 12033, data directory `testnet26`, reset genesis timestamp 1780156800, and the same timestamp as its BIP9 start. Default regtest uses BIP9 `ALWAYS_ACTIVE` / `min_activation_height=0` with DD/oracle height gates at 650; `nDigiDollarMuSig2Height` follows the effective BIP9 boundary (`0`) so v0x03 quotes are valid whenever DigiDollar is active. The direct `-digidollaractivationheight=N` knob retargets both BIP9 and the DD/oracle/MuSig2 gates. Startup oracle-price cache reconstruction follows the BIP9 predicate used by block connection so regtest BIP9-active oracle bundles below 650 are not skipped on restart/reindex. MuSig2 v0x03 oracle bundles are required as soon as DigiDollar is active, not before the DD/oracle activation height. +(v9.26.5 burial) `Consensus::DEPLOYMENT_DIGIDOLLAR` is now a **buried deployment** (BIP90; historically BIP9 bit 23) that gates `SCRIPT_VERIFY_DIGIDOLLAR` at `Consensus::Params::DigiDollarHeight` (mainnet 23869440, testnet26 600, signet/regtest 0 — the verified BIP9 `since` heights; `TaprootHeight`/`AlgoLockHeight` are buried alongside). The static gates `nDDActivationHeight`, `nOracleActivationHeight`, and `nDigiDollarMuSig2Height` remain aligned at the historical floor in `src/kernel/chainparams.cpp` (mainnet 23627520, testnet26 600). Testnet26 uses default P2P port 12033, data directory `testnet26`, and reset genesis timestamp 1780156800. Default regtest buries DigiDollar at height 0 with DD/oracle height gates at 650; `nDigiDollarMuSig2Height = min(nDDActivationHeight, DigiDollarHeight) = 0` so v0x03 quotes are valid whenever DigiDollar is active. The direct `-digidollaractivationheight=N` knob retargets the buried height and the DD/oracle/MuSig2 gates together (DD activates at exactly N); `-testactivationheight=digidollar@H` moves only the buried height, and `-vbparams=digidollar:...` is a startup error. Startup oracle-price cache reconstruction follows the same buried predicate used by block connection so regtest DD-active oracle bundles below 650 are not skipped on restart/reindex. MuSig2 v0x03 oracle bundles are required as soon as DigiDollar is active, not before the DD/oracle activation height. ### 3.3 Block Validation Results @@ -686,7 +686,7 @@ enum class SigVersion { | `SCRIPT_VERIFY_CHECKSEQUENCEVERIFY` | BIP112 CSV | | `SCRIPT_VERIFY_WITNESS` | BIP141 SegWit | | `SCRIPT_VERIFY_TAPROOT` | BIP341/342 Taproot | -| `SCRIPT_VERIFY_DIGIDOLLAR` | DigiDollar opcodes (`OP_DIGIDOLLAR`/`OP_DDVERIFY`/`OP_CHECKPRICE`/`OP_CHECKCOLLATERAL`/`OP_ORACLE`); set in `GetBlockScriptFlags()` (`validation.cpp:2755, 2796-2797`) only when BIP9 `DEPLOYMENT_DIGIDOLLAR` is active. | +| `SCRIPT_VERIFY_DIGIDOLLAR` | DigiDollar opcodes (`OP_DIGIDOLLAR`/`OP_DDVERIFY`/`OP_CHECKPRICE`/`OP_CHECKCOLLATERAL`/`OP_ORACLE`); set in `GetBlockScriptFlags()` (`validation.cpp:2755, 2796-2797`) only when the buried `DEPLOYMENT_DIGIDOLLAR` deployment is active (BIP90 since v9.26.5). | ### 8.4 DigiDollar Opcodes @@ -1183,9 +1183,9 @@ Aggregate Schnorr signature + participation bitmap → Coinbase OP_RETURN |---------|--------------------------------------|----------------------------------------------|-----------------------------------|-----------------| | Mainnet | 23,627,520 | 23,627,520 (= DD) | 23,627,520 (= DD) | 7 signatures from 35 configured active keys | | Testnet26 | 600 | 600 (= DD) | 600 (= DD) | 7 signatures from 35 configured active keys | -| Regtest | 650 | 650 (= DD) | 0 (= BIP9 ALWAYS_ACTIVE boundary) | 4-of-7 | +| Regtest | 650 | 650 (= DD) | 0 (= buried `DigiDollarHeight`) | 4-of-7 | -`nDigiDollarMuSig2Height` now collapses to the effective DigiDollar activation boundary: `nDDActivationHeight` on mainnet/testnet, and BIP9 `min_activation_height=0` on default regtest where DigiDollar is `ALWAYS_ACTIVE`. The legacy `nDigiDollarPhase2Height` / `nDigiDollarPhase3Height` fields no longer exist. +`nDigiDollarMuSig2Height` now collapses to the effective DigiDollar activation boundary: `nDDActivationHeight` on mainnet/testnet, and the buried `DigiDollarHeight = 0` on default regtest (v9.26.5 burial; formerly the BIP9 `ALWAYS_ACTIVE` boundary). The legacy `nDigiDollarPhase2Height` / `nDigiDollarPhase3Height` fields no longer exist. ### 13.2 Price Message Structure @@ -1461,7 +1461,7 @@ This architecture document has been spot-validated against the active DigiByte C | OdoHeight | 9,112,320 | kernel/chainparams.cpp:126 | | DD_TX_VERSION | 0x0D1D0770 | primitives/transaction.h:47 | | OP_DIGIDOLLAR..OP_ORACLE | 0xbb..0xbf | script/script.h:210-214 | -| DEPLOYMENT_DIGIDOLLAR bit | 23 | kernel/chainparams.cpp:177,517,969,1099 | +| DigiDollarHeight (buried deployment, v9.26.5; historically BIP9 bit 23) | mainnet 23,869,440; testnet26 600; signet/regtest 0 | kernel/chainparams.cpp | | nDigiDollarMuSig2Height | equals effective DigiDollar activation boundary (mainnet 23,627,520; testnet26 600; default regtest 0) | kernel/chainparams.cpp | ### Verified Algorithm Implementations diff --git a/CLAUDE.md b/CLAUDE.md index 3ccddee698..f5af8835fa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ Working directory: `/home/jared/Code/digibyte`. Current DigiDollar v1 campaign b 7. `REPO_MAP_DIGIDOLLAR.md` — DigiDollar/oracle file index (production, wallet, RPC, Qt, tests, fuzz) 8. `DIGIDOLLAR_EXPLAINER.md` — User-facing DigiDollar V1 protocol summary 9. `DIGIDOLLAR_ORACLE_EXPLAINER.md` — User-facing oracle/MuSig2 summary -10. `DIGIDOLLAR_ACTIVATION_EXPLAINER.md` — BIP9 gating of DD/oracle surface +10. `DIGIDOLLAR_ACTIVATION_EXPLAINER.md` — Activation gating of DD/oracle surface (v9.26.5 BIP90 burial + BIP9 history) 11. `DIGIDOLLAR_WALLET_INTEGRATION.md` — Wallet/RPC integration guide 12. `DIGIDOLLAR_EXCHANGE_INTEGRATION.md` — Exchange/custody integration guide 13. `ORACLE_DISCOVERY_ARCHITECTURE.md` — Oracle endpoint discovery design @@ -62,17 +62,23 @@ Defined in `src/script/script.h:209-220`: - `OP_CHECKCOLLATERAL = 0xbe` — compares stack ratio to threshold; consumes ` ` and pushes `ratio>=threshold` - `OP_ORACLE = 0xbf` — coinbase oracle bundle marker -These are OP_SUCCESSx-class opcodes that become functional only when `SCRIPT_VERIFY_DIGIDOLLAR` is set, which only happens when BIP9 `DEPLOYMENT_DIGIDOLLAR` is ACTIVE (bit 23). See `IsDigiDollarOpcode` / `IsOpSuccessForFlags` at `src/script/interpreter.cpp:439-453`. +These are OP_SUCCESSx-class opcodes that become functional only when `SCRIPT_VERIFY_DIGIDOLLAR` is set, which only happens when the buried `DEPLOYMENT_DIGIDOLLAR` deployment is active (BIP90 as of v9.26.5; historically BIP9 bit 23). See `IsDigiDollarOpcode` / `IsOpSuccessForFlags` at `src/script/interpreter.cpp:439-453`. -## Activation summary (BIP9 bit 23) +## Activation summary (buried deployment — BIP90, v9.26.5) -| Network | Start | Min activation height | Window | Threshold | Status | -|---------|-------|----------------------|--------|-----------|--------| -| Mainnet | 2026-06-01 (epoch 1780272000) | 23,627,520 | 40,320 blocks (~1 week) | 70% (28,224 of 40,320) | Pending | -| Testnet (testnet26) | BIP9 start/reset genesis 1780156800 | 600 | 200 blocks | 70% (140 of 200) | Check `getdigidollardeploymentinfo` | -| Regtest | ALWAYS_ACTIVE | 0 | 144 blocks (BIP9 default) | 75% (108 of 144) | Active | +As of v9.26.5 the Taproot, DigiDollar, and AlgoLock deployments are **buried** (BIP90): all three are ACTIVE on mainnet, activation is a hardcoded per-network height returned by `Consensus::Params::DeploymentHeight()` (fields `TaprootHeight` / `DigiDollarHeight` / `AlgoLockHeight`), the BIP9 state machine no longer runs for them, and blocks no longer signal bits 2/23/0. `DeploymentPos` retains only `DEPLOYMENT_TESTDUMMY`. The burial heights are the empirically verified BIP9 `since` heights (live mainnet `getdeploymentinfo`; testnet26 verified block-by-block). The historical BIP9 parameters (bit 23, mainnet start 2026-06-01, 70% of a 40,320-block window, `min_activation_height` floor 23,627,520) are preserved in `DIGIDOLLAR_ACTIVATION_EXPLAINER.md`. -`nDDActivationHeight`, `nOracleActivationHeight`, and `nDigiDollarMuSig2Height` collapse to the same height trigger on mainnet (23,627,520 / 23,627,520 / 23,627,520) and testnet (600 / 600 / 600). Default regtest keeps DD/oracle height gates at 650 / 650 while the BIP9 deployment is `ALWAYS_ACTIVE` with `min_activation_height=0`; `nDigiDollarMuSig2Height` follows that effective BIP9 boundary and is `0`, so v0x03 quotes are valid whenever DigiDollar is active. The direct `-digidollaractivationheight=N` regtest knob retargets both the BIP9 minimum and the static DD/oracle/MuSig2 height gates. Generic `-vbparams=digidollar:...` still overrides BIP9 only; because MuSig2 follows the effective DigiDollar BIP9 boundary, a BIP9-only test override can move MuSig2 validation without moving the static DD/oracle P2P gates. Startup oracle-price reconstruction follows the same BIP9 predicate as block connection, so default-regtest BIP9-active blocks below 650 are not dropped during restart/reindex cache rebuilds. The variable in code is `nDigiDollarMuSig2Height`, not the older `nDigiDollarPhase3Height` (`src/consensus/params.h:195`). Once DigiDollar is active, v0x03 MuSig2 is the only on-chain bundle format ever accepted. +| Network | `TaprootHeight` | `DigiDollarHeight` | `AlgoLockHeight` | +|---------|-----------------|--------------------|------------------| +| Mainnet | 21,168,000 | 23,869,440 | 23,869,440 | +| Testnet (testnet26) | 0 | 600 | 0 | +| Signet / Regtest | 0 | 0 | 0 | + +Static gates are unchanged: mainnet `nDDActivationHeight = nOracleActivationHeight = nDigiDollarMuSig2Height = 23,627,520` (the historical BIP9 floor, deliberately below the 23,869,440 burial height); testnet 600 / 600 / 600. Default regtest keeps the DD/oracle height gates at 650 / 650 while `nDigiDollarMuSig2Height = min(650, DigiDollarHeight) = 0`, so v0x03 quotes are valid whenever DigiDollar is active. `EarliestActivationFloor(params) = min(nDDActivationHeight, DigiDollarHeight)` (0 if the deployment is disabled) — value-preserving vs the pre-burial formula on every network. `IsDigiDollarEnabled` is a pure height compare against `DigiDollarHeight` (no `VersionBitsCache` anywhere in the DD path), and startup oracle-price reconstruction uses the same buried predicate as block connection, so DD-active blocks below the regtest 650 gate are still not dropped during restart/reindex cache rebuilds. + +Regtest knobs: `-digidollaractivationheight=N` sets `DigiDollarHeight` AND `nDDActivationHeight`/`nOracleActivationHeight`/`nDigiDollarMuSig2Height` to N, so DigiDollar activates at exactly height N (pre-burial the knob ran real BIP9 signaling and activated at the first 144-block window boundary >= max(432, N)). `-testactivationheight=taproot@H` / `digidollar@H` / `algolock@H` moves ONLY the buried deployment height (static gates keep their defaults); `-digidollaractivationheight` takes precedence for DigiDollar. `-vbparams=digidollar/taproot/algolock` is now a startup error ("Invalid deployment") — only `testdummy` remains. + +RPC/GBT surface: `getdeploymentinfo`/`getblockchaininfo` render the three deployments as `{"type":"buried","active":…,"height":…}` with no `bip9` sub-object. `getdigidollardeploymentinfo` now returns `{enabled, type:"buried", status:"active"|"defined", activation_height (omitted if the deployment is disabled), oracle_activation_height, musig2_format_activation_height, oracle_pubkey_count, oracle_consensus_required, oracle_total_slots, oracle_seed_peers, musig2_session{...}}`; the BIP9 fields (`bit`, `start_time`, `timeout`, `min_activation_height`, `blocks_until_timeout`, `signaling_blocks`, `threshold`, `period_blocks`, `progress_percent`) were removed, and `activation_height` is now always the burial height (the old back-scan reported the first-active/last-LOCKED_IN block instead). `getblocktemplate` always lists `taproot`/`digidollar`/`algolock` in `rules` once active (hardcoded like `csv`); `vbavailable` no longer mentions them and the template `version` never sets bits 2/23/0. `MinBIP9WarningHeight` is 23,909,760 on mainnet and 800 on testnet. The variable in code is `nDigiDollarMuSig2Height`, not the older `nDigiDollarPhase3Height`. Once DigiDollar is active, v0x03 MuSig2 is the only on-chain bundle format ever accepted. ## Oracle roster diff --git a/DIGIDOLLAR_ACTIVATION_EXPLAINER.md b/DIGIDOLLAR_ACTIVATION_EXPLAINER.md index 1cded4be8e..bb2f27b7b7 100644 --- a/DIGIDOLLAR_ACTIVATION_EXPLAINER.md +++ b/DIGIDOLLAR_ACTIVATION_EXPLAINER.md @@ -1,14 +1,37 @@ # DigiDollar BIP9 Activation — Complete Explainer +## Post-activation status (v9.26.5): BURIED DEPLOYMENT + +**DigiDollar activated, and the deployment is now buried.** As of v9.26.5 the Taproot, DigiDollar, and AlgoLock deployments are **buried deployments** (BIP90): activation is a hardcoded per-network height (`Consensus::Params::DeploymentHeight()`, fields `TaprootHeight` / `DigiDollarHeight` / `AlgoLockHeight` in `src/consensus/params.h`), not a live BIP9 state machine. The burial heights are the empirically verified BIP9 `since` heights — testnet26 verified block-by-block, mainnet verified against live `getdeploymentinfo`: + +| Network | `TaprootHeight` | `DigiDollarHeight` | `AlgoLockHeight` | +|---------|-----------------|--------------------|------------------| +| Mainnet | 21,168,000 | 23,869,440 | 23,869,440 | +| Testnet (testnet26) | 0 | 600 | 0 | +| Signet / Regtest | 0 | 0 | 0 | + +The static gates keep their historical floors: mainnet `nDDActivationHeight = nOracleActivationHeight = nDigiDollarMuSig2Height = 23,627,520` (below the 23,869,440 burial height — `EarliestActivationFloor = min(nDDActivationHeight, DigiDollarHeight)` preserves the 23,627,520 prune/collateral floor exactly); testnet 600; default regtest DD/oracle gates 650 with `nDigiDollarMuSig2Height = min(650, DigiDollarHeight) = 0`. + +**What changed operationally in v9.26.5:** + +- **No signaling.** Blocks no longer set bits 2/23/0; the STARTED/LOCKED_IN/FAILED states no longer exist for these three deployments. `getblocktemplate` lists `taproot`/`digidollar`/`algolock` in `rules` when active (hardcoded like `csv`), omits them from `vbavailable`, and never sets their bits in the template `version`. +- **RPC shapes.** `getdeploymentinfo`/`getblockchaininfo` render the three as `{"type":"buried","active":bool,"height":N}` with no `bip9` sub-object. `getdigidollardeploymentinfo` now returns `{enabled, type:"buried", status:"active"|"defined", activation_height (omitted if disabled), oracle_activation_height, musig2_format_activation_height, oracle_pubkey_count, oracle_consensus_required, oracle_total_slots, oracle_seed_peers, musig2_session{...}}` — the BIP9 fields (`bit`, `start_time`, `timeout`, `min_activation_height`, signaling statistics) were removed and `activation_height` is always the burial height. +- **Regtest knobs.** `-digidollaractivationheight=N` sets `DigiDollarHeight` and the static DD/oracle/MuSig2 gates to N, so DigiDollar activates at exactly height N (pre-burial it ran real BIP9 signaling and activated at the first 144-block window boundary >= max(432, N)). New `-testactivationheight=taproot@H` / `digidollar@H` / `algolock@H` moves only the buried deployment height; `-digidollaractivationheight` takes precedence for DigiDollar. `-vbparams=digidollar/taproot/algolock` is now a startup error ("Invalid deployment") — only `testdummy` remains a versionbits deployment. +- **Predicates.** `IsDigiDollarEnabled` is a pure height compare against `DigiDollarHeight`; no `VersionBitsCache` exists anywhere in the DigiDollar path. `MinBIP9WarningHeight` moved to 23,909,760 (mainnet) / 800 (testnet) so the historical signaling periods do not trigger "unknown new rules" warnings. + +> **The rest of this document is a historical record.** It describes the BIP9 mechanism as designed and as it actually ran through activation — verified block-by-block on testnet26 (DEFINED 0–199, STARTED 200–399, LOCKED_IN 400–599, ACTIVE at 600) and via live mainnet `getdeploymentinfo` (DigiDollar locked in and activated at block 23,869,440, six windows above the 23,627,520 floor). It is deliberately preserved because it documents how activation actually happened; it is **not** how current software evaluates activation. + +--- + ## Overview -DigiDollar activates on the DigiByte blockchain through **BIP9 version bit signaling** — the same proven mechanism used by Bitcoin for SegWit and other soft forks. This ensures DigiDollar only activates when a supermajority of miners explicitly signal support, preventing chain splits and ensuring network consensus. +DigiDollar activated on the DigiByte blockchain through **BIP9 version bit signaling** — the same proven mechanism used by Bitcoin for SegWit and other soft forks. This ensured DigiDollar only activated once a supermajority of miners explicitly signaled support, preventing chain splits and ensuring network consensus. (As of v9.26.5 the completed deployment is buried; see the section above.) **Key principle:** Nothing consensus-critical for DigiDollar works until activation. DD/oracle RPCs, DD transactions, DD opcodes, oracle price relay, oracle consensus relay, MuSig2 relay, `getoracles`, and signed oracle version heartbeats are dormant until the activation predicates below pass. --- -## BIP9 Deployment Parameters +## BIP9 Deployment Parameters (historical) ### Mainnet | Parameter | Value | @@ -38,9 +61,9 @@ DigiDollar activates on the DigiByte blockchain through **BIP9 version bit signa --- -## BIP9 State Machine +## BIP9 State Machine (historical — this is how activation actually ran) -DigiDollar follows the standard BIP9 state transitions: +DigiDollar followed the standard BIP9 state transitions: ``` DEFINED ──→ STARTED ──→ LOCKED_IN ──→ ACTIVE @@ -78,9 +101,9 @@ DEFINED ──→ STARTED ──→ LOCKED_IN ──→ ACTIVE - **Qt behavior:** Activation overlay disappears. Full DD tab (overview, send, receive, mint, redeem, positions, transactions) becomes accessible. - **Oracle behavior:** Authorized oracle operators (slots 0-34 in `consensus.vOraclePublicKeys`, mainnet/testnet) can start their daemon, broadcast off-chain attestations and version heartbeats, participate in MuSig2 nonce/context/partial-sig rounds, and aggregate into the v0x03 on-chain bundle that miners embed in the coinbase for price-dependent DD blocks. `nDigiDollarMuSig2Height` is aligned with the effective DigiDollar activation boundary — MuSig2 v0x03 is the only accepted on-chain format from the moment DigiDollar/oracle consensus activates. -> **Activation boundary nuance (1-block off-by-one).** `getdeploymentinfo` exposes a BIP9 view (`bip9.status = active`) that flips at the period boundary block — i.e. the block whose `pindexPrev->nHeight + 1 == min_activation_height`. The height-based gate `Consensus::IsOracleActive(params, height) == (height >= nOracleActivationHeight)` flips one block later, at `height == min_activation_height` itself. There is therefore a single-block window where `bip9.status` reports `active` but `IsOracleActive(tip)` is still `false`. This is harmless on production because `IsDigiDollarEnabled(prev_block)` already returns true at the period boundary and the miner refuses price-dependent DD mint/redeem templates without a valid v0x03 bundle. DD transfer-only blocks do not need a block oracle price. Boundary tests should use the height-based predicate (`IsOracleActive`/`IsDigiDollarEnabled`) rather than `getdeploymentinfo.bip9.status` when they need the consensus-rule moment, and Wave 12's `DD-FA-SEC-010` fix in `SpendsDigiDollarCollateralVault` deliberately uses `min(nDDActivationHeight, BIP9 min_activation_height)` for the same reason. +> **Activation boundary nuance (1-block off-by-one — historical, pre-burial).** This nuance applied while the deployment was a live BIP9 deployment; post-burial (v9.26.5) `getdeploymentinfo` has no `bip9` view for DigiDollar, and both the deployment predicate and `IsOracleActive` are pure height compares (the first active block is the burial height itself). Historically: `getdeploymentinfo` exposed a BIP9 view (`bip9.status = active`) that flips at the period boundary block — i.e. the block whose `pindexPrev->nHeight + 1 == min_activation_height`. The height-based gate `Consensus::IsOracleActive(params, height) == (height >= nOracleActivationHeight)` flips one block later, at `height == min_activation_height` itself. There is therefore a single-block window where `bip9.status` reports `active` but `IsOracleActive(tip)` is still `false`. This is harmless on production because `IsDigiDollarEnabled(prev_block)` already returns true at the period boundary and the miner refuses price-dependent DD mint/redeem templates without a valid v0x03 bundle. DD transfer-only blocks do not need a block oracle price. Boundary tests should use the height-based predicate (`IsOracleActive`/`IsDigiDollarEnabled`) rather than `getdeploymentinfo.bip9.status` when they need the consensus-rule moment, and Wave 12's `DD-FA-SEC-010` fix in `SpendsDigiDollarCollateralVault` deliberately uses `min(nDDActivationHeight, BIP9 min_activation_height)` for the same reason. -> **Regtest activation knobs.** The direct `-digidollaractivationheight=N` regtest knob defined in `src/chainparams.cpp:102-109` now retargets both BIP9 `min_activation_height` and the static DD/oracle height gates (`nDDActivationHeight` / `nOracleActivationHeight`) in `src/kernel/chainparams.cpp:1222-1225`. Generic `-vbparams=digidollar:start:timeout:minheight` still overrides BIP9 only; because MuSig2 follows the effective DigiDollar BIP9 boundary, a BIP9-only test override can move MuSig2 validation without moving the static DD/oracle P2P gates. Default regtest remains intentionally special: BIP9 is `ALWAYS_ACTIVE` with `min_activation_height=0`, while the DD/oracle P2P height gates default to `650` for local testing. `nDigiDollarMuSig2Height` follows the effective BIP9 boundary (`0`) so v0x03 quotes are valid whenever DigiDollar is active. Startup oracle-price reconstruction follows the BIP9 predicate used by block connection, so BIP9-active default-regtest blocks below 650 are not skipped during restart/reindex cache rebuilds. +> **Regtest activation knobs (current, v9.26.5).** The direct `-digidollaractivationheight=N` regtest knob (`src/chainparams.cpp`) retargets the buried `DigiDollarHeight` together with the static DD/oracle/MuSig2 height gates (`nDDActivationHeight` / `nOracleActivationHeight` / `nDigiDollarMuSig2Height`) in `src/kernel/chainparams.cpp`, so DigiDollar activates at exactly height N. `-testactivationheight=digidollar@H` moves only the buried deployment height (static gates keep their defaults; `-digidollaractivationheight` takes precedence), which lets tests decouple the deployment boundary from the static 650 DD/oracle P2P gates in either direction. `-vbparams=digidollar:...` is now a startup error. Default regtest remains intentionally special: the buried `DigiDollarHeight` is `0`, while the DD/oracle P2P height gates default to `650` for local testing. `nDigiDollarMuSig2Height = min(650, DigiDollarHeight) = 0` so v0x03 quotes are valid whenever DigiDollar is active. Startup oracle-price reconstruction follows the buried predicate used by block connection, so DD-active default-regtest blocks below 650 are not skipped during restart/reindex cache rebuilds. --- @@ -102,7 +125,7 @@ The DigiDollar/oracle RPC surface is split between the node-context registration **Removed / never present:** `sendoracleprice` was deleted as a fake-price-injection vulnerability; `submitoracleprice` does not exist anywhere in the source tree. Oracle prices come exclusively from live exchange aggregation aggregated under MuSig2. `src/rpc/digidollar_transactions.cpp` declares `getdigidollarinfo`, `transferdigidollar`, `createrawddtransaction`, and `listredeemablepositions`, but the file is **not registered** anywhere — treat it as legacy/unused. -**Gate pattern:** DD price/position/transaction/oracle-operation RPCs call `DigiDollar::IsDigiDollarEnabled(tip, chainman)` near the top of their handler. That helper checks the BIP9 `DEPLOYMENT_DIGIDOLLAR` state via `DeploymentActiveAfter()`. Local wallet key-management RPCs (`createoraclekey`, `exportoracleprivkey`, `importoracleprivkey`) and the deployment-status probe are intentionally usable before activation. +**Gate pattern:** DD price/position/transaction/oracle-operation RPCs call `DigiDollar::IsDigiDollarEnabled(tip, chainman)` near the top of their handler. That helper checks the buried `DEPLOYMENT_DIGIDOLLAR` activation height via `DeploymentActiveAfter()` (BIP90 since v9.26.5). Local wallet key-management RPCs (`createoraclekey`, `exportoracleprivkey`, `importoracleprivkey`) and the deployment-status probe are intentionally usable before activation. ### P2P Message Handlers @@ -120,20 +143,20 @@ The DigiDollar/oracle RPC surface is split between the node-context registration | `oraclehb` | `NetMsgType::ORACLEHEARTBEAT` | line 6175 | Signed oracle software/protocol heartbeat; telemetry, not a price input | | `getoracles` | `NetMsgType::GETORACLES` | line 6262 | Pull request for missing oracle messages; replies with fresh `oracleprice` messages and recent `oraclehb` heartbeats | -**Note:** The price/consensus/MuSig2/getoracles gates use `Consensus::IsOracleActive(params, ActiveChain().Height())`, which is `nHeight >= params.nOracleActivationHeight`. On mainnet and testnet `nOracleActivationHeight` equals `nDDActivationHeight` and the BIP9 `min_activation_height`; on default regtest BIP9 is `ALWAYS_ACTIVE` at min height 0 while the P2P height gates default to 650. A peer sending gated oracle messages before the height gate is silently ignored — no ban, no misbehaviour penalty — just dropped at the start of each handler. +**Note:** The price/consensus/MuSig2/getoracles gates use `Consensus::IsOracleActive(params, ActiveChain().Height())`, which is `nHeight >= params.nOracleActivationHeight`. On mainnet and testnet `nOracleActivationHeight` equals `nDDActivationHeight` (the historical BIP9 floor — mainnet 23,627,520, below the 23,869,440 buried activation height); on default regtest the buried `DigiDollarHeight` is 0 while the P2P height gates default to 650. A peer sending gated oracle messages before the height gate is silently ignored — no ban, no misbehaviour penalty — just dropped at the start of each handler. > **AUDIT NOTE:** As of the current code, `NetMsgType::ORACLEHEARTBEAT` uses `IsOracleP2PActive`, the same top-level activation helper used by the price, consensus, MuSig2, and `getoracles` handlers. It is also restricted to active consensus-roster oracle IDs, Schnorr-authenticated against chainparams, capped at 240 messages per peer per hour, deduplicated, and only returned by `getoracles` when recent. -> **Caveat.** The height predicate `IsOracleActive` is independent of the BIP9 deployment view (`getdeploymentinfo.bip9.status`). On production they collapse to the same trigger because chainparams pins `nOracleActivationHeight == nDDActivationHeight == BIP9 min_activation_height`. There is a single-block boundary window described in the Phase 4 nuance above where the BIP9 view says `active` but the height gate is still `false`. On default regtest, BIP9 `ALWAYS_ACTIVE` means block validation can be DD-active before the 650 P2P height gate; use `IsOracleActive` and `IsDigiDollarEnabled` deliberately for boundary tests when the P2P moment and consensus-rule moment differ. +> **Caveat (v9.26.5).** The height predicate `IsOracleActive` is independent of the buried deployment predicate (`IsDigiDollarEnabled`). On mainnet the static gates (23,627,520) sit below the buried activation height (23,869,440), and on default regtest the buried height (0) sits below the 650 P2P gates — in both cases `IsOracleP2PActive` requires BOTH predicates, so the oracle P2P surface only opens once the later of the two boundaries is passed. Use `IsOracleActive` and `IsDigiDollarEnabled` deliberately for boundary tests when the P2P moment and consensus-rule moment differ. -### Consensus Validation (all BIP9-gated) +### Consensus Validation (all activation-gated) 1. **Mempool acceptance** (`src/validation.cpp:976-989`): `DigiDollar::HasDigiDollarMarker(tx)` + `IsDigiDollarEnabled()` → rejects DD TXs with `TX_CONSENSUS "digidollar-not-active"`. After activation, mempool acceptance also requires that an oracle quote is available for any DD transaction (commit `81bf974f40`). -2. **Block validation** (`src/validation.cpp:2816-2854`): `DeploymentActiveAt(DEPLOYMENT_DIGIDOLLAR)` during `ConnectBlock()` delegates to `DeploymentActiveAfter(index.pprev, ...)`, so the candidate block is judged using the previous block's BIP9 state. Blocks containing DD TXs before activation are rejected. After activation, `ValidateBlockOracleData` (`src/oracle/bundle_manager.cpp:2151`) requires DD mint/redeem blocks to carry exactly one v0x03 MuSig2 oracle bundle in the coinbase. DD transfer-only and non-DD blocks may omit oracle data; if any block includes one it must still be a valid v0x03 bundle (commit `1e08bd811f`). +2. **Block validation** (`src/validation.cpp:2816-2854`): `DeploymentActiveAt(DEPLOYMENT_DIGIDOLLAR)` during `ConnectBlock()` delegates to `DeploymentActiveAfter(index.pprev, ...)`, so the candidate block is judged by comparing the previous block's height + 1 against the buried activation height (v9.26.5). Blocks containing DD TXs before activation are rejected. After activation, `ValidateBlockOracleData` (`src/oracle/bundle_manager.cpp:2151`) requires DD mint/redeem blocks to carry exactly one v0x03 MuSig2 oracle bundle in the coinbase. DD transfer-only and non-DD blocks may omit oracle data; if any block includes one it must still be a valid v0x03 bundle (commit `1e08bd811f`). 3. **Script verification** (`validation.cpp` script-flag setup): `SCRIPT_VERIFY_DIGIDOLLAR` flag only set when `DeploymentActiveAt()` returns true, so the Tapscript OP_SUCCESSx-class DD opcodes are not interpreted as DigiDollar operations before activation. Once active, `OP_CHECKPRICE` is reserved and deterministically disabled (`src/script/interpreter.cpp:708-735`): it consumes one stack item and pushes false rather than reading node-local oracle state. 4. **Mining graceful degradation** (`src/node/miner.cpp`, commit `6b5ff516c3`): `CreateNewBlock` strips price-dependent DD mint/redeem txs when no valid oracle bundle is available rather than aborting block assembly. Transfer-only DD txs are validated with oracle-price validation skipped because they do not need a block oracle price. The block is still produced; rejected DD txs remain in the mempool until either they confirm in a later attempt or are evicted. -Historical validation, IBD, and reorg handling follow the same predicates. `ValidateBlockOracleData()` returns true before the historical activation state, startup price-cache reconstruction only loads blocks that are BIP9-active for their historical context, IBD/catch-up skips oracle-dependent DD validation where the code cannot safely re-evaluate old wall-clock freshness with current state, and `DisconnectBlock()` removes the connected block's price-cache entry during reorg. +Historical validation, IBD, and reorg handling follow the same predicates. `ValidateBlockOracleData()` returns true before the historical activation state, startup price-cache reconstruction only loads blocks that pass the same activation predicate for their historical context, IBD/catch-up skips oracle-dependent DD validation where the code cannot safely re-evaluate old wall-clock freshness with current state, and `DisconnectBlock()` removes the connected block's price-cache entry during reorg. ### Qt GUI @@ -143,9 +166,11 @@ Historical validation, IBD, and reorg handling follow the same predicates. `Vali --- -## Miner Signaling — How It Works +## Miner Signaling — How It Works (historical) -### Why miners signal automatically +Post-burial (v9.26.5) miners no longer signal: `ComputeBlockVersion` never sets bits 2/23/0, `getblocktemplate` advertises `taproot`/`digidollar`/`algolock` as plain `rules` entries when active, and `vbavailable` no longer mentions them. The description below is how signaling worked during the live BIP9 deployment. + +### Why miners signaled automatically The `VBDeploymentInfo` for DigiDollar has `gbt_force = true` (in `src/deploymentinfo.cpp`). This means: @@ -167,32 +192,30 @@ During STARTED/LOCKED_IN, blocks should have version `0x20800004` or similar (wi --- -## Testing BIP9 Activation +## Testing Activation + +> **v9.26.5:** the BIP9 lifecycle/signaling test phases (state ladder, threshold math, timeout/FAILED) are unrepresentable post-burial and were replaced with buried-boundary equivalents; the tests keep their file names. ### Functional Tests -1. **`digidollar_activation.py`** — Tests the full activation lifecycle: - - Mines through DEFINED → STARTED → LOCKED_IN → ACTIVE - - Verifies DD RPCs fail before activation, work after +1. **`digidollar_activation.py`** — Tests the activation boundary: + - Verifies DD RPCs fail before the buried activation height, work after - Tests DD minting, sending, redeeming after activation - - Checks version bits in block headers 2. **`digidollar_activation_boundary.py`** — Tests edge cases: - - Exact block boundaries between phases - - Threshold calculation (exactly 140/200) - - Below-threshold signaling (remains STARTED) - - min_activation_height enforcement + - Exact activation-height boundary (off-by-one on either side) + - Pre-activation DD rejection and reorg-below-activation behavior ### Manual Testing Checklist Before activation (any block < 600): - [ ] DD price/position/transaction/oracle-operation RPCs return "DigiDollar is not yet active on this blockchain" - [ ] Local oracle key-management RPCs (`createoraclekey`, `exportoracleprivkey`, `importoracleprivkey`) remain usable for pre-activation operator setup/recovery -- [ ] `getdeploymentinfo` shows correct BIP9 state +- [ ] `getdeploymentinfo` shows the `digidollar` entry as `{"type":"buried","active":false,"height":N}` (v9.26.5) - [ ] Qt DD tab shows activation overlay - [ ] No oracle messages processed (check debug.log; `IsOracleActive` returns false) - [ ] DD transactions rejected from mempool with "digidollar-not-active" -- [ ] Block version includes bit 23 after STARTED (block 200+) +- [ ] Block versions do NOT signal bit 23 (buried deployment — no signaling) After activation (block 600+): - [ ] DD RPCs functional @@ -207,9 +230,9 @@ After activation (block 600+): --- -## Mainnet Activation Timeline +## Mainnet Activation Timeline (historical — completed) -On mainnet, the process is: +This is the process as it actually completed on mainnet. DigiDollar locked in and activated at block **23,869,440** (July 2026, six confirmation windows above the 23,627,520 floor), and the deployment was buried in v9.26.5. Historically, the process was: 1. **Release:** Publish binaries with DigiDollar code and BIP9 deployment 2. **Upgrade period:** Miners and nodes upgrade (BIP9 start time: June 1, 2026) @@ -219,7 +242,7 @@ On mainnet, the process is: 6. **Activation:** Block height reaches `min_activation_height` (23,627,520) and BIP9 is ACTIVE 7. **DigiDollar live:** All DD functionality enabled across the network -**Timeout:** If 70% signaling is not reached by June 1, 2027, the deployment transitions to FAILED. A new deployment with different parameters would be needed. +**Timeout:** If 70% signaling had not been reached by June 1, 2027, the deployment would have transitioned to FAILED and a new deployment with different parameters would have been needed. This never happened — activation succeeded, and post-burial the FAILED state no longer exists for this deployment. --- @@ -231,9 +254,9 @@ On mainnet, the process is: 3. **Oracle P2P safety:** Price, consensus, MuSig2, `getoracles`, and signed `oraclehb` heartbeat messages received before `IsOracleP2PActive` are silently dropped (not banned). -4. **Consensus safety:** Mempool policy rejects DD-marker transactions before activation, but block consensus preserves base-chain compatibility: pre-activation DD-looking markers are treated as ordinary DGB data and DigiDollar semantics are not applied until BIP9 is ACTIVE. +4. **Consensus safety:** Mempool policy rejects DD-marker transactions before activation, but block consensus preserves base-chain compatibility: pre-activation DD-looking markers are treated as ordinary DGB data and DigiDollar semantics are not applied until the deployment is active (buried height since v9.26.5). -5. **BIP9 guarantees:** The activation mechanism is the same one Bitcoin used for SegWit. It's battle-tested across multiple blockchains and provides clear upgrade coordination. +5. **BIP9 guarantees (historical):** The activation mechanism was the same one Bitcoin used for SegWit — battle-tested across multiple blockchains, providing clear upgrade coordination. Having served that purpose, the completed deployment is now buried (BIP90, v9.26.5), exactly as Bitcoin buried CSV and SegWit after their activations. --- @@ -247,15 +270,17 @@ Both heights are intentionally aligned in `src/kernel/chainparams.cpp`: | Testnet26 | `600` | `600` | `600` | | Regtest | `650` | `650` | `0` | -A practical implication: there is no period in which the oracle P2P surface is live but DD itself is not, and there is no period in which DD is active but MuSig2 v0x03 is not yet the on-chain bundle format. On mainnet/testnet the three numeric heights collapse to one event; on default regtest MuSig2 follows the BIP9 `ALWAYS_ACTIVE` boundary while DD/oracle height gates remain at 650 for local testing. Documents that say "mainnet `nOracleActivationHeight = 3000000`" are stale; that earlier staging configuration was removed before V1 launch. +A practical implication: there is no period in which the oracle P2P surface is live but DD itself is not (the P2P gate requires both `IsOracleActive` and `IsDigiDollarEnabled`), and there is no period in which DD is active but MuSig2 v0x03 is not yet the on-chain bundle format. On default regtest MuSig2 follows the buried `DigiDollarHeight` boundary (`0`) while DD/oracle height gates remain at 650 for local testing. Documents that say "mainnet `nOracleActivationHeight = 3000000`" are stale; that earlier staging configuration was removed before V1 launch. + +> **v9.26.5 burial note.** These three static gates keep the 23,627,520 mainnet floor even though the deployment is buried at its actual activation height 23,869,440. The floor is what drives `EarliestActivationFloor = min(nDDActivationHeight, DigiDollarHeight)` (prune lock, pre-floor coin gate), while the buried `DigiDollarHeight` is what gates DD consensus/RPC/mempool/script. On mainnet the actual single activation event was block 23,869,440. ## File Reference | Component | File | Function | |-----------|------|----------| -| BIP9 deployment params | `src/kernel/chainparams.cpp` | `vDeployments[DEPLOYMENT_DIGIDOLLAR]` | -| BIP9 state machine | `src/versionbits.cpp` | `AbstractThresholdConditionChecker` / `VersionBitsConditionChecker` | -| Deployment info | `src/deploymentinfo.cpp` | `VersionBitsDeploymentInfo[]` | +| Buried activation heights (v9.26.5) | `src/kernel/chainparams.cpp` / `src/consensus/params.h` | `TaprootHeight` / `DigiDollarHeight` / `AlgoLockHeight`, returned by `Consensus::Params::DeploymentHeight()` | +| Buried activation predicate | `src/deploymentstatus.h` | `DeploymentActiveAfter` / `DeploymentActiveAt` height compares (the BIP9 machinery in `src/versionbits.cpp` now serves only `DEPLOYMENT_TESTDUMMY`) | +| Deployment names | `src/deploymentinfo.cpp` | `DeploymentName(BuriedDeployment)` + `GetBuriedDeployment()` (resolves `-testactivationheight` names); `VersionBitsDeploymentInfo[]` retains only testdummy | | RPC activation gate | `src/rpc/digidollar.cpp` | `IsDigiDollarEnabled()` check in each RPC | | P2P activation gate | `src/net_processing.cpp` | `IsOracleP2PActive()` in `ORACLEPRICE`/`ORACLEBUNDLE`/`ORACLECONSENSUS`/`ORACLEATTESTATION`/`ORACLEMUSIGNONCE`/`ORACLEMUSIGCONTEXT`/`ORACLEMUSIGPARTIALSIG`/`GETORACLES`/`ORACLEHEARTBEAT` | | Mempool gate | `src/validation.cpp:976-989` | `IsDigiDollarEnabled()` in `AcceptToMemoryPool`; recent MuSig2 quote required for DD txs | diff --git a/DIGIDOLLAR_ARCHITECTURE.md b/DIGIDOLLAR_ARCHITECTURE.md index fdf86bc756..797f8fe53d 100644 --- a/DIGIDOLLAR_ARCHITECTURE.md +++ b/DIGIDOLLAR_ARCHITECTURE.md @@ -1158,7 +1158,7 @@ size_t LoadFromDatabase(); // ✅ Working - loads all DD data including UTXOs |----------|---------|--------|-------| | **System Health** | `getdigidollarstats` | ✅ Complete | Network-wide UTXO scanning + system health | | | `getdcamultiplier` | ✅ Complete | DCA multiplier calculations | -| | `getdigidollardeploymentinfo` | ✅ Complete | BIP9 deployment activation info | +| | `getdigidollardeploymentinfo` | ✅ Complete | Buried deployment activation info (v9.26.5: `type`/`status`/`activation_height`; BIP9 signaling fields removed) | | | `getprotectionstatus` | ✅ Complete | DCA/ERR/volatility status | | **Collateral** | `calculatecollateralrequirement` | ✅ Complete | Real-time collateral calculation | | | `estimatecollateral` | ✅ Complete | Quick collateral estimation | diff --git a/DIGIDOLLAR_ORACLE_ARCHITECTURE.md b/DIGIDOLLAR_ORACLE_ARCHITECTURE.md index 0b64c60015..ca13f7dca6 100644 --- a/DIGIDOLLAR_ORACLE_ARCHITECTURE.md +++ b/DIGIDOLLAR_ORACLE_ARCHITECTURE.md @@ -81,7 +81,7 @@ V1 ships with: - ✅ Single validator path on mainnet and testnet (`OracleDataValidator::ValidateBlockOracleData`, `src/oracle/bundle_manager.cpp:2151`) — the prior mainnet short-circuit is gone - ✅ P2P message surface: `oracleprice`, `oraclebundle` (received-and-dropped), `oracleconsns`, `oracleattest`, `oramusnonce`, `oramusigctx`, `oramusigpsig`, `oraclehb`, `getoracles` (`src/protocol.cpp:53-62`, handlers in `src/net_processing.cpp` 5440–6340). All oracle P2P handlers, including `oraclehb`, share the `IsOracleP2PActive` gate. - ✅ Six initialized exchange fetchers (`src/oracle/exchange.cpp:1092-1097`) -- ✅ Block-validated price cache, gated by BIP9 `DEPLOYMENT_DIGIDOLLAR` (`src/validation.cpp:3064-3094, 3365-3372`) +- ✅ Block-validated price cache, gated by the buried `DEPLOYMENT_DIGIDOLLAR` deployment (BIP90 since v9.26.5) (`src/validation.cpp:3064-3094, 3365-3372`) - ✅ BIP-340 Schnorr verification at every relay hop, with bound-from-chainparams pubkey replacement before verification (`src/net_processing.cpp:5462-5491`) so an attacker cannot ship their own pubkey alongside a forged signature **Removed / never-shipped:** @@ -110,7 +110,7 @@ V1 ships with: - Oracle operators fetch and broadcast fresh exchange prices every 60 seconds; chainparams separately control how often block-level oracle prices are accepted per network **If you're running a node:** -- Your node validates the MuSig2 aggregate signature in every DD mint/redeem block once `DEPLOYMENT_DIGIDOLLAR` is BIP9-active and `nHeight >= nOracleActivationHeight`; DD transfer-only and ordinary DGB blocks can omit a coinbase oracle bundle +- Your node validates the MuSig2 aggregate signature in every DD mint/redeem block once `DEPLOYMENT_DIGIDOLLAR` is active (buried height since v9.26.5) and `nHeight >= nOracleActivationHeight`; DD transfer-only and ordinary DGB blocks can omit a coinbase oracle bundle - No setup needed — validation happens automatically; the trust anchor is `consensus.vOraclePublicKeys` in chainparams - Enable `-debug=digidollar` to see oracle activity @@ -155,7 +155,7 @@ Core implementation: ├── src/script/interpreter.{h,cpp} Reserved/disabled OP_CHECKPRICE behavior ├── src/validation.cpp ConnectBlock → ValidateBlockOracleData (right after │ CheckBlock); ConnectBlock price cache; UpdatePriceCache -│ gated on BIP9 DEPLOYMENT_DIGIDOLLAR +│ gated on buried DEPLOYMENT_DIGIDOLLAR (v9.26.5) ├── src/net_processing.cpp P2P handlers (~5440–6340), including heartbeat, are gated │ by IsOracleP2PActive, rate-limited, roster-limited, and │ verified with chainparams pubkey replacement before @@ -167,7 +167,8 @@ Core implementation: └── src/kernel/chainparams.cpp vOracleNodes (35 mainnet/testnet active slots, 7 regtest), vOraclePublicKeys (35 active mainnet/testnet, 7 regtest), nDDActivationHeight, - nOracleActivationHeight, nDigiDollarMuSig2Height, BIP9 params + nOracleActivationHeight, nDigiDollarMuSig2Height, buried + DigiDollarHeight (v9.26.5) Tests (current; counts in REPO_MAP_DIGIDOLLAR.md): ├── src/test/digidollar_*_tests.cpp DD validation, mint, redeem, transfer, P2P, persistence,… @@ -1429,7 +1430,7 @@ bool OracleDataValidator::ValidateBlockOracleData( // 1. Determine height (pindex_prev->nHeight+1, or BIP34 from coinbase scriptSig). int32_t block_height = /* ... */; - // 2. BIP9 / height gate. Pre-activation: skip oracle validation entirely. + // 2. Activation / height gate (buried deployment since v9.26.5). Pre-activation: skip oracle validation entirely. if (pindex_prev) { if (!DigiDollar::IsDigiDollarEnabled(pindex_prev, params)) return true; } else if (block_height < params.nDDActivationHeight) { @@ -1518,13 +1519,13 @@ bool OracleDataValidator::ValidateBlockOracleData( } //═══════════════════════════════════════════════════════════════════ - // STEP 3: BIP9 ACTIVATION CHECK (lines 1590-1599) + // STEP 3: ACTIVATION CHECK (buried deployment since v9.26.5) //═══════════════════════════════════════════════════════════════════ - // Primary: BIP9 deployment check (when pindex_prev available) + // Primary: buried deployment check (when pindex_prev available) // Fallback: Height-based check (when no chain context) if (pindex_prev) { if (!DigiDollar::IsDigiDollarEnabled(pindex_prev, params)) { - return true; // Oracle validation not required before BIP9 activation + return true; // Oracle validation not required before activation } } else { if (block_height < params.nDDActivationHeight) { @@ -1821,7 +1822,7 @@ void OracleBundleManager::RemovePriceCache(int height) ### 14.1 V1 Activation & Quorum Reality (replaces the old Phase Two roadmap) -The "Phase Two roadmap" section that previously occupied this slot is obsolete. Phase 3 / MuSig2 is the only on-chain oracle format in V1, available everywhere from the moment DigiDollar is BIP9-active. The roadmap below has been replaced with the actual configuration the validator uses today. +The "Phase Two roadmap" section that previously occupied this slot is obsolete. Phase 3 / MuSig2 is the only on-chain oracle format in V1, available everywhere from the moment DigiDollar is active (buried deployment since v9.26.5; historically BIP9). The roadmap below has been replaced with the actual configuration the validator uses today. **Code-validated configuration** (`src/kernel/chainparams.cpp`, `src/consensus/params.h`): @@ -1832,7 +1833,7 @@ int nOracleActivationHeight{std::numeric_limits::max()}; int nDigiDollarMuSig2Height{std::numeric_limits::max()}; // Mainnet override (src/kernel/chainparams.cpp): -consensus.nDDActivationHeight = 23627520; // BIP9 min_activation_height +consensus.nDDActivationHeight = 23627520; // historical BIP9 floor; buried DigiDollarHeight = 23869440 (v9.26.5) consensus.nOracleActivationHeight = consensus.nDDActivationHeight; // 23627520 consensus.nDigiDollarMuSig2Height = consensus.nDDActivationHeight; consensus.nOracleRequiredMessages = 7; // off-chain quorum input to MuSig2 @@ -1851,7 +1852,7 @@ consensus.nOracleConsensusRequired = 7; // Regtest override (chainparams.cpp:1112-1119): consensus.nDDActivationHeight = 650; consensus.nOracleActivationHeight = 650; -consensus.nDigiDollarMuSig2Height = 0; // BIP9 ALWAYS_ACTIVE boundary +consensus.nDigiDollarMuSig2Height = 0; // min(650, buried DigiDollarHeight=0) — v9.26.5 burial consensus.nOraclePubkeyCount = 7; consensus.nOracleConsensusRequired = 4; ``` @@ -1950,7 +1951,7 @@ There is no longer a separate "activate Phase Two on testnet" step — testnet/r - v0x03 on-chain payload: `version + bitmap_len + bitmap + epoch + price + timestamp + 64-byte aggregate sig` (`COracleBundle::SerializeV03Data`, `OracleBundleManager::CreateOracleScript`) - Validator: single code path for mainnet/testnet/regtest in `OracleDataValidator::ValidateBlockOracleData` (`src/oracle/bundle_manager.cpp:2151`) - P2P handlers: 9 message types in `src/protocol.cpp:53-62`; price/consensus/MuSig2/getoracles handlers, including `oraclehb`, share the `IsOracleP2PActive` gate in `src/net_processing.cpp` ~5440–6340, and `oraclebundle` is accepted-and-dropped. -- BIP9: bit 23, mainnet start `2026-06-01`, mainnet timeout `2027-06-01`, mainnet `min_activation_height=23627520`, mainnet window 40320 / threshold 28224 (70%); testnet26 start at genesis, `min_activation_height=600`, window 200, threshold 140 (70%); regtest `ALWAYS_ACTIVE` +- Historical BIP9 (deployment buried in v9.26.5 — mainnet activated at 23,869,440, testnet26 at 600, regtest buried height 0): bit 23, mainnet start `2026-06-01`, mainnet timeout `2027-06-01`, mainnet `min_activation_height=23627520`, mainnet window 40320 / threshold 28224 (70%); testnet26 start at genesis, `min_activation_height=600`, window 200, threshold 140 (70%); regtest `ALWAYS_ACTIVE` - `OP_CHECKPRICE` is reserved and deterministically disabled (`src/script/interpreter.cpp:708-735`); it consumes one operand and pushes false without reading oracle state - 6 active exchange fetchers initialized in `MultiExchangeAggregator::InitializeFetchers` (`src/oracle/exchange.cpp:1092-1097`); 5 fetcher classes still compile but are NOT initialized (Coinbase, Kraken, Messari, Bittrex, Poloniex); CoinMarketCap removed entirely. `FetchAllPrices()` iterates the initialized fetchers sequentially. - `min_required_sources = 2` is the `MultiExchangeAggregator` header default (`src/oracle/exchange.h:235`); the production caller `OracleNode::FetchMedianPrice` raises the floor to 3 via `SetMinRequiredSources(3)` (`src/oracle/node.cpp:450`), so the live oracle daemon publishes only when >=3 of the 6 fetchers respond. Outlier filtering removes prices more than 10% from the median and aggregation requires the source floor before and after filtering. @@ -1962,19 +1963,20 @@ Validation range: 100 - 100,000,000 micro-USD ($0.0001 - $100.00) On-chain bundle size: 86-byte minimum v0x03 data; 90 bytes on the 35-slot mainnet/testnet bitmap Off-chain attestation: 128-byte COraclePriceMessage (32-byte XOnly pubkey + 64-byte Schnorr) Quorum: 7 signatures from the configured active mainnet/testnet keyset, 4-of-7 regtest -Activation heights: Mainnet 23627520, testnet26 600, regtest DD/oracle height gates 650 with MuSig2 at BIP9 boundary 0 by default +Activation heights: Buried DigiDollarHeight mainnet 23869440 (static DD/oracle gates at the 23627520 floor), testnet26 600, regtest buried 0 with DD/oracle height gates 650 and MuSig2 0 by default (v9.26.5 burial) ``` -Regtest note: the default BIP9 deployment is `ALWAYS_ACTIVE` with -`min_activation_height=0`, while the DD/oracle P2P height gates default to -650. `nDigiDollarMuSig2Height` follows the BIP9 boundary so v0x03 quotes are -valid whenever DigiDollar is active. The direct `-digidollaractivationheight=N` -knob retargets BIP9 and the DD/oracle/MuSig2 gates; generic -`-vbparams=digidollar:...` remains a BIP9-only override, but MuSig2 validation -follows that effective DigiDollar BIP9 boundary while the static DD/oracle P2P -gates remain unchanged. Startup oracle-price cache reconstruction follows the -BIP9 predicate used by block connection, so default-regtest BIP9-active oracle -bundles below 650 are not skipped on restart/reindex. +Regtest note (v9.26.5 burial): the default buried `DigiDollarHeight` is `0`, +while the DD/oracle P2P height gates default to 650. +`nDigiDollarMuSig2Height = min(nDDActivationHeight, DigiDollarHeight) = 0` so +v0x03 quotes are valid whenever DigiDollar is active. The direct +`-digidollaractivationheight=N` knob retargets the buried height and the +DD/oracle/MuSig2 gates together (DD activates at exactly N); +`-testactivationheight=digidollar@H` moves only the buried deployment height, +and `-vbparams=digidollar:...` is now a startup error. Startup oracle-price +cache reconstruction follows the same buried predicate used by block +connection, so default-regtest DD-active oracle bundles below 650 are not +skipped on restart/reindex. ## Historical Issue Tracker (resolved in V1) diff --git a/REPO_MAP.md b/REPO_MAP.md index ced7322c0c..f3fc44e61d 100644 --- a/REPO_MAP.md +++ b/REPO_MAP.md @@ -841,14 +841,14 @@ - `multiAlgoDiffChangeTarget` / `alwaysUpdateDiffChangeTarget` / `workComputationChangeTarget` / `algoSwapChangeTarget` → DigiByte multi-algo / DigiShield / DigiSpeed / Odo activation heights - `OdoHeight` / `nOdoShapechangeInterval` → Odocrypt activation height + 10-day key rotation interval - `nMinerConfirmationWindow` / `nRuleChangeActivationThreshold` → BIP9 window/threshold - - `vDeployments[]` (BIP9): includes `DEPLOYMENT_TESTDUMMY`, `DEPLOYMENT_TAPROOT` (bit 2), and ⚠️ `DEPLOYMENT_DIGIDOLLAR` (bit 23, gates `SCRIPT_VERIFY_DIGIDOLLAR`) + - `vDeployments[]` (BIP9): only `DEPLOYMENT_TESTDUMMY` remains since the v9.26.5 burial; Taproot/DigiDollar/AlgoLock are buried heights `TaprootHeight` / ⚠️ `DigiDollarHeight` (gates `SCRIPT_VERIFY_DIGIDOLLAR`) / `AlgoLockHeight` - ⚠️ `nDDActivationHeight` / `nOracleActivationHeight` / `nDigiDollarMuSig2Height` → DigiDollar / oracle / MuSig2 v0x03 activation heights - ⚠️ `nDDOracleEpochBlocks` / `nDDOracleUpdateInterval` / `nOracleEpochLength` / `nOracleRequiredMessages` / `nOracleTotalOracles` → oracle system parameters - ⚠️ `nOraclePubkeyCount` / `nOracleConsensusRequired` → MuSig2 quorum sizing (mainnet/testnet 35 active keys and 7 signatures required) - ⚠️ `vOraclePublicKeys` → hardcoded oracle x-only Schnorr keys (slot order matches MuSig2 participation bitmap) - ⚠️ `IsMuSig2OracleActive(height)` → inline helper returning `height >= nDigiDollarMuSig2Height` -- `BuriedDeployment` enum → activation heights for BIP34, BIP65, BIP66, CSV, SegWit, NVERSIONBIPS, RESERVEALGO, Odocrypt -- `DeploymentPos` enum (`DEPLOYMENT_TESTDUMMY`, `DEPLOYMENT_TAPROOT`, ⚠️ `DEPLOYMENT_DIGIDOLLAR`) +- `BuriedDeployment` enum → activation heights for BIP34, BIP65, BIP66, CSV, SegWit, NVERSIONBIPS, RESERVEALGO, Odocrypt, and (since the v9.26.5 burial) Taproot, ⚠️ DigiDollar, AlgoLock via `DeploymentHeight()` +- `DeploymentPos` enum (`DEPLOYMENT_TESTDUMMY` only since the v9.26.5 burial; `DEPLOYMENT_TAPROOT` / ⚠️ `DEPLOYMENT_DIGIDOLLAR` / `DEPLOYMENT_ALGOLOCK` moved to `BuriedDeployment`) - `BIP9Deployment` (struct) with `bit`, `nStartTime`, `nTimeout`, `min_activation_height`, `ALWAYS_ACTIVE`/`NEVER_ACTIVE`/`NO_TIMEOUT` sentinels - ⚠️ `IsOracleActive(params, height)` → free function returning `height >= params.nOracleActivationHeight` - ⚠️ `IsMuSig2Active(params, height)` → wrapper around `Params::IsMuSig2OracleActive` diff --git a/REPO_MAP_DIGIDOLLAR.md b/REPO_MAP_DIGIDOLLAR.md index 5632956a7b..ed9fddd7c4 100644 --- a/REPO_MAP_DIGIDOLLAR.md +++ b/REPO_MAP_DIGIDOLLAR.md @@ -39,12 +39,12 @@ This is the granular file index for all DigiDollar and Oracle source code. Read - `GetRequiredDDForRedemption(systemCollateral)` → normal: returns ddMinted; ERR: returns (ddMinted × 100) / systemCollateral - `AddRedemptionPath(path)` → adds path to availablePaths if not already present - `HasRedemptionPath(path)` → checks if specific redemption path is available -- `DigiDollar::IsDigiDollarEnabled(pindexPrev, chainman)` → checks BIP9 DEPLOYMENT_DIGIDOLLAR activation via DeploymentActiveAfter -- `DigiDollar::IsDigiDollarEnabled(pindexPrev, params)` → overload using consensus params + temporary VersionBitsCache +- `DigiDollar::IsDigiDollarEnabled(pindexPrev, chainman)` → checks buried DEPLOYMENT_DIGIDOLLAR activation via DeploymentActiveAfter (BIP90 since v9.26.5) +- `DigiDollar::IsDigiDollarEnabled(pindexPrev, params)` → overload using consensus params — pure height compare against `DeploymentHeight(DEPLOYMENT_DIGIDOLLAR)` (no VersionBitsCache since the v9.26.5 burial) ### src/digidollar/digidollar.cpp - Implementation of all `CDigiDollarOutput` and `CCollateralPosition` methods -- `DigiDollar::IsDigiDollarEnabled()` → both overloads delegate to `DeploymentActiveAfter` for BIP9 checking +- `DigiDollar::IsDigiDollarEnabled()` → both overloads reduce to the buried-height predicate (v9.26.5 burial) ### src/digidollar/health.h - `DigiDollar::SystemMetrics` (struct) → aggregates all system-wide DD health data: supply, collateral, per-tier breakdown, DCA/ERR/volatility status, oracle status @@ -764,10 +764,10 @@ Files outside the DigiDollar/Oracle directories that contain DD integration code - `EncodeDigiDollarAddress(dest)` / `DecodeDigiDollarAddress(str)` → free functions for DD address conversion ### src/chainparams.cpp -- ⚠️ Handles `-digidollaractivationheight` CLI arg for regtest, sets BIP9 DEPLOYMENT_DIGIDOLLAR parameters +- ⚠️ Handles `-digidollaractivationheight` CLI arg for regtest; since the v9.26.5 burial it sets the buried `DigiDollarHeight` plus the static DD/oracle/MuSig2 gates (DD activates at exactly N) ### src/validation.cpp / src/validation.h -- ⚠️ `MemPoolAccept::PreChecks` → checks `DigiDollar::HasDigiDollarMarker()`, verifies BIP9 activation via `IsDigiDollarEnabled()`, creates `ValidationContext` with oracle price from `GetOraclePriceForTransaction()`, calls `ValidateDigiDollarTransaction()` +- ⚠️ `MemPoolAccept::PreChecks` → checks `DigiDollar::HasDigiDollarMarker()`, verifies buried-deployment activation via `IsDigiDollarEnabled()`, creates `ValidationContext` with oracle price from `GetOraclePriceForTransaction()`, calls `ValidateDigiDollarTransaction()` - ⚠️ `ConnectBlock` → same DD validation during block connection with `skipOracleValidation` for historical blocks, includes `txLookup` callback for block-db DD amount extraction - ⚠️ `GetBlockScriptFlags` → sets `SCRIPT_VERIFY_DIGIDOLLAR` flag when DEPLOYMENT_DIGIDOLLAR is active - ⚠️ `DisconnectBlock` → calls `RemovePriceCache()` to revert oracle price data @@ -801,13 +801,13 @@ Files outside the DigiDollar/Oracle directories that contain DD integration code - ⚠️ Calls `OracleBundleManager::AddOracleBundleToBlock()` to embed oracle data in coinbase during block assembly ### src/consensus/tx_check.cpp -- ⚠️ `CheckTransaction` → defers DD validation to ConnectBlock (context-free, cannot check BIP9 activation); 0-value P2TR outputs pass the `nValue >= 0` consensus check +- ⚠️ `CheckTransaction` → defers DD validation to ConnectBlock (context-free, cannot check deployment activation); 0-value P2TR outputs pass the `nValue >= 0` consensus check ### src/consensus/tx_verify.cpp - ⚠️ Skips DD-specific validation that requires chain state (deferred to ConnectBlock) ### src/consensus/params.h -- ⚠️ `Consensus::DEPLOYMENT_DIGIDOLLAR` BIP9 deployment definition +- ⚠️ `Consensus::DEPLOYMENT_DIGIDOLLAR` buried deployment (BIP90 since v9.26.5): `BuriedDeployment` enumerator + `DigiDollarHeight` via `DeploymentHeight()`; formerly a BIP9 `vDeployments` entry ### src/policy/policy.cpp - ⚠️ `IsStandardTx` → exempts DD transactions from standard dust/size checks via version marker detection @@ -820,7 +820,7 @@ Files outside the DigiDollar/Oracle directories that contain DD integration code - ⚠️ `OracleVersionHeartbeatMsg` (class) → signed oracle software/protocol heartbeat: version fields, timestamp, nonce, software/subversion strings, chain-bound signature hash ### src/deploymentinfo.cpp -- ⚠️ `DEPLOYMENT_DIGIDOLLAR` name and GBT name registration +- ⚠️ `DEPLOYMENT_DIGIDOLLAR` buried-deployment name registration (`DeploymentName(BuriedDeployment)` + `GetBuriedDeployment()` for `-testactivationheight`); removed from `VersionBitsDeploymentInfo[]` in the v9.26.5 burial ### src/core_write.cpp - ⚠️ DD-aware transaction serialization for `decoderawtransaction` RPC output @@ -934,7 +934,7 @@ present in the tree but not compiled into the current unit-test binary. | File | Coverage Area | |------|--------------| -| `digidollar_activation_tests.cpp` | BIP9 activation logic, height-based feature gating, deployment status checks | +| `digidollar_activation_tests.cpp` | Buried-height activation gating and deployment status checks (BIP9 state-machine cases removed in the v9.26.5 burial) | | `digidollar_activation_wave12_tests.cpp` | Wave 12 activation boundary and deployment predicate regressions | | `digidollar_address_tests.cpp` | DD address encoding/decoding, version bytes, network-specific prefixes, validation | | `digidollar_burn_enforcement_tests.cpp` | Collateral-vault burn enforcement and non-DD spend guard coverage | @@ -1103,7 +1103,7 @@ compatibility but is a legacy/superseded scaffold; the live oracle P2P proof is | File | Coverage Area | |------|--------------| -| `digidollar_activation.py` | Basic BIP9 activation of DD features on regtest | +| `digidollar_activation.py` | Basic activation of DD features at the buried height on regtest (BIP9 signaling lifecycle removed in the v9.26.5 burial) | | `digidollar_activation_boundary.py` | Activation edge cases: exact height, off-by-one, pre/post activation behavior | | `digidollar_activation_multinode.py` | Multi-node activation state and deployment synchronization | | `digidollar_basic.py` | End-to-end DD workflow: mint → transfer → redeem on regtest | diff --git a/doc/release-notes/release-notes-9.26.5.md b/doc/release-notes/release-notes-9.26.5.md new file mode 100644 index 0000000000..505f5addb3 --- /dev/null +++ b/doc/release-notes/release-notes-9.26.5.md @@ -0,0 +1,179 @@ +DigiByte Core version 9.26.5 +============================ + +DigiByte Core v9.26.5 is a patch release on top of v9.26.4. It ships two changes: + +1. **A startup fix:** the DigiDollar oracle startup scan no longer hangs the node + for ~15 minutes on mainnet — it now completes in a few seconds. +2. **Deployment burial (BIP90):** the Taproot, DigiDollar, and AlgoLock soft + forks — all three now ACTIVE on mainnet — are converted from live BIP9 + version-bits deployments into **buried deployments** with hardcoded + activation heights, the same housekeeping Bitcoin Core performed for CSV and + SegWit after their activations. + +Neither change alters which blocks or transactions are valid on the current +mainnet or testnet chains. The burial does change several RPC output shapes and +regtest options — see "Breaking interface changes" below before upgrading +anything that parses deployment status. + + +How to Upgrade +============== + +Shut down DigiByte Core, replace the binaries, and restart. A reindex is **not** +required, and no configuration changes are needed for normal (full or pruned) +nodes. Wallets, mining, pruning, and oracle operation continue to work as in +v9.26.4. + + +Notable changes +=============== + +DigiDollar oracle startup hang fixed (~15 minutes → ~3 seconds) +--------------------------------------------------------------- + +On startup the node rebuilds its in-memory oracle price and volatility cache by +scanning the last 172,800 blocks (~30 days). In v9.26.4, every scanned block +re-evaluated the DigiDollar activation gate through an overload of +`IsDigiDollarEnabled` that allocated a *throwaway* versionbits cache and re-ran +the BIP9 threshold state machine from scratch — an O(window) walk per block, +~172,800 times, with nothing memoized. On mainnet this took ~15 minutes, held +`cs_main` before RPC warmup finished (so RPC returned "Verifying blocks..." +/ error -28 and P2P stalled the whole time), and spammed 150k+ per-block log +lines. + +The fix (commit `3ec927b8b4`) routes the startup per-block gate through the +node's shared, memoized versionbits cache — the exact same lookup `ConnectBlock` +uses — making it O(1) amortized, and replaces the per-block skip logging with a +single aggregate count. This is a pure performance change with no consensus +impact: unit and functional tests prove the startup gate computes the identical +activation boolean at every height, and a restart equals a `-reindex` equals the +pre-restart oracle state. Verified live on mainnet: the oracle startup scan +dropped from ~15 minutes to ~3 seconds. + +With the deployment burial below, the DigiDollar activation check becomes a +plain height comparison, which removes this entire class of cost permanently. + + +Taproot, DigiDollar, and AlgoLock are now buried deployments (BIP90) +-------------------------------------------------------------------- + +All three BIP9 deployments have completed their lifecycle and are ACTIVE on +mainnet: + +| Deployment | BIP9 bit | Mainnet activation height | Testnet26 | Signet / Regtest | +|------------|----------|---------------------------|-----------|------------------| +| Taproot (BIPs 340-342) | 2 | 21,168,000 | 0 (always active) | 0 | +| DigiDollar | 23 | 23,869,440 | 600 | 0 | +| AlgoLock (retired-algo rejection) | 0 | 23,869,440 | 0 (always active) | 0 | + +v9.26.5 "buries" them (BIP90): the activation heights above are now hardcoded +per network in chainparams (`TaprootHeight` / `DigiDollarHeight` / +`AlgoLockHeight`, returned by `Consensus::Params::DeploymentHeight()`), and the +node no longer runs the BIP9 state machine for them. The heights are the +empirically verified BIP9 `since` heights — taken from live mainnet +`getdeploymentinfo` and verified block-by-block on testnet26 (DEFINED 0-199, +STARTED 200-399, LOCKED_IN 400-599, ACTIVE at 600). + +**Why bury them:** + +- The BIP9 state machine no longer serves any purpose for these deployments — + they can never go back to signaling, time out, or fail. Burying removes + per-block BIP9 state recomputation from `ConnectBlock` and startup, and turns + every DigiDollar activation check into a single height comparison. +- Blocks stop signaling bits 2/23/0, so `getblocktemplate` version fields are + clean for pool software (this also structurally resolves the bit-23 + version-rolling interference some SHA256D pool stacks reported). +- It matches upstream practice: Bitcoin Core buried CSV and SegWit the same way + after their activations. + +**What deliberately did NOT change:** + +- The static DigiDollar gates keep their historical floors: + `nDDActivationHeight = nOracleActivationHeight = nDigiDollarMuSig2Height = + 23,627,520` on mainnet (600 on testnet26; 650/650/0 on default regtest). The + 23,627,520 floor is intentionally *below* the 23,869,440 burial height; it + continues to drive the pruning floor, the prune lock, and the pre-floor + collateral gate exactly as in v9.26.4, so existing pruned nodes are + unaffected. +- The AlgoLock static backstop (`nGroestlDeactivationHeight = 23,808,000`) + remains and is still OR'd with the deployment check — retired-algorithm + blocks are rejected over exactly the same height ranges as before. +- Taproot script validation flags were already applied unconditionally (with + the standard exceptions list), so the Taproot burial has zero + script-consensus effect. + +**Consensus compatibility:** a v9.26.5 node accepts and rejects exactly the +same blocks as a v9.26.4 node for every block on the current mainnet and +testnet chains: below the burial heights those chains contain no +deployment-gated content that the BIP9 rules judged differently, and at/above +the burial heights the deployments are active under both versions. The standard +BIP90 caveat applies, as it did for Bitcoin's burials: in the hypothetical of +an (astronomically expensive) deep reorganization back below the burial +heights, a re-mined chain with different signaling could in principle be judged +differently by pre-burial and post-burial nodes. This affects no real chain and +does not change the security model in practice. + +**Versionbits warning ranges:** `MinBIP9WarningHeight` is raised to 23,909,760 +on mainnet (DigiDollar/AlgoLock activation height + one 40,320-block +confirmation window) and 800 on testnet26, so the historical bit-2/23/0 +signaling periods do not trigger spurious "unknown new rules activated" +warnings now that upgraded nodes no longer set those bits. + + +Breaking interface changes +========================== + +RPC +--- + +- **`getdigidollardeploymentinfo`** has a new shape. It now returns `enabled`, + `type` (always `"buried"`), `status` (`"active"` or `"defined"`), + `activation_height` (the burial height; omitted only if the deployment is + disabled on the network), plus the unchanged oracle/MuSig2 fields + (`oracle_activation_height`, `musig2_format_activation_height`, + `oracle_pubkey_count`, `oracle_consensus_required`, `oracle_total_slots`, + `oracle_seed_peers`, `musig2_session{...}`). The BIP9 fields are **removed**: + `bit`, `start_time`, `timeout`, `min_activation_height`, + `blocks_until_timeout`, `signaling_blocks`, `threshold`, `period_blocks`, + `progress_percent`. `status` can no longer be `started`, `locked_in`, or + `failed`. Note `activation_height` now reports the exact burial height + (previously the back-scan reported the first active block of the tip's chain, + which could differ by one). +- **`getdeploymentinfo` / `getblockchaininfo`**: the `taproot`, `digidollar`, + and `algolock` entries are now rendered as buried deployments — + `{"type": "buried", "active": , "height": }` — with **no `bip9` + sub-object**. Anything reading `deployments.digidollar.bip9.status` must + switch to `active`/`height`. +- **`getblocktemplate`**: `rules` now always contains `"taproot"`, + `"digidollar"`, and `"algolock"` once active (hardcoded, like `"csv"`; no + `!` prefix, so clients that do not understand them may safely proceed). + `vbavailable` no longer mentions them, and the template `version` never sets + bits 2, 23, or 0. Block versions on the network stop signaling those bits. + +Command-line options (regtest) +------------------------------ + +- **`-vbparams=taproot:...`, `-vbparams=digidollar:...`, and + `-vbparams=algolock:...` are now a startup error** ("Invalid deployment"). + Only `testdummy` remains a versionbits deployment. Regtest scripts using + these must migrate to `-testactivationheight` (below). +- **`-digidollaractivationheight=N` now activates DigiDollar at exactly height + N.** It sets the buried `DigiDollarHeight` together with the static + DD/oracle/MuSig2 gates. Pre-burial, this knob ran real BIP9 signaling and + DigiDollar only became active at the first 144-block window boundary >= + max(432, N) — e.g. N=650 used to activate at 720. Tests and scripts that + relied on the old window-boundary timing must be updated. +- **New: `-testactivationheight=taproot@H` / `digidollar@H` / `algolock@H`** + set only the buried deployment height (the static DD/oracle/MuSig2 gates keep + their regtest defaults). For DigiDollar, `-digidollaractivationheight` takes + precedence when both are given. + + +Credits +======= + +Thanks to the node operators and pool engineers who reported the slow mainnet +startup and the versionbit signaling quirks, and to everyone who verified the +activation heights on the live mainnet and testnet26 chains ahead of the +burial. diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 9a5263f3a1..18e44eb419 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -234,6 +234,7 @@ DIGIBYTE_TESTS =\ test/dbwrapper_tests.cpp \ test/dandelion_tests.cpp \ test/denialofservice_tests.cpp \ + test/deployment_burial_tests.cpp \ test/descriptor_tests.cpp \ test/flatfile_tests.cpp \ test/fs_tests.cpp \ diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 57deaf19fa..51037ee67d 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -96,17 +96,18 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti } } - // Handle DigiDollar specific activation height for regtest - // Uses real BIP9 signaling (not ALWAYS_ACTIVE) so the deployment goes through - // the full DEFINED → STARTED → LOCKED_IN → ACTIVE state machine. + // Handle DigiDollar specific activation height for regtest. + // DigiDollar is a buried deployment (BIP90): the knob retargets the buried + // activation height together with the static DD/oracle/MuSig2 height gates, + // so DigiDollar activates at exactly this height. (Pre-burial this knob ran + // the real BIP9 state machine, which activated at the first 144-block + // window boundary >= max(432, N).) if (auto digidollar_height = args.GetIntArg("-digidollaractivationheight")) { - CChainParams::VersionBitsParameters vbparams{}; - vbparams.start_time = 0; // Epoch 0: start signaling immediately (MTP will exceed this from block 1) - vbparams.timeout = Consensus::BIP9Deployment::NO_TIMEOUT; - vbparams.min_activation_height = *digidollar_height; - options.version_bits_parameters[Consensus::DEPLOYMENT_DIGIDOLLAR] = vbparams; - options.digidollar_activation_height = *digidollar_height; - LogPrintf("Setting DigiDollar activation height for regtest to %d (BIP9 signaling mode)\n", *digidollar_height); + if (*digidollar_height < 0 || *digidollar_height >= std::numeric_limits::max()) { + throw std::runtime_error(strprintf("Invalid height value (%d) for -digidollaractivationheight.", *digidollar_height)); + } + options.digidollar_activation_height = static_cast(*digidollar_height); + LogPrintf("Setting DigiDollar activation height for regtest to %d (buried deployment)\n", *digidollar_height); } } diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 3c44168618..fd8b6505ea 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -15,7 +15,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: main, test, signet, regtest", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " "This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv, taproot, digidollar, algolock). Note: digidollar@height moves only the buried deployment height; -digidollaractivationheight also retargets the static DD/oracle/MuSig2 gates and takes precedence. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); diff --git a/src/consensus/digidollar.cpp b/src/consensus/digidollar.cpp index 63c462895d..496e47e59a 100644 --- a/src/consensus/digidollar.cpp +++ b/src/consensus/digidollar.cpp @@ -9,21 +9,24 @@ #include