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..0defb856ad 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 — the static DD/oracle gates keep their defaults, but `nDigiDollarMuSig2Height` is derived as `min(nDDActivationHeight, DigiDollarHeight)` and so follows `digidollar@H` below 650; `-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/RELEASE_v9.26.5.md b/RELEASE_v9.26.5.md new file mode 100644 index 0000000000..9c4b870a9b --- /dev/null +++ b/RELEASE_v9.26.5.md @@ -0,0 +1,349 @@ +# DigiByte Core v9.26.5 DigiDollar Release Notes + +DigiByte Core v9.26.5 is a patch release on top of v9.26.4 in the formal +DigiByte v9 / DigiDollar mainnet release line, and it is the **first release +after DigiDollar activated on mainnet**. DigiDollar locked in through BIP9 +miner signaling and became active at block **23,869,440** on 17 July 2026, +alongside the AlgoLock retired-algorithm rule. Taproot has been active since +block 21,168,000. + +v9.26.5 contains 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 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. + +--- + +## Changes In v9.26.5 Since v9.26.4 + +- Fixed the ~15-minute DigiDollar oracle startup hang on mainnet (RPC error + -28 / "Verifying blocks…" / stalled P2P while the node started). Startup is + now a few seconds. +- Buried the Taproot, DigiDollar, and AlgoLock deployments (BIP90): activation + is now a fixed per-network height; the BIP9 state machine no longer runs for + them and blocks no longer signal bits 2, 23, or 0. +- `getdigidollardeploymentinfo` has a new, simpler "buried" output shape (the + BIP9 signaling fields are gone), and `getdeploymentinfo` now reports the + three deployments as `type: "buried"`. +- `getblocktemplate` version fields are permanently clean of deployment + signaling bits; the `rules` array now always lists `taproot`, `digidollar`, + and `algolock` once active. +- Regtest testing knobs changed: `-digidollaractivationheight=N` now activates + DigiDollar at exactly height N; new + `-testactivationheight=taproot|digidollar|algolock@HEIGHT` overrides exist; + `-vbparams` for the three buried names is now a startup error. +- Versionbits warning heights raised so historical signaling does not trigger + spurious "unknown new rules" warnings. +- Documentation updated across the DigiDollar doc set; full release notes in + `doc/release-notes/release-notes-9.26.5.md`; new unit and functional tests + pin the burial heights and boundary behavior. + +--- + +## Read This First + +**DigiDollar is live on mainnet.** There is nothing left to signal and nothing +to configure: any v9.26.5 node on mainnet enforces DigiDollar, AlgoLock, and +Taproot from their recorded activation heights. + +Upgrading is drop-in: shut down, replace binaries, restart. **No reindex is +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. + +Mainnet is normal mainnet: + +| Setting | Value | +| --- | --- | +| Network | DigiByte mainnet | +| P2P port | `12024` | +| RPC port | `14022` | +| Data directory | default DigiByte mainnet data directory | +| Genesis block | unchanged DigiByte mainnet genesis | + +--- + +## Fix: DigiDollar Oracle Startup Hang (~15 Minutes → Seconds) + +### What Happened + +On startup, a DigiDollar node rebuilds its in-memory oracle price and +volatility state by scanning the last 172,800 blocks (~30 days). In v9.26.4, +every scanned block re-evaluated the DigiDollar activation gate through a code +path that allocated a *throwaway* versionbits cache and re-ran the BIP9 +threshold state machine from scratch — roughly 172,800 times, with nothing +memoized. On mainnet, right after activation, this took ~15 minutes, held the +main lock before RPC warmup finished (so RPC returned error -28, the GUI sat +on "Verifying blocks…", and P2P stalled), and wrote 150,000+ per-block log +lines. + +### The Fix + +The startup per-block gate now goes through the node's shared, memoized +versionbits cache — the exact same lookup block validation already uses — and +the per-block log flood was replaced with a single aggregate line. Verified +live on mainnet: the oracle startup scan dropped from ~15 minutes to ~3 +seconds, and the node came up healthy and caught up. + +This is a pure performance change with no consensus impact: unit and +functional tests prove the startup gate computes the identical activation +result at every height, and that a restart equals a `-reindex` equals the +pre-restart oracle state. + +With the deployment burial below, every DigiDollar activation check in the +node becomes a single height comparison, which removes this entire class of +cost permanently — including the per-block cost that block validation itself +was still paying during sync. + +--- + +## Housekeeping: Taproot, DigiDollar, And AlgoLock Are Now Buried (BIP90) + +### What "Buried" Means + +A soft fork activates once. After it is active and buried deep in the chain, +there is no reason to keep re-running the BIP9 signaling state machine for it +on every node forever. "Burying" a deployment (BIP90) replaces the state +machine with the recorded activation height, hardcoded in chainparams — +exactly what DigiByte and Bitcoin already did for CSV, SegWit, ReserveAlgo, +and Odo. + +### Activation Heights + +The heights are the real, on-chain BIP9 activation heights — read from live +mainnet `getdeploymentinfo` and verified block-by-block on testnet26 +(signaling in blocks 200–599, active at exactly 600): + +| Deployment | BIP9 bit | Mainnet | 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 | + +### Why Bury Them + +- The BIP9 machinery serves no further purpose for these deployments: they can + never re-signal, time out, or fail. +- It removes all remaining BIP9 threshold-state recomputation from block + validation and startup. Every DigiDollar activation check is now one height + comparison. +- Blocks stop signaling bits 2, 23, and 0, so `getblocktemplate` version + fields are permanently clean for pool software (this also structurally + resolves the bit-23 version-rolling interference some SHA256D pool stacks + reported during signaling). +- It matches upstream practice and shrinks the consensus surface. + +### What Deliberately Did NOT Change + +- **The static DigiDollar gates keep their historical floors:** + `nDDActivationHeight = nOracleActivationHeight = nDigiDollarMuSig2Height = + 23,627,520` on mainnet (600 on testnet26). The 23,627,520 floor is + intentionally *below* the 23,869,440 activation 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 enforced alongside the deployment check — + retired-algorithm blocks are rejected over exactly the same height ranges as + before. +- **Taproot script validation** was already applied unconditionally, 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. The standard BIP90 +caveat applies, as it did for Bitcoin's burials: only in the hypothetical of +an astronomically expensive deep reorganization back below the activation +heights could pre-burial and post-burial nodes judge a re-mined chain +differently. This affects no real chain and does not change the security model +in practice. + +### Versionbits Warnings + +`MinBIP9WarningHeight` rises to 23,909,760 on mainnet (activation height plus +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` — whether DigiDollar is active at the current tip + - `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`. `activation_height` now reports the + exact activation height (previously a back-scan that could differ by one). + +- **`getdeploymentinfo`** (and REST `/deploymentinfo`): 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 carrying those bits. +- The `digidollar-oracle` opt-in rule and `default_oracle_commitment` + mechanics are unchanged from v9.26.4. + +### Command-Line Options (regtest only) + +- `-vbparams=taproot:...`, `-vbparams=digidollar:...`, and + `-vbparams=algolock:...` are now a **startup error** ("Invalid + deployment"). Only `testdummy` remains a versionbits deployment. +- `-digidollaractivationheight=N` now activates DigiDollar at **exactly + height N** (it sets the buried height together with the static + DD/oracle/MuSig2 gates). Pre-burial, this knob ran real BIP9 signaling and + activation landed on the first 144-block window boundary at or above + max(432, N) — e.g. N=650 used to activate at 720. Scripts relying on the + old window-boundary timing must be updated. +- New: `-testactivationheight=taproot@H` / `digidollar@H` / `algolock@H` set + only the buried deployment height. For `digidollar@H` the static DD/oracle + gates keep their regtest defaults (650), while `nDigiDollarMuSig2Height` is + derived as `min(nDDActivationHeight, DigiDollarHeight)` and so follows H + below 650. `-digidollaractivationheight` takes precedence when both are + given. + +None of these affect mainnet or testnet operation. + +--- + +## Mining And Pools + +No action is required. Templates keep working as in v9.26.4; the only visible +differences are that the template `version` field no longer carries any +deployment signaling bits and that `rules` explicitly lists the three active +soft forks. Pools that narrowed version-rolling masks or filtered bit 23 +during the signaling period can drop those workarounds. + +DigiDollar-aware mining is unchanged: + +```bash +digibyte-cli getblocktemplate '{"rules":["segwit","digidollar-oracle"]}' sha256d +``` + +--- + +## Who Should Upgrade + +Everyone, at normal operational convenience — this release contains no +consensus rule change for the live chains, so there is no coordination +deadline. Upgrade sooner if: + +- You restart nodes regularly (the ~15-minute startup hang is fixed). +- You run infrastructure that parses `getdigidollardeploymentinfo` or + `getdeploymentinfo` (block explorers, dashboards, exchange tooling) — update + your parsers for the new buried shapes at the same time. +- You run regtest-based CI that uses `-vbparams` or + `-digidollaractivationheight` — migrate per the section above. + +--- + +## What Does Not Change + +- Consensus validity of every block on mainnet and testnet26. +- Ports, data directories, network magic, genesis, addresses. +- Wallet files, DigiDollar positions, mint/send/redeem behavior, lock tiers, + collateral rules, DCA/ERR/volatility protection. +- The oracle roster (35 slots, 7-signature MuSig2 quorum on mainnet/testnet) + and the v0x03 on-chain bundle format. +- Pruning behavior and the DigiDollar prune floor (still anchored at + 23,627,520 on mainnet). +- P2P protocol, Dandelion++, BIP324 v2 transport option. + +--- + +## Useful Status Commands + +Check chain and deployment state (note the new buried shapes): + +```bash +digibyte-cli getblockchaininfo +digibyte-cli getdeploymentinfo +digibyte-cli getdigidollardeploymentinfo +``` + +Expected on mainnet: every deployment reports `"type": "buried"` and +`"active": true` (only `testdummy` remains BIP9), and +`getdigidollardeploymentinfo` reports `"status": "active"` with +`"activation_height": 23869440`. + +Check oracle and price state: + +```bash +digibyte-cli getoracleprice +digibyte-cli getalloracleprices +digibyte-cli getoracles +digibyte-cli getdigidollarstats +``` + +--- + +## Testing And Validation Coverage + +- Full unit suite: 3,413 test cases, no errors — including a new + `deployment_burial_tests` suite that pins the per-network burial heights, + their confirmation-window alignment, the preserved activation floors, the + regtest knob semantics, and the exact activation boundary. +- Full functional test suite: all tests pass. The DigiDollar activation tests + were rewritten from BIP9 signaling ladders to buried-boundary lifecycles + (pre-activation rejection, exact boundary flip, post-activation + mint/send/redeem, reorg-below-activation purge, multinode/IBD/reindex + parity, pruning). +- The startup fix's consensus neutrality is pinned by unit and functional + tests (restart == `-reindex` == pre-restart oracle state) and was verified + live on mainnet. +- The burial heights were verified against the live mainnet chain and a fully + synced testnet26 node before hardcoding, and the change was reviewed by + independent adversarial passes over the consensus diff, the RPC/GBT surface, + and the test migration. + +--- + +## Documentation + +- `doc/release-notes/release-notes-9.26.5.md` — full release notes for this + version. +- `DIGIDOLLAR_ACTIVATION_EXPLAINER.md` — now documents the burial and + preserves the complete BIP9 activation history as a historical record. +- `DIGIDOLLAR_EXPLAINER.md`, `DIGIDOLLAR_ARCHITECTURE.md`, + `DIGIDOLLAR_ORACLE_ARCHITECTURE.md`, `DIGIDOLLAR_WALLET_INTEGRATION.md`, + `DIGIDOLLAR_EXCHANGE_INTEGRATION.md` — updated where the burial changed + interfaces or predicates. + +--- + +## Final Reminder + +DigiDollar is live on DigiByte mainnet. v9.26.5 is the post-activation +maintenance release: it makes node startup fast again and retires the +now-finished activation machinery the safe, precedented way. Upgrade at your +convenience, and update anything that parses deployment status before you do. 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/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/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..86de339e1e --- /dev/null +++ b/doc/release-notes/release-notes-9.26.5.md @@ -0,0 +1,182 @@ +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`** (and REST `/deploymentinfo`): 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`. + (`getblockchaininfo` carries no deployment information in this codebase.) +- **`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. For `digidollar@H` the static + DD/oracle gates keep their regtest defaults (650), while + `nDigiDollarMuSig2Height` is derived as + `min(nDDActivationHeight, DigiDollarHeight)` and so follows H below 650. + `-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 640bded12a..18e44eb419 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 \ @@ -233,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..d17003acb7 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 the buried deployment height but not the static DD/oracle gates (nDigiDollarMuSig2Height is derived as min(nDDActivationHeight, DigiDollarHeight) and so may move with it); -digidollaractivationheight retargets everything together 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