diff --git a/.github/workflows/build-raven.yml b/.github/workflows/build-raven.yml index b0e34796c9..d9af7ee028 100644 --- a/.github/workflows/build-raven.yml +++ b/.github/workflows/build-raven.yml @@ -59,13 +59,22 @@ jobs: run: | ./autogen.sh mkdir -p "$HOME/.ccache" + "$PWD/depends/x86_64-pc-linux-gnu/native/bin/ccache" --print-config > /dev/null CONFIG_SITE="$PWD/depends/x86_64-pc-linux-gnu/share/config.site" \ - ./configure --without-gui --disable-bench + ./configure --without-gui --with-tx --disable-bench - name: Build and run unit tests run: | make -j2 - make check + if ! make check; then + for log in src/test-suite.log src/test/test_raven.log; do + if [ -f "$log" ]; then + echo "Failure details from $log:" + tail -n 160 "$log" + fi + done + exit 1 + fi - name: Exercise the real PQ verifier fuzz target run: | @@ -81,8 +90,8 @@ jobs: run: | python3 contrib/devtools/test-required-functional-gate.py python3 test/functional/test_runner.py --require-tests --jobs=2 \ - wallet_encryption_rewrite.py wallet_asset_multiwallet.py rpc_assettransfer.py feature_chainstate_ahead.py \ - feature_asset_activation_reorg.py feature_transfer_script_reorg.py feature_rip5_activation_reorg.py \ + wallet_encryption_rewrite.py wallet_asset_multiwallet.py feature_rewards_restart.py wallet_pq_recovery.py wallet_restricted_multiwallet.py wallet_messaging_multiwallet.py rpc_assettransfer.py feature_chainstate_ahead.py \ + feature_asset_activation_reorg.py feature_pq_activation_reorg_sigops.py feature_transfer_script_reorg.py feature_rip5_activation_reorg.py feature_enforced_coinbase_activation.py \ feature_asset_dbcrash.py feature_asset_dbcrash_transfer.py feature_asset_db_missing_block.py \ feature_asset_verifydb_sameblock.py feature_transfer_mempool_reorg.py feature_mempool_clear_reissue.py feature_mempool_reissue_restart.py feature_transfer_sidebranch.py feature_pq_asset_anchor.py feature_pq_asset_authority_selection.py feature_pq_asset_source_rpc.py feature_pq_asset_wallet_ownership.py feature_loadblock.py \ feature_restricted_dbcrash.py feature_restricted_verifydb.py feature_restricted_verifydb_root.py \ @@ -227,6 +236,7 @@ jobs: run: | ./autogen.sh mkdir -p "$HOME/.ccache" + "$PWD/depends/${{ matrix.host }}/native/bin/ccache" --print-config > /dev/null - name: Configure Build run: bash -Eeuo pipefail "${SCRIPTS}/04-configure-build.sh" "${{ matrix.OS }}" "$GITHUB_WORKSPACE" @@ -240,6 +250,34 @@ jobs: - name: Build Raven run: make -j2 + - name: Install Qt runtime test prerequisites + if: matrix.OS == 'linux' + run: sudo apt-get install -y xvfb xauth x11-utils libxtst6 libxkbcommon-x11-0 + + - name: Verify Qt smoke runtime libraries + if: matrix.OS == 'linux' + shell: bash + run: | + for binary in src/qt/raven-qt src/raven-cli; do + test -x "$binary" + libraries="$(ldd "$binary")" + if grep -Fq 'not found' <<< "$libraries"; then + printf 'Missing runtime libraries for %s:\n%s\n' "$binary" "$libraries" + exit 1 + fi + done + + - name: Test Qt startup without wallet + if: matrix.OS == 'linux' + run: ./contrib/devtools/test-qt-no-wallet-startup.sh + + - name: Test Qt Options without wallet + if: matrix.OS == 'linux' + run: | + xvfb-run -a -s '-screen 0 1024x768x24' \ + python3 contrib/devtools/test-qt-no-wallet-options.py \ + src/qt/raven-qt src/raven-cli + # Skip the binary checks for now. # name: Check Binary Security # run: ${SCRIPTS}/05-binary-checks.sh ${{ MATRIX.OS }} ${{ GITHUB.WORKSPACE }} diff --git a/.github/workflows/rip25-v48-final-gate.yml b/.github/workflows/rip25-v48-final-gate.yml index 35bc9d677d..740620882a 100644 --- a/.github/workflows/rip25-v48-final-gate.yml +++ b/.github/workflows/rip25-v48-final-gate.yml @@ -72,8 +72,9 @@ jobs: run: | ./autogen.sh mkdir -p "$HOME/.ccache" + "$PWD/depends/x86_64-pc-linux-gnu/native/bin/ccache" --print-config > /dev/null CONFIG_SITE="$PWD/depends/x86_64-pc-linux-gnu/share/config.site" \ - ./configure --without-gui --disable-bench + ./configure --without-gui --with-tx --disable-bench - id: build_tests name: Build tests @@ -103,8 +104,8 @@ jobs: run: | python3 contrib/devtools/test-required-functional-gate.py python3 test/functional/test_runner.py --require-tests --jobs=2 \ - wallet_encryption_rewrite.py wallet_asset_multiwallet.py rpc_assettransfer.py feature_chainstate_ahead.py \ - feature_asset_activation_reorg.py feature_transfer_script_reorg.py feature_rip5_activation_reorg.py \ + wallet_encryption_rewrite.py wallet_asset_multiwallet.py feature_rewards_restart.py wallet_pq_recovery.py wallet_restricted_multiwallet.py wallet_messaging_multiwallet.py rpc_assettransfer.py feature_chainstate_ahead.py \ + feature_asset_activation_reorg.py feature_pq_activation_reorg_sigops.py feature_transfer_script_reorg.py feature_rip5_activation_reorg.py feature_enforced_coinbase_activation.py \ feature_asset_dbcrash.py feature_asset_dbcrash_transfer.py feature_asset_db_missing_block.py \ feature_asset_verifydb_sameblock.py feature_transfer_mempool_reorg.py feature_mempool_clear_reissue.py feature_mempool_reissue_restart.py feature_transfer_sidebranch.py feature_pq_asset_anchor.py feature_pq_asset_authority_selection.py feature_pq_asset_source_rpc.py feature_pq_asset_wallet_ownership.py feature_loadblock.py \ feature_restricted_dbcrash.py feature_restricted_verifydb.py feature_restricted_verifydb_root.py \ @@ -218,6 +219,7 @@ jobs: run: | ./autogen.sh mkdir -p "$HOME/.ccache" + "$PWD/depends/${{ matrix.host }}/native/bin/ccache" --print-config > /dev/null CONFIG_SITE="$PWD/depends/${{ matrix.host }}/share/config.site" \ ./configure ${{ matrix.configure_flags }} diff --git a/README.md b/README.md index 605e8da5f3..7bd0740f26 100644 --- a/README.md +++ b/README.md @@ -7,37 +7,33 @@ https://ravencoin.org ## RIP-25: Post-Quantum Signatures (This Fork) -This fork implements [RIP-25](doc/RIP-0025-PQ-Signatures.md) ([GitHub Issue #1280](https://github.com/RavenProject/Ravencoin/issues/1280)), a proposal to add **quantum-resistant native RVN transaction signing** to Ravencoin using ML-DSA-44 (FIPS 204). +This fork implements [RIP-25](doc/RIP-0025-PQ-Signatures.md) ([GitHub Issue #1280](https://github.com/RavenProject/Ravencoin/issues/1280)), which adds **quantum-resistant native RVN transaction signing** using ML-DSA-44 (FIPS 204). The 4.8.1 candidate adds a separate bit-13 rule for new asset and owner-token outputs. ### What it does -New **witness v2** addresses use ML-DSA-44 (a NIST-standardized post-quantum signature algorithm) exclusively. Existing ECDSA addresses (witness v0) continue working unchanged. Users can migrate native RVN from ECDSA to ML-DSA-44 addresses before quantum computers can break ECDSA. Ravencoin assets remain on their legacy ownership conditions and are outside the current RIP-25 scope. +New **witness v2** addresses use ML-DSA-44 (a NIST-standardized post-quantum signature algorithm) exclusively. Existing ECDSA addresses continue working unchanged. Users can migrate native RVN to ML-DSA-44 addresses voluntarily. The original bit-12 rule does not change legacy asset ownership. Once bit 13 activates, newly created asset and owner-token outputs require a matching native RVN witness-v2 ML-DSA anchor when spent, with no ECDSA asset signature. Historical outputs retain their classical spending rule until their owners migrate them. -- **Old addresses (witness v0):** ECDSA/secp256k1, unchanged -- **New addresses (witness v2):** ML-DSA-44 only, quantum-resistant +- **Classical addresses:** ECDSA/secp256k1, unchanged +- **PQ addresses (witness v2):** ML-DSA-44 only, quantum-resistant - **Migration:** Users send native RVN from old to new addresses at their own pace -- **Assets:** Normal, owner, reissuable, unique, restricted, and qualifier assets are not protected by RIP-25 +- **Assets:** New post-bit-13 asset and owner-token outputs require an ML-DSA anchor; historical holdings remain classical until voluntary migration ### Key changes | Area | Change | |------|--------| -| **Consensus** | BIP9 soft-fork deployment (bit 12, 85% threshold), phased block weight increase (8 → 12 → 16 MWU) | +| **Consensus** | Bit-12 BIP9 signaling (1714 of 2016); incompatible 8 to 12 to 16 MWU block-limit transition and contextual PQ witness discount | | **Script** | Witness version 2 validation: 2-element witness stack [mldsa_sig, mldsa_pk], SHA256(pk) == program | -| **Policy** | `TX_WITNESS_V2_PQ_KEYHASH` standard type, PQ witness discount (8x), PQ-aware dust threshold | +| **Policy** | `TX_WITNESS_V2_PQ_KEYHASH` standard type and PQ-aware dust threshold | | **Addresses** | Bech32m encoding for witness v2 (HRP: `rvn` mainnet, `trvn` testnet, `rcrt` regtest) | | **Network** | `NODE_PQ_HYBRID` service flag (bit 5), 16 MB protocol message limit | -| **Crypto** | `src/crypto/mldsa.h/cpp` — ML-DSA-44 via [liboqs](https://github.com/open-quantum-safe/liboqs) (FIPS 204 compliant) | -| **Keys** | `src/pqkey.h/cpp` — `CPQKey` / `CPQPubKey` for ML-DSA-44 key management | +| **Crypto** | `src/crypto/mldsa.h/cpp`: ML-DSA-44 via [liboqs](https://github.com/open-quantum-safe/liboqs) (FIPS 204 compliant) | +| **Keys** | `src/pqkey.h/cpp`: `CPQKey` / `CPQPubKey` for ML-DSA-44 key management | | **Wallet** | `getnewpqaddress` RPC, PQ keystore integration, `IsMine` for witness v2 | -| **Assets** | Existing asset scripts remain legacy P2PKH-only; witness-v2 PQ destinations protect native RVN only | +| **Assets** | Bit 13 adds PQ-only asset carriers paired with native RVN witness-v2 inputs; historical asset outputs remain classically spendable | | **Signing** | ML-DSA-44 signing in `sign.cpp` via `TransactionSignatureCreator` | | **Build** | liboqs added as dependency (`depends/packages/liboqs.mk`, `configure.ac --with-liboqs`) | -| **Tests** | `src/test/pqkey_tests.cpp` — unit tests for ML-DSA-44 keygen, sign/verify, witness programs | - -### Branch - -Audit and remediation work is on [`fix/rip25-v48-glm-remediation`](https://github.com/ALENOC/Ravencoin/tree/fix/rip25-v48-glm-remediation). +| **Tests** | `src/test/pqkey_tests.cpp`: unit tests for ML-DSA-44 keygen, sign/verify, witness programs | ### Building with liboqs diff --git a/configure.ac b/configure.ac index 9e51c20fec..61029467d9 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl require autoconf 2.60 (AS_ECHO/AS_ECHO_N) AC_PREREQ([2.60]) define(_CLIENT_VERSION_MAJOR, 4) define(_CLIENT_VERSION_MINOR, 8) -define(_CLIENT_VERSION_REVISION, 0) +define(_CLIENT_VERSION_REVISION, 1) define(_CLIENT_VERSION_BUILD, 0) define(_CLIENT_VERSION_RC, 0) define(_CLIENT_VERSION_IS_RELEASE, true) diff --git a/contrib/devtools/check-rip25-v48-invariants.sh b/contrib/devtools/check-rip25-v48-invariants.sh index 79d4265574..82062b54c3 100755 --- a/contrib/devtools/check-rip25-v48-invariants.sh +++ b/contrib/devtools/check-rip25-v48-invariants.sh @@ -91,6 +91,21 @@ require_fixed 'feature_pq_asset_source_rpc.py' .github/workflows/build-raven.yml require_fixed 'feature_pq_asset_wallet_ownership.py' test/functional/test_runner.py 'PQ asset wallet ownership regression is not registered' require_fixed 'feature_mempool_clear_reissue.py' test/functional/test_runner.py 'mempool clear reissue regression is not registered' require_fixed 'feature_mempool_reissue_restart.py' test/functional/test_runner.py 'mempool reissue restart regression is not registered' +require_fixed "'feature_rewards_restart.py'" test/functional/test_runner.py 'reward restart regression is not registered' +require_fixed 'feature_rewards_restart.py' .github/workflows/rip25-v48-final-gate.yml 'final gate omits reward restart regression' +require_fixed 'feature_rewards_restart.py' .github/workflows/build-raven.yml 'release workflow omits reward restart regression' +require_fixed "'wallet_pq_recovery.py'" test/functional/test_runner.py 'PQ wallet recovery regression is not registered' +require_fixed 'wallet_pq_recovery.py' .github/workflows/rip25-v48-final-gate.yml 'final gate omits PQ wallet recovery regression' +require_fixed 'wallet_pq_recovery.py' .github/workflows/build-raven.yml 'release workflow omits PQ wallet recovery regression' +require_fixed "'wallet_restricted_multiwallet.py'" test/functional/test_runner.py 'restricted multiwallet regression is not registered' +require_fixed 'wallet_restricted_multiwallet.py' .github/workflows/rip25-v48-final-gate.yml 'final gate omits restricted multiwallet regression' +require_fixed 'wallet_restricted_multiwallet.py' .github/workflows/build-raven.yml 'release workflow omits restricted multiwallet regression' +require_fixed "'wallet_messaging_multiwallet.py'" test/functional/test_runner.py 'messaging multiwallet regression is not registered' +require_fixed 'wallet_messaging_multiwallet.py' .github/workflows/rip25-v48-final-gate.yml 'final gate omits messaging multiwallet regression' +require_fixed 'wallet_messaging_multiwallet.py' .github/workflows/build-raven.yml 'release workflow omits messaging multiwallet regression' +require_fixed "'feature_pq_activation_reorg_sigops.py'" test/functional/test_runner.py 'PQ activation reorg and sigop regression is not registered' +require_fixed 'feature_pq_activation_reorg_sigops.py' .github/workflows/rip25-v48-final-gate.yml 'final gate omits PQ activation reorg and sigop regression' +require_fixed 'feature_pq_activation_reorg_sigops.py' .github/workflows/build-raven.yml 'release workflow omits PQ activation reorg and sigop regression' # GLM-003: contextual 8 -> 12 -> 16 MWU and UTXO-bound 8x discount. require_fixed 'VersionBitsStateSinceHeight' src/validation.cpp 'deterministic RIP-25 phase boundary missing' @@ -619,12 +634,17 @@ require_fixed 'python3 contrib/devtools/test-required-functional-gate.py' "$fina require_fixed 'python3 test/functional/test_runner.py --require-tests' "$final_gate" 'functional gate permits absent or skipped security tests' require_fixed 'feature_mempool_clear_reissue.py' "$final_gate" 'final gate omits inherited mempool clear regression' require_fixed 'feature_mempool_reissue_restart.py' "$final_gate" 'final gate omits inherited reissue restart regression' -require_fixed 'wallet_encryption_rewrite.py wallet_asset_multiwallet.py rpc_assettransfer.py feature_chainstate_ahead.py' "$final_gate" 'required wallet, multiwallet asset, asset-scope, and chainstate-ahead functional tests are missing' +require_fixed 'wallet_encryption_rewrite.py wallet_asset_multiwallet.py feature_rewards_restart.py wallet_pq_recovery.py wallet_restricted_multiwallet.py wallet_messaging_multiwallet.py rpc_assettransfer.py feature_chainstate_ahead.py' "$final_gate" 'required wallet, rewards, PQ recovery, restricted multiwallet, messaging multiwallet, asset-scope, and chainstate-ahead functional tests are missing' require_fixed 'wallet_asset_multiwallet.py' .github/workflows/build-raven.yml 'release security job omits the multiwallet asset regression' require_fixed "'wallet_asset_multiwallet.py'" test/functional/test_runner.py 'multiwallet asset regression is not in the functional suite' require_fixed 'id: posttest_integrity' "$final_gate" 'final gate does not recheck source after security tests' final_security_job="$(sed -n '/^ security-tests:/,/^ build:/p' "$final_gate")" require_text "$final_security_job" 'test/test_raven_fuzzy --pq-smoke' 'final security job does not run the PQ verifier fuzz smoke test' +mapfile -t final_security_sha_lines < <(grep -nFx ' test "$(git rev-parse HEAD)" = "$GITHUB_SHA"' <<<"$final_security_job" | cut -d: -f1) +(( ${#final_security_sha_lines[@]} == 2 )) || fail 'final security job must execute exact-SHA checks before and after tests' +final_make_check_line="$(grep -nFx ' run: make check' <<<"$final_security_job" | cut -d: -f1 || true)" +[[ "$final_make_check_line" =~ ^[0-9]+$ ]] || fail 'final security job does not execute make check' +(( final_security_sha_lines[0] < final_make_check_line && final_make_check_line < final_security_sha_lines[1] )) || fail 'final security job does not bracket make check with exact-SHA checks' require_fixed 'id: postbuild_integrity' "$final_gate" 'final gate does not recheck source after cross-builds' final_build_matrix="$(sed -n '/^ matrix:/,/^ steps:/p' "$final_gate")" require_text "$final_build_matrix" $' - name: aarch64-disable-wallet\n host: aarch64-linux-gnu\n packages: g++-aarch64-linux-gnu\n configure_flags: --without-gui --disable-wallet --disable-bench\n run_tests: false' 'final gate lacks the aarch64 no-wallet cross-build' @@ -667,7 +687,12 @@ require_text "$release_security_job" 'feature_mempool_clear_reissue.py' 'release require_text "$release_security_job" 'feature_mempool_reissue_restart.py' 'release artifact workflow omits inherited reissue restart regression' require_text "$release_security_job" 'feature_chainstate_ahead.py' 'release artifact workflow does not test chainstate-ahead recovery' require_text "$release_security_job" 'test "$(git rev-parse HEAD)" = "$GITHUB_SHA"' 'release security job does not bind tests to the artifact SHA' -require_text "$release_build_job" ' needs: security-tests' 'release artifact matrix can package without passing security tests' +grep -Fxq ' needs: security-tests' <<<"$release_build_job" || fail 'release artifact matrix can package without passing security tests' +mapfile -t release_security_sha_lines < <(grep -nFx ' test "$(git rev-parse HEAD)" = "$GITHUB_SHA"' <<<"$release_security_job" | cut -d: -f1) +(( ${#release_security_sha_lines[@]} == 2 )) || fail 'release security job must execute exact-SHA checks before and after tests' +release_make_check_line="$(grep -nFx ' if ! make check; then' <<<"$release_security_job" | cut -d: -f1 || true)" +[[ "$release_make_check_line" =~ ^[0-9]+$ ]] || fail 'release security job does not execute make check' +(( release_security_sha_lines[0] < release_make_check_line && release_make_check_line < release_security_sha_lines[1] )) || fail 'release security job does not bracket make check with exact-SHA checks' release_matrix="$(sed -n '/^ matrix:/,/^ steps:/p' "$release_workflow")" release_axes="$(grep -E '^ [A-Za-z_][A-Za-z_0-9]*:' <<<"$release_matrix" || true)" [[ "$release_axes" == ' include:' ]] || fail 'release workflow has unexpected matrix axes' @@ -723,6 +748,8 @@ if [[ "$mode" == '--structural-only' ]]; then exit 0 fi +python3 contrib/devtools/test-rip25-invariant-gate-negative-controls.py + test_binary=src/test/test_raven [[ -x "$test_binary" ]] || fail "behavioral test binary is missing or not executable: $test_binary" # Qt and benchmark translation units are not linked into the headless @@ -804,7 +831,8 @@ echo "$fuzz_smoke_output" "$fuzz_binary" --pq-seed-tx | "$fuzz_binary" python3 contrib/devtools/test-required-functional-gate.py +PYTHONPATH=test/functional python3 -m unittest test_framework.test_cache_peers python3 test/functional/test_runner.py --require-tests --jobs=2 \ - wallet_encryption_rewrite.py rpc_assettransfer.py feature_chainstate_ahead.py feature_pq_asset_anchor.py feature_pq_asset_authority_selection.py feature_pq_asset_source_rpc.py feature_pq_asset_wallet_ownership.py feature_mempool_clear_reissue.py feature_mempool_reissue_restart.py + wallet_encryption_rewrite.py feature_rewards_restart.py wallet_pq_recovery.py wallet_restricted_multiwallet.py wallet_messaging_multiwallet.py rpc_assettransfer.py feature_chainstate_ahead.py feature_pq_activation_reorg_sigops.py feature_pq_asset_anchor.py feature_pq_asset_authority_selection.py feature_pq_asset_source_rpc.py feature_pq_asset_wallet_ownership.py feature_mempool_clear_reissue.py feature_mempool_reissue_restart.py echo 'RIP-25/v4.8 structural + behavioral invariants: OK' diff --git a/contrib/devtools/test-qt-no-wallet-options.py b/contrib/devtools/test-qt-no-wallet-options.py new file mode 100644 index 0000000000..1c7058b79d --- /dev/null +++ b/contrib/devtools/test-qt-no-wallet-options.py @@ -0,0 +1,206 @@ +#!/usr/bin/env python3 +"""Exercise the wallet-disabled Qt Options dialog under Xvfb.""" + +import ctypes +import os +from pathlib import Path +import re +import shutil +import socket +import subprocess +import sys +import tempfile +import time + + +def check_executable(path): + if not Path(path).is_file() or not os.access(path, os.X_OK): + raise RuntimeError("Missing executable: " + path) + + +def find_windows(): + output = subprocess.check_output(["xwininfo", "-root", "-tree"], text=True) + return [ + (int(match.group(1), 16), match.group(2)) + for line in output.splitlines() + if (match := re.search(r'^\s*(0x[0-9a-f]+) "([^"]+)"', line)) + ] + + +def wait_window(title, seconds): + deadline = time.monotonic() + seconds + while time.monotonic() < deadline: + matches = [ident for ident, name in find_windows() if title in name] + if matches: + return matches[-1] + time.sleep(0.25) + raise RuntimeError("Window did not appear: " + title) + + +def window_origin(ident): + output = subprocess.check_output(["xwininfo", "-id", hex(ident)], text=True) + x = re.search(r"Absolute upper-left X:\s*(-?\d+)", output) + y = re.search(r"Absolute upper-left Y:\s*(-?\d+)", output) + if not x or not y: + raise RuntimeError("Cannot locate Qt window") + return int(x.group(1)), int(y.group(1)) + + +class XInput: + def __init__(self): + x11 = ctypes.CDLL("libX11.so.6") + xtst = ctypes.CDLL("libXtst.so.6") + display_type = ctypes.c_void_p + window_type = ctypes.c_ulong + x11.XOpenDisplay.argtypes = [ctypes.c_char_p] + x11.XOpenDisplay.restype = display_type + x11.XSetInputFocus.argtypes = [ + display_type, window_type, ctypes.c_int, window_type + ] + x11.XStringToKeysym.argtypes = [ctypes.c_char_p] + x11.XStringToKeysym.restype = window_type + x11.XKeysymToKeycode.argtypes = [display_type, window_type] + x11.XKeysymToKeycode.restype = ctypes.c_uint + x11.XSync.argtypes = [display_type, ctypes.c_int] + xtst.XTestFakeKeyEvent.argtypes = [ + display_type, ctypes.c_uint, ctypes.c_int, window_type + ] + xtst.XTestFakeMotionEvent.argtypes = [ + display_type, ctypes.c_int, ctypes.c_int, ctypes.c_int, window_type + ] + xtst.XTestFakeButtonEvent.argtypes = [ + display_type, ctypes.c_uint, ctypes.c_int, window_type + ] + self.display = x11.XOpenDisplay(None) + if not self.display: + raise RuntimeError("X11 display is unavailable") + self.x11 = x11 + self.xtst = xtst + + def focus(self, ident): + self.x11.XSetInputFocus(self.display, ident, 2, 0) + self.x11.XSync(self.display, 0) + + def key(self, name): + alt = name.startswith("Alt+") + name = name[4:] if alt else name + alt_code = self.x11.XKeysymToKeycode( + self.display, self.x11.XStringToKeysym(b"Alt_L") + ) + code = self.x11.XKeysymToKeycode( + self.display, self.x11.XStringToKeysym(name.encode()) + ) + if not code: + raise RuntimeError("Unknown X11 key: " + name) + if alt: + self.xtst.XTestFakeKeyEvent(self.display, alt_code, 1, 0) + self.xtst.XTestFakeKeyEvent(self.display, code, 1, 0) + self.xtst.XTestFakeKeyEvent(self.display, code, 0, 0) + if alt: + self.xtst.XTestFakeKeyEvent(self.display, alt_code, 0, 0) + self.x11.XSync(self.display, 0) + time.sleep(0.3) + + def click(self, x, y): + self.xtst.XTestFakeMotionEvent(self.display, -1, x, y, 0) + self.xtst.XTestFakeButtonEvent(self.display, 1, 1, 0) + self.xtst.XTestFakeButtonEvent(self.display, 1, 0, 0) + self.x11.XSync(self.display, 0) + time.sleep(0.3) + + +def main(qt_binary, cli_binary): + check_executable(qt_binary) + check_executable(cli_binary) + if not shutil.which("xwininfo"): + raise RuntimeError("xwininfo is required") + if not os.environ.get("DISPLAY"): + raise RuntimeError("Run this test under Xvfb") + + work = Path(tempfile.mkdtemp(prefix="raven-qt-no-wallet-options.")) + data = work / "data" + config = work / "config" + data.mkdir() + config.mkdir() + with socket.socket() as port_socket: + port_socket.bind(("127.0.0.1", 0)) + port = port_socket.getsockname()[1] + cli = [ + cli_binary, f"-datadir={data}", "-regtest", + f"-rpcport={port}", "-rpcclienttimeout=3" + ] + env = os.environ.copy() + env["QT_QPA_PLATFORM"] = "xcb" + env["XDG_CONFIG_HOME"] = str(config) + qt_log = work / "raven-qt.log" + print(f"Qt options smoke evidence: {work}", flush=True) + + with qt_log.open("w") as log: + qt = subprocess.Popen( + [ + qt_binary, f"-datadir={data}", "-regtest", "-disablewallet", + "-server=1", "-listen=0", "-connect=0", "-dnsseed=0", + f"-rpcport={port}", "-splash=0", "-lang=en", + ], + env=env, stdout=log, stderr=subprocess.STDOUT, + ) + try: + deadline = time.monotonic() + 120 + while time.monotonic() < deadline: + if qt.poll() is not None: + raise RuntimeError(f"Qt exited before RPC readiness: {qt.returncode}") + response = subprocess.run( + cli + ["getblockchaininfo"], capture_output=True, timeout=5 + ) + if response.returncode == 0: + break + time.sleep(1) + else: + raise RuntimeError("Qt RPC did not become ready") + + main_window = wait_window("Raven Core - Node [regtest]", 30) + xinput = XInput() + xinput.focus(main_window) + left, top = window_origin(main_window) + xinput.click(left + 63, top + 12) + xinput.key("Down") + xinput.key("Return") + + options_window = wait_window("Options", 10) + xinput.focus(options_window) + for key in ("Alt+w", "Alt+i", "Tab", "space", "Alt+o"): + xinput.key(key) + time.sleep(2) + if qt.poll() is not None: + raise RuntimeError(f"Qt crashed after Options submit: {qt.returncode}") + + settings = list(config.rglob("*.conf")) + if not any( + "fToolbarIconsOnly=true" in file.read_text() for file in settings + ): + raise RuntimeError("Icons only option was not saved") + stopped = subprocess.run(cli + ["stop"], capture_output=True, timeout=10) + if stopped.returncode != 0: + raise RuntimeError("Qt RPC stop failed") + if qt.wait(timeout=30) != 0: + raise RuntimeError(f"Qt shutdown failed: {qt.returncode}") + print("Qt no-wallet Options interaction passed", flush=True) + finally: + if qt.poll() is None: + subprocess.run(cli + ["stop"], capture_output=True, timeout=5) + try: + qt.wait(timeout=10) + except subprocess.TimeoutExpired: + qt.kill() + qt.wait() + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: test-qt-no-wallet-options.py RAVEN_QT RAVEN_CLI", file=sys.stderr) + sys.exit(2) + try: + main(sys.argv[1], sys.argv[2]) + except Exception as error: + print(f"Qt no-wallet Options smoke failed: {error}", file=sys.stderr) + sys.exit(1) diff --git a/contrib/devtools/test-qt-no-wallet-startup.sh b/contrib/devtools/test-qt-no-wallet-startup.sh new file mode 100755 index 0000000000..8880f4182a --- /dev/null +++ b/contrib/devtools/test-qt-no-wallet-startup.sh @@ -0,0 +1,130 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +if (( $# > 2 )); then + echo "Usage: $0 [raven-qt path] [raven-cli path]" >&2 + exit 2 +fi + +repo_root="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" +qt_bin="${1:-${RAVEN_QT_BIN:-$repo_root/src/qt/raven-qt}}" +cli_bin="${2:-${RAVEN_CLI_BIN:-$repo_root/src/raven-cli}}" + +for tool in xvfb-run python3; do + command -v "$tool" >/dev/null 2>&1 || { + echo "Missing required tool: $tool" >&2 + exit 2 + } +done +for binary in "$qt_bin" "$cli_bin"; do + if [[ ! -x "$binary" ]]; then + echo "Missing executable: $binary" >&2 + exit 2 + fi +done + +work_dir="$(mktemp -d "${TMPDIR:-/tmp}/raven-qt-no-wallet.XXXXXXXX")" +data_dir="$work_dir/data" +mkdir -p "$data_dir" +qt_log="$work_dir/raven-qt.log" +rpc_log="$work_dir/raven-cli.log" +qt_pid="" +waited=0 + +rpc_port="${RAVEN_RPC_PORT:-$(python3 -c 'import socket; s = socket.socket(); s.bind(("127.0.0.1", 0)); print(s.getsockname()[1]); s.close()')}" +cli=("$cli_bin" -datadir="$data_dir" -regtest -rpcport="$rpc_port" -rpcclienttimeout=3) + +show_logs() { + for log in "$qt_log" "$rpc_log" "$data_dir/regtest/debug.log"; do + if [[ -f "$log" ]]; then + echo "===== $log =====" >&2 + tail -n 100 "$log" >&2 + fi + done +} + +cleanup() { + status=$? + trap - EXIT + if [[ -n "$qt_pid" && "$waited" -eq 0 ]]; then + if kill -0 "$qt_pid" 2>/dev/null; then + "${cli[@]}" stop >/dev/null 2>&1 || true + for (( i = 0; i < 10; i++ )); do + kill -0 "$qt_pid" 2>/dev/null || break + sleep 1 + done + if kill -0 "$qt_pid" 2>/dev/null; then + pkill -TERM -P "$qt_pid" 2>/dev/null || true + kill "$qt_pid" 2>/dev/null || true + fi + fi + wait "$qt_pid" 2>/dev/null || true + fi + if (( status != 0 )); then + show_logs + echo "Qt no-wallet startup failed. Logs: $work_dir" >&2 + fi + exit "$status" +} +trap cleanup EXIT + +echo "Starting $qt_bin with -regtest -disablewallet under Xvfb" +QT_QPA_PLATFORM=xcb xvfb-run -a -s '-screen 0 1024x768x24' \ + "$qt_bin" -datadir="$data_dir" -regtest -disablewallet -server=1 \ + -listen=0 -connect=0 -dnsseed=0 -rpcport="$rpc_port" -splash=0 >"$qt_log" 2>&1 & +qt_pid=$! + +deadline=$((SECONDS + 120)) +rpc_ready=0 +while (( SECONDS < deadline )); do + if ! kill -0 "$qt_pid" 2>/dev/null; then + if wait "$qt_pid"; then + exit_code=0 + else + exit_code=$? + fi + waited=1 + echo "Qt exited before RPC was ready (status $exit_code)" >&2 + exit 1 + fi + if "${cli[@]}" getblockchaininfo >/dev/null 2>"$rpc_log"; then + rpc_ready=1 + break + fi + sleep 1 +done + +if (( ! rpc_ready )); then + echo "Qt RPC did not become ready within 120 seconds" >&2 + exit 1 +fi +sleep 2 +if ! kill -0 "$qt_pid" 2>/dev/null; then + echo "Qt exited after RPC readiness" >&2 + exit 1 +fi + +"${cli[@]}" stop >"$rpc_log" 2>&1 || { + echo "Qt RPC stop failed" >&2 + exit 1 +} + +deadline=$((SECONDS + 30)) +while kill -0 "$qt_pid" 2>/dev/null; do + if (( SECONDS >= deadline )); then + echo "Qt did not stop within 30 seconds" >&2 + exit 1 + fi + sleep 1 +done + +if wait "$qt_pid"; then + waited=1 + echo "Qt no-wallet startup and RPC shutdown passed. Logs: $work_dir" +else + exit_code=$? + waited=1 + echo "Qt exited with status $exit_code after RPC stop" >&2 + exit 1 +fi diff --git a/contrib/devtools/test-rip25-invariant-gate-negative-controls.py b/contrib/devtools/test-rip25-invariant-gate-negative-controls.py new file mode 100644 index 0000000000..ccb4d1da9c --- /dev/null +++ b/contrib/devtools/test-rip25-invariant-gate-negative-controls.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +"""Check that inert workflow bait cannot satisfy the invariant gate.""" + +from pathlib import Path +import shutil +import subprocess +import tempfile +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +GATE = ROOT / "contrib/devtools/check-rip25-v48-invariants.sh" +WORKFLOWS = ("build-raven.yml", "rip25-v48-final-gate.yml") + + +class InvariantGateNegativeControls(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.tempdir = tempfile.TemporaryDirectory(prefix="rip25-gate-") + cls.fixture = Path(cls.tempdir.name) + for entry in ROOT.iterdir(): + if entry.name in (".git", ".github"): + continue + (cls.fixture / entry.name).symlink_to(entry, target_is_directory=entry.is_dir()) + github = cls.fixture / ".github" + github.mkdir() + for entry in (ROOT / ".github").iterdir(): + if entry.name == "workflows": + shutil.copytree(entry, github / entry.name) + else: + (github / entry.name).symlink_to(entry, target_is_directory=entry.is_dir()) + + @classmethod + def tearDownClass(cls): + cls.tempdir.cleanup() + + def run_gate(self): + return subprocess.run( + ["bash", str(GATE), "--structural-only"], cwd=self.fixture, + stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, + ) + + def check_mutation(self, workflow, old, new, occurrence=1): + path = self.fixture / ".github/workflows" / workflow + original = path.read_text(encoding="utf-8") + self.assertIn(old, original, "The workflow fixture no longer matches the test") + self.assertGreaterEqual(original.count(old), occurrence) + at = -1 + for _ in range(occurrence): + at = original.index(old, at + 1) + mutated = original[:at] + new + original[at + len(old):] + path.write_text(mutated, encoding="utf-8") + try: + result = self.run_gate() + self.assertNotEqual( + result.returncode, 0, + workflow + " accepted inert bait: " + old + "\n" + result.stdout, + ) + finally: + path.write_text(original, encoding="utf-8") + + def test_unmodified_workflows_pass(self): + result = self.run_gate() + self.assertEqual(result.returncode, 0, result.stderr) + + def test_build_raven_dependency_is_executable_yaml(self): + self.check_mutation( + WORKFLOWS[0], " needs: security-tests", + " # needs: security-tests", + ) + + def test_build_raven_make_check_is_executed(self): + self.check_mutation( + WORKFLOWS[0], " if ! make check; then", + " if ! echo 'make check'; then", + ) + + def test_build_raven_sha_check_is_executed_before_and_after(self): + old = ' test "$(git rev-parse HEAD)" = "$GITHUB_SHA"' + self.check_mutation(WORKFLOWS[0], old, " # " + old.strip()) + self.check_mutation(WORKFLOWS[0], old, " # " + old.strip(), occurrence=2) + + def test_final_gate_make_check_is_executed(self): + self.check_mutation( + WORKFLOWS[1], " run: make check", + " run: echo 'make check'", + ) + + def test_final_gate_sha_check_is_executed_before_and_after(self): + old = ' test "$(git rev-parse HEAD)" = "$GITHUB_SHA"' + self.check_mutation(WORKFLOWS[1], old, " # " + old.strip()) + self.check_mutation(WORKFLOWS[1], old, " # " + old.strip(), occurrence=2) + + +if __name__ == "__main__": + unittest.main() diff --git a/contrib/devtools/test-rip25-release-contract.py b/contrib/devtools/test-rip25-release-contract.py new file mode 100644 index 0000000000..8472259669 --- /dev/null +++ b/contrib/devtools/test-rip25-release-contract.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +"""Check the RIP-25 mining rollout and 4.8.1 release documentation contract.""" + +from pathlib import Path +import re +import unittest + + +ROOT = Path(__file__).resolve().parents[2] +NOTES = ROOT / "doc/release-notes/release-notes-4.8.1.md" +PROCESS = ROOT / "doc/release-process.md" + + +def section(text, heading): + match = re.search(r"(?im)^#{2,3} " + heading + r"[^\n]*\n", text) + if match is None: + raise AssertionError("Missing section: " + heading) + end = re.search(r"(?m)^#{2,3} ", text[match.end():]) + return text[match.end():match.end() + end.start() if end else None] + + +def require_paragraph(test, text, description, *patterns): + paragraphs = re.split(r"\n\s*\n", text) + for paragraph in paragraphs: + if all(re.search(pattern, paragraph, re.IGNORECASE | re.DOTALL) + for pattern in patterns): + return + test.fail("Missing coherent guidance for " + description) + + +class Rip25ReleaseContractTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.notes = NOTES.read_text(encoding="utf-8") + cls.process = PROCESS.read_text(encoding="utf-8") + + def test_mining_template_source_contract(self): + miner = (ROOT / "src/miner.cpp").read_text(encoding="utf-8") + validation = (ROOT / "src/validation.cpp").read_text(encoding="utf-8") + versionbits = (ROOT / "src/versionbits.cpp").read_text(encoding="utf-8") + mining_rpc = (ROOT / "src/rpc/mining.cpp").read_text(encoding="utf-8") + chainparams = (ROOT / "src/chainparams.cpp").read_text(encoding="utf-8") + + self.assertRegex(miner, r"pblock->nVersion\s*=\s*ComputeBlockVersion\(") + self.assertRegex(validation, r"state\s*==\s*THRESHOLD_LOCKED_IN\s*\|\|\s*state\s*==\s*THRESHOLD_STARTED") + self.assertRegex(versionbits, r'"pq_hybrid"\s*,\s*/\*\.gbt_force =\*/\s*true') + self.assertRegex(mining_rpc, r"if\s*\(!vbinfo\.gbt_force\)\s*\{[^}]*pblock->nVersion\s*&=\s*~VersionBitsMask") + self.assertRegex(miner, r'if\s*\(chainparams\.MineBlocksOnDemand\(\)\)\s*pblock->nVersion\s*=\s*gArgs\.GetArg\("-blockversion"') + + mainnet = chainparams.split("class CMainParams", 1)[1].split("class CTestNetParams", 1)[0] + testnet = chainparams.split("class CTestNetParams", 1)[1].split("class CRegTestParams", 1)[0] + regtest = chainparams.split("class CRegTestParams", 1)[1] + self.assertRegex(mainnet, r"DEPLOYMENT_PQ_HYBRID\]\.bit\s*=\s*12;") + self.assertRegex(mainnet, r"fMineBlocksOnDemand\s*=\s*false;") + self.assertRegex(testnet, r"fMineBlocksOnDemand\s*=\s*false;") + self.assertRegex(regtest, r"fMineBlocksOnDemand\s*=\s*true;") + + def test_notes_explain_automatic_signaling(self): + rollout = section(self.notes, "Mainnet signaling") + require_paragraph(self, rollout, "automatic bit-12 mining templates", + r"getblocktemplate|block templates?", r"bit[ -]?12", + r"STARTED", r"automatic|by default", r"signal") + require_paragraph(self, rollout, "mainnet override limits", + r"-blockversion", r"regtest", r"mainnet", r"override|disable") + + def test_notes_require_recorded_prestart_decision(self): + rollout = section(self.notes, "Mainnet signaling") + require_paragraph(self, rollout, "recorded owner go/no-go before original STARTED", + r"go/no-go", r"record", r"owner", r"evidence", + r"before", r"original", r"STARTED") + + def test_notes_require_qualified_replacement_rollout(self): + rollout = section(self.notes, "Mainnet signaling") + require_paragraph(self, rollout, "qualified replacement before original window", + r"replacement release", r"qualif", r"start", r"timeout", + r"install|deploy", r"miner", r"validator", r"service", + r"before", r"original") + + def test_process_puts_qualification_and_owner_approval_before_tag(self): + qualification = section(self.process, "Core 4\\.8\\.1 pre-tag qualification") + steps = re.findall(r"(?ms)^\d+\. (.*?)(?=^\d+\. |\Z)", qualification) + self.assertGreaterEqual(len(steps), 4, "Use an ordered pre-tag sequence") + + def step_for(description, *patterns): + matches = [index for index, step in enumerate(steps) + if all(re.search(pattern, step, re.IGNORECASE) + for pattern in patterns)] + self.assertEqual(len(matches), 1, "Missing or repeated step: " + description) + return matches[0] + + check = step_for("green make check on final SHA", r"final.*SHA", r"make check", r"green|pass") + ci = step_for("paired Build Raven and Final Gate on final SHA", + r"Build Raven", r"Final Gate", r"same.*SHA|final.*SHA", r"green|pass") + packages = step_for("five reviewed packages", r"five", r"artifact|package", + r"checksum", r"provenance") + workflow = (ROOT / ".github/workflows/build-raven.yml").read_text(encoding="utf-8") + targets = re.findall(r"(?m)^\s+- OS: ([a-z0-9]+)\s*$", workflow) + self.assertEqual(len(targets), 5, "Review every Build Raven matrix target") + for target in targets: + self.assertRegex(steps[packages], r"\b" + re.escape(target) + r"\b") + approval = step_for("separate owner tag approval", r"owner", r"separate|explicit", + r"approv", r"tag") + tag = step_for("tag exact qualified SHA", r"create.*tag|tag.*create", + r"exact.*SHA|same.*SHA|final.*SHA") + self.assertLess(check, ci) + self.assertLess(ci, packages) + self.assertLess(packages, approval) + self.assertLess(approval, tag) + self.assertLess(self.process.index("Core 4.8.1 pre-tag qualification"), + self.process.index("For releases other than 4.8.1"), + "The 4.8.1 sequence must precede generic tag instructions") + + +if __name__ == "__main__": + unittest.main() diff --git a/depends/packages/bdb.mk b/depends/packages/bdb.mk index 050a60adde..41de4f668d 100644 --- a/depends/packages/bdb.mk +++ b/depends/packages/bdb.mk @@ -3,6 +3,7 @@ $(package)_version=4.8.30 $(package)_download_path=https://download.oracle.com/berkeley-db $(package)_file_name=db-$($(package)_version).NC.tar.gz $(package)_sha256_hash=12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef +$(package)_patches=clear_new_pages.patch $(package)_build_subdir=build_unix define $(package)_set_vars @@ -13,6 +14,7 @@ $(package)_cxxflags=-std=c++11 endef define $(package)_preprocess_cmds + patch -p1 < $($(package)_patch_dir)/clear_new_pages.patch && \ sed -i.old 's/__atomic_compare_exchange/__atomic_compare_exchange_db/' dbinc/atomic.h && \ sed -i.old 's/atomic_init/atomic_init_db/' dbinc/atomic.h mp/mp_region.c mp/mp_mvcc.c mp/mp_fget.c mutex/mut_method.c mutex/mut_tas.c && \ cp -f $(BASEDIR)/config.guess $(BASEDIR)/config.sub dist diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk index bbe0375409..034b14e2d4 100644 --- a/depends/packages/dbus.mk +++ b/depends/packages/dbus.mk @@ -1,10 +1,17 @@ package=dbus $(package)_version=1.10.18 $(package)_download_path=https://dbus.freedesktop.org/releases/dbus +$(package)_fallback_download_path=https://download.bitcoinabc.org/depends-sources $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e38cc514a $(package)_dependencies=expat +define $(package)_fetch_cmds +( test -f $(dbus_source) || \ + $(call fetch_file_inner,dbus,$(dbus_download_path),$(dbus_download_file),$(dbus_file_name),$(dbus_sha256_hash)) || \ + $(call fetch_file_inner,dbus,$(dbus_fallback_download_path),$(dbus_download_file),$(dbus_file_name),$(dbus_sha256_hash)) ) +endef + define $(package)_set_vars $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-static --without-x endef diff --git a/depends/packages/fontconfig.mk b/depends/packages/fontconfig.mk index 7b0eb1a8f1..a0b291cedb 100644 --- a/depends/packages/fontconfig.mk +++ b/depends/packages/fontconfig.mk @@ -1,10 +1,17 @@ package=fontconfig $(package)_version=2.12.1 $(package)_download_path=https://mirrors.mit.edu/macports/distfiles/fontconfig/ +$(package)_fallback_download_path=https://distfiles.macports.org/fontconfig $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f7d6c7f3 $(package)_dependencies=freetype expat +define $(package)_fetch_cmds +( test -f $(fontconfig_source) || \ + $(call fetch_file_inner,fontconfig,$(fontconfig_download_path),$(fontconfig_download_file),$(fontconfig_file_name),$(fontconfig_sha256_hash)) || \ + $(call fetch_file_inner,fontconfig,$(fontconfig_fallback_download_path),$(fontconfig_download_file),$(fontconfig_file_name),$(fontconfig_sha256_hash)) ) +endef + define $(package)_set_vars $(package)_config_opts=--disable-docs --disable-static endef diff --git a/depends/packages/freetype.mk b/depends/packages/freetype.mk index 55303caa95..025bbb2383 100644 --- a/depends/packages/freetype.mk +++ b/depends/packages/freetype.mk @@ -1,9 +1,16 @@ package=freetype $(package)_version=2.7.1 -$(package)_download_path=https://download.savannah.gnu.org/releases/$(package) +$(package)_download_path=https://download-mirror.savannah.gnu.org/releases/$(package) +$(package)_fallback_download_path=https://downloads.sourceforge.net/project/freetype/freetype2/$($(package)_version) $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88 +define $(package)_fetch_cmds +( test -f $(freetype_source) || \ + $(call fetch_file_inner,freetype,$(freetype_download_path),$(freetype_download_file),$(freetype_file_name),$(freetype_sha256_hash)) || \ + $(call fetch_file_inner,freetype,$(freetype_fallback_download_path),$(freetype_download_file),$(freetype_file_name),$(freetype_sha256_hash)) ) +endef + define $(package)_set_vars $(package)_config_opts=--without-zlib --without-png --disable-static $(package)_config_opts_linux=--with-pic diff --git a/depends/packages/libXau.mk b/depends/packages/libXau.mk index e87df2e4de..ca5d1779eb 100644 --- a/depends/packages/libXau.mk +++ b/depends/packages/libXau.mk @@ -1,10 +1,17 @@ package=libXau $(package)_version=1.0.8 $(package)_download_path=http://xorg.freedesktop.org/releases/individual/lib/ +$(package)_fallback_download_path=https://download.bitcoinabc.org/depends-sources $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=fdd477320aeb5cdd67272838722d6b7d544887dfe7de46e1e7cc0c27c2bea4f2 $(package)_dependencies=xproto +define $(package)_fetch_cmds +( test -f $(libXau_source) || \ + $(call fetch_file_inner,libXau,$(libXau_download_path),$(libXau_download_file),$(libXau_file_name),$(libXau_sha256_hash)) || \ + $(call fetch_file_inner,libXau,$(libXau_fallback_download_path),$(libXau_download_file),$(libXau_file_name),$(libXau_sha256_hash)) ) +endef + define $(package)_set_vars $(package)_config_opts=--disable-shared $(package)_config_opts_linux=--with-pic diff --git a/depends/packages/libxcb.mk b/depends/packages/libxcb.mk index 28f2bd6f25..e665a3655d 100644 --- a/depends/packages/libxcb.mk +++ b/depends/packages/libxcb.mk @@ -1,10 +1,17 @@ package=libxcb $(package)_version=1.10 $(package)_download_path=http://xcb.freedesktop.org/dist +$(package)_fallback_download_path=https://download.bitcoinabc.org/depends-sources $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=98d9ab05b636dd088603b64229dd1ab2d2cc02ab807892e107d674f9c3f2d5b5 $(package)_dependencies=xcb_proto libXau xproto +define $(package)_fetch_cmds +( test -f $(libxcb_source) || \ + $(call fetch_file_inner,libxcb,$(libxcb_download_path),$(libxcb_download_file),$(libxcb_file_name),$(libxcb_sha256_hash)) || \ + $(call fetch_file_inner,libxcb,$(libxcb_fallback_download_path),$(libxcb_download_file),$(libxcb_file_name),$(libxcb_sha256_hash)) ) +endef + define $(package)_set_vars $(package)_config_opts=--disable-static endef diff --git a/depends/packages/xcb_proto.mk b/depends/packages/xcb_proto.mk index 0c7c958d62..e6c9a34e7b 100644 --- a/depends/packages/xcb_proto.mk +++ b/depends/packages/xcb_proto.mk @@ -1,9 +1,16 @@ package=xcb_proto $(package)_version=1.10 $(package)_download_path=http://xcb.freedesktop.org/dist +$(package)_fallback_download_path=https://download.bitcoinabc.org/depends-sources $(package)_file_name=xcb-proto-$($(package)_version).tar.bz2 $(package)_sha256_hash=7ef40ddd855b750bc597d2a435da21e55e502a0fefa85b274f2c922800baaf05 +define $(package)_fetch_cmds +( test -f $(xcb_proto_source) || \ + $(call fetch_file_inner,xcb_proto,$(xcb_proto_download_path),$(xcb_proto_download_file),$(xcb_proto_file_name),$(xcb_proto_sha256_hash)) || \ + $(call fetch_file_inner,xcb_proto,$(xcb_proto_fallback_download_path),$(xcb_proto_download_file),$(xcb_proto_file_name),$(xcb_proto_sha256_hash)) ) +endef + define $(package)_set_vars $(package)_config_opts=--disable-shared $(package)_config_opts_linux=--with-pic diff --git a/depends/packages/xproto.mk b/depends/packages/xproto.mk index 50a90b2685..03b6a8b1f9 100644 --- a/depends/packages/xproto.mk +++ b/depends/packages/xproto.mk @@ -1,9 +1,16 @@ package=xproto $(package)_version=7.0.26 $(package)_download_path=http://xorg.freedesktop.org/releases/individual/proto +$(package)_fallback_download_path=https://download.bitcoinabc.org/depends-sources $(package)_file_name=$(package)-$($(package)_version).tar.bz2 $(package)_sha256_hash=636162c1759805a5a0114a369dffdeccb8af8c859ef6e1445f26a4e6e046514f +define $(package)_fetch_cmds +( test -f $(xproto_source) || \ + $(call fetch_file_inner,xproto,$(xproto_download_path),$(xproto_download_file),$(xproto_file_name),$(xproto_sha256_hash)) || \ + $(call fetch_file_inner,xproto,$(xproto_fallback_download_path),$(xproto_download_file),$(xproto_file_name),$(xproto_sha256_hash)) ) +endef + define $(package)_set_vars $(package)_config_opts=--disable-shared endef diff --git a/depends/patches/bdb/clear_new_pages.patch b/depends/patches/bdb/clear_new_pages.patch new file mode 100644 index 0000000000..7c662560f6 --- /dev/null +++ b/depends/patches/bdb/clear_new_pages.patch @@ -0,0 +1,15 @@ +--- a/db/db.c ++++ b/db/db.c +@@ -549,3 +549,3 @@ +- clear_len = CRYPTO_ON(env) ? +- (dbp->pgsize != 0 ? dbp->pgsize : DB_CLEARLEN_NOTSET) : +- DB_PAGE_DB_LEN; ++ /* Clear the full page so old records cannot survive page reuse. */ ++ clear_len = dbp->pgsize != 0 ? ++ dbp->pgsize : DB_CLEARLEN_NOTSET; +@@ -555,3 +555,2 @@ +- clear_len = CRYPTO_ON(env) ? +- (dbp->pgsize != 0 ? dbp->pgsize : DB_CLEARLEN_NOTSET) : +- DB_PAGE_DB_LEN; ++ clear_len = dbp->pgsize != 0 ? ++ dbp->pgsize : DB_CLEARLEN_NOTSET; diff --git a/doc/RIP-0025-PQ-Assets-Followup.md b/doc/RIP-0025-PQ-Assets-Followup.md index 8751a9dd03..a8406288ea 100644 --- a/doc/RIP-0025-PQ-Assets-Followup.md +++ b/doc/RIP-0025-PQ-Assets-Followup.md @@ -1,22 +1,30 @@ # RIP-25 PQ Asset Extension Design Note -Status: dormant bit 13 consensus candidate for the 4.8.1 working branch. -Default public-network parameters do not activate it. Wallet construction -and recovery paths have local tests; public activation and release -qualification remain open. +Status: bit 13 PQ-only asset candidate with approved mainnet and testnet +BIP9 windows in the 4.8.1 working branch. Bit 13 is not active on either +public network. Public activation and release qualification remain blocked +by the native bit-12 deployment plan, testnet history review, and final +same-SHA release evidence. ## 1. Current Scope -On the default public-network parameters, RIP-25 witness-v2 protects native RVN outputs only. No current Ravencoin asset class is yet qualified for issuance, custody, transfer, reissue, tagging, freezing, or spending under an ML-DSA-44 ownership condition. The 4.8.1 candidate assigns a separate, dormant bit 13 for a dependent asset rule and exercises it on regtest. That candidate is not a deployed public-network protection or a release-qualified feature. +Until bit 13 activates, no Ravencoin asset class gains ML-DSA-44 ownership +from this candidate. The 4.8.1 working branch schedules a separate, +dependent bit-13 rule for new asset and owner-token outputs. The PQ-only +implementation passed a same-SHA CI pair on `9151946ad`, but it is not a +deployed public-network protection or a release-qualified feature. -The current spendable asset envelope is: +The historical spendable asset envelope is: ``` OP_DUP OP_HASH160 <20-byte-key-id> OP_EQUALVERIFY OP_CHECKSIG OP_RVN_ASSET OP_DROP ``` -`CScript::IsAssetScript` recognizes `OP_RVN_ASSET` only at byte offset 25. `Solver` then extracts the 20-byte key identifier from the P2PKH prefix. Wallet ownership and signing consequently resolve the output through a classical secp256k1 `CKeyID`. +`CScript::IsAssetScript` recognizes `OP_RVN_ASSET` only at byte offset 25. +Historical outputs use the P2PKH prefix and a secp256k1 `CKeyID`. +The candidate PQ-only prefix in Section 20 preserves that offset while +replacing classical authorization with a program-derived asset identifier. The limitation applies to all current asset classes: @@ -38,7 +46,7 @@ A cryptographically relevant quantum computer that recovers a secp256k1 private Wallet and RPC messages must not imply that a native-RVN PQ address alone protects asset owner or administrator tokens. The asset builder rejects that bare address and, at active bit 13, requires a canonical -classical|PQ asset descriptor. +`assetID|PQ` descriptor. The asset identifier has no classical private key. ## 3. Why the Proposed Concatenation Is Unsafe @@ -114,27 +122,27 @@ No asset extension should be coupled silently to an already deployed RIP-25 bit. The wallet uses a distinct composite PQ asset destination rather than reusing a native-RVN address without a defined asset meaning. -The working branch defines a wallet-facing, network-bound descriptor of -the form `|`. Its decoder -requires exactly one separator, the expected destination types, and exact -canonical re-encoding on the selected network. The descriptor is not a -consensus script and not a native RVN payment address. At active bit 13, -the wallet uses it for root, subasset, unique, restricted, qualifier, -reissue, transfer, and administrative outputs. The raw asset constructor -accepts it for corresponding output forms. `getnewpqassetaddress` generates -both keys only after the dependent rule is active, persists the pairing, -and `listpqassetaddresses` retrieves owned pairs after reload and validated -key-only salvage. A single Bech32m address cannot be used -for the two independent keys without defining a new encoding and changing -the existing address-length assumptions. The descriptor is split before -restricted-asset verifier or qualifier-index lookups: those indexes remain -keyed by the classical address. The wallet selects a matching funded native +The working branch defines a wallet-facing, network-bound `assetID|PQ` +descriptor. `assetID` encodes `HASH160(P)` as a legacy-sized address field; +`P` is the native witness-v2 program. Its decoder requires exactly one +separator, the expected destination types, the program-derived identifier, +and canonical re-encoding on the selected network. The identifier has no +classical private key and is not a native RVN payment address. At active +bit 13, the wallet uses the descriptor for root, subasset, unique, +restricted, qualifier, reissue, transfer, and administrative outputs. The +raw asset constructor accepts it for corresponding forms. +`getnewpqassetaddress` generates an ML-DSA key only after the dependent +rule is active, persists the descriptor, and `listpqassetaddresses` +retrieves owned descriptors after reload and validated key-only salvage. +Restricted-asset verifier and qualifier indexes use the program-derived +asset identifier. The wallet selects a matching funded native PQ anchor and returns protected asset change or owner authority. For one -consumed program, ordinary positive native change refreshes the anchor at +consumed program, ordinary positive native change can refresh the anchor at the same PQ address. Exact-fee, dust, multiple-program, or explicit native -change can omit that refresh, so the holder must send native RVN to the -affected PQ address before another protected spend. Raw callers must -provide matching funded native anchor inputs themselves. The local +change can omit that refresh. Ordinary RVN payments can also consume an +anchor without spending the asset. In either case the holder must send native +RVN to the affected PQ address before another protected spend. Raw callers +must provide matching funded native anchor inputs themselves. The local full-chain suite covers representative operations for every named class, nontrivial restricted verification, encrypted backup recovery, and GUI compilation; it does not certify public deployment. @@ -155,7 +163,12 @@ All high-level and raw-transaction RPCs must construct only canonical activated ## 8. Asset Index and Database Compatibility -Asset indexes currently derive an address identity from legacy 20-byte data. A PQ program is 32 bytes. The follow-up design must version index keys or otherwise prevent truncation and type confusion. +Asset indexes use 20-byte identifiers. In the PQ-only candidate that +identifier is `HASH160(P)`, rather than an independently selected +classical key identifier. A bare identifier is accounting data, not the complete PQ +destination: custody and spending still require the committed 32-byte +program and a matching funded anchor. The historical two-program collision +described in FINDING-153 belonged to the superseded dual-signature form. It must specify: @@ -171,7 +184,7 @@ Database readers must reject malformed or unknown key versions without interpret ## 9. Owner-Token Migration -Existing `ASSET!` outputs cannot become quantum-resistant merely because RIP-25 activates. A migration mechanism must prove current authorization while moving control to a PQ asset condition. Design choices include a normal legacy-authorized transfer into the new condition or a dedicated migration transaction type. +Existing `ASSET!` outputs cannot become quantum-resistant merely because RIP-25 activates. The current candidate permits a normal legacy-authorized transfer of a historical asset or owner-token UTXO into a new tagged output. The old input remains under its creation-height rules; the new output must meet the active bit-13 rule. A migration mechanism must prove current authorization while moving control to a PQ asset condition. The proposal must address: @@ -211,20 +224,23 @@ The follow-up implementation should not be proposed for activation without: ## 12. Current Decision -The approved native-RVN RIP-25 rules remain unchanged. The 4.8.1 working -branch includes a dependent, dormant bit 13 asset-consensus candidate. Wallet -asset builders still reject a bare witness-v2 destination, since a native PQ -address alone is not a hybrid asset destination. The candidate has passed -local unit, functional, invariant, and Qt compilation checks but is not -qualified for public-network activation or a 4.8.1 asset-PQ release claim. -Cross-platform exact-SHA CI, release artifacts, a second independent audit, -and an explicit protocol-owner activation decision remain required. +The native-RVN bit-12 rules retain their 8 to 12 to 16 MWU plan. The +4.8.1 working branch implements a dependent PQ-only bit-13 asset rule and +sets the approved public BIP9 windows. Wallet asset builders still reject +a bare native witness-v2 destination: an asset requires its canonical +`assetID|PQ` descriptor. Unit, functional, wallet-recovery, and pinned-Qt +checks passed with all five Build Raven artifacts and a green Final Gate +on candidate SHA `9151946ad`. A second independent audit froze and tracked +its findings. Release qualification still needs the bit-12 network upgrade +plan, historical testnet review, remaining finding closure, and final-SHA +artifacts. The dates in code do not authorize publishing or mining under +the new rule. -## 13. Experimental Compatibility Direction +## 13. Historical Dual-Signature Compatibility Research -The following records the compatibility research behind the dormant bit 13 -candidate. It must not be described as protecting assets on the default -public-network parameters. +This section records earlier compatibility research. Its P2PKH plus PQ +tail was superseded by the PQ-only format in Section 20. It is not the +current ownership rule or a public-network protection claim. Directly appending a witness-v2 program or witness to a legacy P2PKH asset output does not work as a compatible soft fork. The complete script is not a @@ -260,8 +276,8 @@ compatibility, not evidence that the proposed new consensus rule is safe. A canonical helper recognizes the narrow envelope and rejects short/long tails, nonminimal pushes, malformed payloads, and transfer messages -without an explicit expiry field. The dormant bit 13 candidate uses this -helper in consensus only after its effective activation height. An old-valid +without an explicit expiry field. The earlier dual-signature candidate used +this helper only after its effective activation height. An old-valid script ending in `OP_DROP` plus 31 arbitrary bytes is byte-identical to a candidate with a 32-byte program beginning `OP_DROP`. The helper necessarily classifies it as a candidate. The spend rule therefore @@ -269,25 +285,20 @@ checks the creating UTXO's height against the asset extension's activation height. Applying the rule only at spending height would retroactively lock historical coins and could split nodes after reorg or reindex. -The proposed new rule would not put an unexpected witness on the asset input. +The earlier proposal would not put an unexpected witness on the asset input. Instead, the spending transaction would also consume a native RIP-25 witness-v2 UTXO whose program equals the program committed in the asset output. Its fixed `SIGHASH_ALL` ML-DSA signature commits to every asset input and output in the same transaction. A quantum attacker with only the P2PKH private key could not create the required PQ input. -This construction remains a candidate rather than a release-qualified -specification. The 4.8.1 working branch assigns BIP9 bit 13 but leaves its -start and timeout equal and far in the future on every network, so default -nodes do not activate it. Its tested consensus prototype uses the canonical -parser and creation-height gate described below. Before public-network -activation it still needs complete anchor-refresh behavior, cross-platform -exact-SHA qualification, independent review, and an explicit migration and -activation decision. The wallet now has tested protected owner returns, -asset change, restricted-address checks, and representative all-class -vectors. Bit 13 timing must not be enabled without protocol-owner review. +This dual-signature construction has been superseded. Section 20 specifies +the current PQ-only output: its asset input has an empty scriptSig and +does not require a classical signature. The public bit-13 windows are +scheduled in code, but network activation and release remain gated as +described in Section 16 and the security remediation register. -## 14. Contextual Enforcement Map (Dormant Candidate) +## 14. Contextual Enforcement Map (PQ-Only Candidate) The candidate's UTXO-aware hook runs in `ConnectBlock` before `UpdateCoins`. `Consensus::CheckTxPQAssets` requires canonical tagged asset outputs at the @@ -304,17 +315,20 @@ Effective asset enforcement requires bit 13, the historical bit 8 transfer parser, and native RIP-25 bit 12 all to be active. Its first enforcement height is the maximum of their activation heights. This prevents a node validating a side branch from borrowing the global active tip's state. -The parser's historical `OP_DROP` lookalike requires a creation-height check -against the asset extension activation height, not merely a spend-height -check. Reorgs across activation revalidate affected mempool transactions +The PQ-only prefix remains historically valid before activation, so the +input rule uses its creating height rather than only its spending height. +The wallet does not label an early lookalike as PQ custody, and mempool +policy rejects its premature creation. Reorgs across activation revalidate +affected mempool transactions through staged removal so asset reissue reservations and compact-block indexes stay consistent. Reusing the existing RIP-25 bit 12 would be unsafe because it would make the newly upgraded nodes enforce a rule older RIP-25 nodes do not. `CheckTxPQAssets` runs outside the script cache. When it finds an asset input -created after activation, `ConnectBlock` forces both the classical and PQ -script checks even under assumevalid. A native witness-v2 input is verified +created after activation, `ConnectBlock` requires the matching PQ anchor +and an empty asset scriptSig even under assumevalid; ECDSA is not required. +A native witness-v2 input is verified under the contextual RIP-25 flag, not by treating the asset envelope itself as a witness program. `TestBlockValidity` is the miner's final template check, but template selection and package policy still need boundary tests. @@ -325,29 +339,29 @@ program requires a different anchor input. The wallet selects a funded matching anchor and signs it. A newly received asset output does not itself fund its matching native anchor: the recipient must send RVN to the descriptor's PQ address. A single-program spend can refresh that anchor -from ordinary native change, but the edge cases listed in Section 7 still -require manual top-up. Bit 13 is assigned but dormant. +from ordinary native change, but the edge cases listed in Section 17 still +require manual top-up. Bit 13 has future public windows but is not active. ## 15. 4.8.1 Integration Candidate and Release Gate The 4.8.1 integration request expands RIP-25 to assets. The native-RVN -implementation and its bit 12 deployment remain unchanged. This section -records the dormant bit 13 consensus candidate, not a public-network rule -already in force or a release-qualified feature. +8 to 12 to 16 MWU plan remains a separate bit-12 deployment concern. This +section records the scheduled bit-13 PQ-only candidate, not a rule already +in force or a release-qualified feature. Let `H` be the first candidate-block height at which bit 13, bit 8, and -native RIP-25 bit 12 are all active. A safe soft-fork direction requires both: +native RIP-25 bit 12 are all active. The asset rule requires both: -1. Every spendable asset output created at height `h >= H`, including an owner - token, has one canonical 32-byte PQ program in the validated legacy-compatible - envelope. Null asset metadata outputs remain governed by their existing - rules. An asset output without the canonical program is invalid at `h >= H`. +1. Every spendable asset output created at height `h >= H`, including an + owner token, has the canonical 25-byte PQ-only prefix and one bound + 32-byte program. Null asset metadata outputs retain their existing + rules. An asset output without this form is invalid at `h >= H`. 2. A transaction spending an asset UTXO created at `h >= H` must also spend a native RIP-25 witness-v2 UTXO whose 32-byte program equals that asset output's committed program. One valid anchor input may authorize several tagged asset inputs only when their programs match. The native witness-v2 fixed `SIGHASH_ALL` signature then binds every input and output of the - transaction. Classical asset-script authorization remains required too. + transaction. The asset input has an empty scriptSig and needs no ECDSA. The candidate block's previous index, not the global active tip, must select the BIP9 state. Mempool admission uses the current tip and treats an unconfirmed @@ -356,11 +370,11 @@ assumevalid shortcuts. Native witness-v2 signature verification must still run under assumevalid when the asset anchor is needed. A transaction may not remove the PQ condition by sending its asset to a legacy-only output after activation. -The creation-height rule is essential. A pre-activation script ending in -`OP_DROP` plus 31 bytes can be byte-identical to a tagged candidate. Requiring -an anchor from such a historical coin would retroactively make old-valid -spends invalid. Conversely, a pre-activation tagged-looking output cannot be -advertised as PQ-protected merely because its spend happens after activation. +The creation-height rule is essential. A pre-activation PQ-only lookalike +was valid under historical script rules and may be spent without an anchor. +Requiring one retroactively would lock an old-valid coin. The wallet must +not advertise that historical output as PQ custody. Mempool policy rejects +new lookalikes before activation and removes them after a boundary reorg. This construction still does not retroactively secure existing owner tokens. Their classical key holder must authorize a migration into a newly tagged @@ -368,22 +382,30 @@ output before that classical key is compromised. A quantum attacker who obtains the old key can race that migration. Software cannot distinguish the rightful holder from the attacker using the old signature alone. -The following remain release-blocking before assigning public-network -activation times or publishing a 4.8.1 artifact as asset-PQ-qualified: -independent protocol review of the hybrid address and anchor construction, -the manual-funding and refresh policy, exact-SHA cross-platform CI and -artifacts, miner and mempool equivalence, BIP9 reorg evidence, and an -operator-visible migration plan for existing owner and administrative -holdings. The local tests cover ownership, selection, output classes, -restricted indexes, same-block and mempool-parent heights, and reorgs, but -do not replace independent deployment review. Reusing bit 12 would change -the meaning of an already active deployment on testnet/regtest and is -excluded from this candidate. +Core 4.8.0 accepted a 932-block regtest chain containing 66 PQ-only asset +outputs and 35 later spends from the candidate, including an asset spend +with empty scriptSig. This checks the asset format at ordinary block sizes; +Core 4.8.0 does not enforce ML-DSA. FINDING-160 separately proves a split +at the native 8 MWU boundary and requires a coordinated incompatible-rule +upgrade plan. The bit-12 mainnet dates are now fixed at 2027-07-01 and +2029-01-01 UTC. Testnet history, final-SHA artifacts and `make check`, +the 8 MWU boundary proof on that SHA, and operator readiness remain +release gates. The historical unexplained CI failure has an accepted +disposition, but any new failure still requires investigation. Reusing +bit 12 for assets would change the meaning of its +earlier deployment and is excluded. ## 16. Proposed Operator Activation Sequence -This is a sequence for review, not a scheduled public-network activation. -The default public-network bit-13 start and timeout values remain dormant. +This is the operator sequence for the approved bit-13 windows, not an +authorization to publish the release or activate a public chain. Testnet +starts on 2027-04-01 and times out on 2028-10-01 at 1310/2016; +mainnet starts on 2029-01-01 and times out on 2030-07-01 at 1714/2016. +The mainnet bit-12 window starts on 2027-07-01 and times out on +2029-01-01 at 1714/2016. The approved FINDING-160 operator plan, +testnet-history review, and final release qualification remain gates +before public deployment. If bit 12 fails or activates too late for a +safe asset rollout, review the bit-13 window before it starts. 1. Deploy and review the native RIP-25 bit-12 implementation. Its BIP9 transition enables witness-v2 ML-DSA-44 script validation, the 12 MWU @@ -396,23 +418,158 @@ The default public-network bit-13 start and timeout values remain dormant. height `H` is the maximum of the three activation heights. Tests must cover the `H-2` through `H+2` boundary, restart, invalidate/reconsider, and reorgs across BIP9 `LOCKED_IN` and `ACTIVE`. -3. Before assigning public bit-13 parameters, obtain an independent - consensus and wallet review, reproducible cross-platform builds, and - a migration communication plan. Updating software without a network - deployment does not protect assets. +3. Before public deployment, verify the full shared testnet history and + bit-12 prerequisite, execute the incompatible block-rule upgrade plan, + obtain same-SHA final release artifacts, and communicate migration. + Updating software without a network deployment does not protect assets. 4. At or after `H`, newly created spendable asset outputs of every class, including `ASSET!` owner authority, must carry the canonical PQ program. - A spend of such an output requires its classical signature and a native - witness-v2 input with the matching program and valid network-context - ML-DSA-44 signature. The asset itself is not a witness program. + A spend of such an output has an empty asset scriptSig and requires a + native witness-v2 input with the matching program and a valid + network-context ML-DSA-44 signature. No ECDSA key authorizes the new + output. The asset itself is not a witness program. 5. Holders of older asset and owner-token outputs must explicitly transfer them into tagged outputs with their existing classical authorization. - They should obtain a canonical classical|PQ descriptor and fund its PQ + They should obtain a canonical `assetID|PQ` descriptor and fund its PQ native address with RVN for the first protected spend. There is no automatic or retroactive migration, and a compromised classical key can race a migration. 6. After each protected asset spend, inspect the native PQ anchor balance. Ordinary single-program native change can replenish it, but exact-fee, dust, explicit-change, and multiple-program cases may need a manual - RVN top-up before another spend. Backup and verify both private keys - and their descriptor association before moving administrative tokens. + RVN top-up before another spend. Back up and verify the ML-DSA key and + descriptor association before moving administrative tokens. Preserve + classical keys for historical UTXOs until those holdings are migrated. + +## 17. Current Anchor Funding Procedure + +This procedure documents FINDING-148, FINDING-150, and FINDING-151 while the +automatic refresh and raw-funding gaps remain open. A protected asset UTXO is +not lost when its native anchor is spent. Consensus still requires a matching +native witness-v2 input for its next spend, and the wallet rejects a protected +spend when it cannot find one. The owner can restore availability by sending +native RVN to the PQ address in the asset's original descriptor. Confirm that +payment, then retry the asset spend. Check every distinct PQ program involved +in a transaction. A payment to a different PQ address cannot authorize it. + +The following situations can leave no reusable anchor even when a protected +asset or owner token returns to the wallet: + +- an exact-fee or dust remainder with no native change output; +- a spend that consumes assets under multiple PQ programs, since one native + change output cannot refresh them all; +- an explicit native RVN change destination other than the relevant PQ + address; +- an ordinary RVN payment that selects the last funded asset anchor. + +For a raw protected asset spend, `fundrawtransaction` can add fee funding but +does not discover the required anchors from the asset inputs. Construct it +with these steps: + +1. Identify each protected asset outpoint being spent and record its committed + 32-byte PQ program and associated native witness-v2 address. +2. Select at least one funded native witness-v2 UTXO with the same program for + each distinct protected program. Include those outpoints explicitly in + the raw transaction's input list beside the asset outpoints. +3. Use a canonical `assetID|PQ` descriptor for each new asset output. Add a + positive, spendable native RVN output back to every PQ address whose last + anchor is being consumed. Reserve enough RVN for fees and future spends. +4. If using `fundrawtransaction` for the remaining fee, inspect its resulting + inputs and outputs. It neither supplies a missing matching anchor nor + guarantees that its change replenishes one. Sign the complete transaction, + inspect it again, then broadcast it. An unanchored protected spend is + rejected with `bad-pq-asset-anchor`. + +## 18. Interpreting the Current Asset Index + +FINDING-153 described an earlier dual-signature candidate in which two PQ +programs could share a classical address-index row. The PQ-only format now +derives a distinct synthetic asset identifier from each PQ program. A +historical classical output still has its original address identifier, so +index rows alone do not establish whether an outpoint needs a classical key +or a matching PQ anchor. Use the actual output's script and recorded +descriptor for custody and transfers. A descriptor assembled manually from +owned key material is not necessarily listed by `listpqassetaddresses`, +which lists persisted wallet pairings. + +## 19. Migrating Historical Assets and Owner Tokens + +FINDING-155 concerns partial movement of a historical asset UTXO. Activation +does not add a PQ program to outputs created earlier. Their classical key can +still authorize a spend, but every spendable asset output newly created after +activation needs a canonical PQ tag. Migrate before the old classical key can +be compromised: + +1. Back up the wallet and verify that the destination PQ key and the + classical keys for historical outputs can be recovered. Inventory + historical asset and `ASSET!` owner-token + outpoints, including their quantities and classical addresses. +2. For a single legacy asset UTXO, transfer its entire quantity to a new + canonical `assetID|PQ` descriptor. Transfer an owner token's full unit + the same way. If a source address holds several UTXOs, use explicit + outpoint selection when exact UTXO control is needed. +3. For a partial transfer through `transferfromaddress` or + `transferfromaddresses`, supply the canonical descriptor in the final + `asset_change_address` argument as well as a protected recipient. Without + it, the wallet rejects the transaction rather than creating classical-only + asset change. The GUI path has no equivalent explicit asset-change field. +4. Verify that every newly created spendable asset and owner-token output has + its intended PQ program. Fund each destination's native PQ address with + RVN before its first protected spend. Keep the original descriptor and PQ + key available for recovery, and retain classical keys for any historical + holdings. + +No software can distinguish the rightful owner from an attacker who already +controls the old classical key. Migration does not retroactively protect the +historical UTXO, and a compromised holder can be raced before confirmation. + +## 20. PQ-Only Asset Ownership Decision and Candidate Format + +The owner selected the native-RVN migration model for assets and owner tokens. +Outputs created under the bit-13 rule require an ML-DSA-44 key without an +ECDSA signature. Historical asset and owner-token outputs retain their +classical spending rule and migrate voluntarily, with no sunset. The +PQ-only format and wallet path passed paired CI on `9151946ad` under +FINDING-158; public activation and release qualification remain pending. + +This means a holder can reach PQ-only custody after migrating all historical +UTXOs, but consensus continues to accept classical spends of unmigrated +historical outputs. Bits 12 and 13 do not by themselves impose a global +PQ-only spending rule on the earlier UTXO set. + +An input carrying a native witness-v2 RVN output with the matching PQ program +provides the ML-DSA authorization for an asset input. The asset input has an +empty scriptSig and needs no ECDSA signature. The separate native input still +needs RVN funding and may need replenishment after the spend, as described in +section 17. + +The PQ-only asset locking prefix is exactly 25 bytes: + +``` +OP_1 OP_1 PUSH20 HASH160(P) OP_DROP OP_DROP +OP_RVN_ASSET +``` + +The asset opcode remains at byte 25, so the existing asset parser identifies +the output and extracts `HASH160(P)` as its address identifier. The prefix +executes to one true stack item with an empty scriptSig; it does not execute +ECDSA. The existing script interpreter treats every byte after +`OP_RVN_ASSET` as asset data. Consensus requires a matching valid witness-v2 +anchor whenever such an output is spent at an active origin height and +rejects the old dual-signature prefix on new outputs. The +`HASH160(P)` binding prevents a sender from selecting an unrelated qualified +or unfrozen classical address while assigning ownership to a different PQ +program. The 160-bit identifier and restricted-asset semantics were included +in the independent asset review; final release-SHA regression remains due. + +A red-then-green unit test shows that this prefix passes the existing +standard script check with an empty scriptSig, is recognized as an asset, +and is parsed only when the address identifier matches `HASH160(P)`. +Core 4.8.0 also accepted a 932-block regtest chain with 66 such outputs +and 35 later spends at ordinary block sizes. This does not resolve the +native block-limit split in FINDING-160. Wallet, RPC, Qt, migration and +consensus regressions passed paired release-target CI at `9151946ad`. +The owner-approved bit-13 dates are in `src/chainparams.cpp` at +`1dfae5b21`, with a red-then-green parameter regression and local +`make check`. Final-SHA CI and historical testnet compatibility review +are still required. diff --git a/doc/RIP-0025-PQ-Signatures.md b/doc/RIP-0025-PQ-Signatures.md index 1e78bb0eb6..6221867624 100644 --- a/doc/RIP-0025-PQ-Signatures.md +++ b/doc/RIP-0025-PQ-Signatures.md @@ -16,7 +16,11 @@ License: MIT This RIP proposes adding **ML-DSA-44** (FIPS 204) as a post-quantum digital signature scheme to Ravencoin via a new **witness version 2** program. New PQ addresses use ML-DSA-44 exclusively (no ECDSA). Existing ECDSA addresses (witness v0) continue working unchanged. Users can migrate native RVN from ECDSA to ML-DSA-44 addresses before quantum computers can break ECDSA. Ravencoin asset outputs are not covered by this version of RIP-25. -The upgrade is deployed as a **soft fork** following the SegWit extensibility model. A phased block weight increase from 8 MWU to 16 MWU, combined with a PQ witness discount factor, ensures that network throughput remains adequate during and after migration. +The witness-v2 signature rule uses the SegWit extensibility model and bit-12 +signaling. The combined deployment also raises the block weight limit from +8 MWU to 12 MWU at activation and to 16 MWU one nominal year later, and +discounts PQ witness bytes. Those block rules require a coordinated network +upgrade from Core 4.8.0. See Backwards Compatibility below. --- @@ -263,7 +267,10 @@ When a node encounters a witness version 2 program of length 32 bytes: 7. Verify: `ML_DSA_44_Verify(mldsa_pk, sighash, network_context, mldsa_sig)` (ML-DSA check) 8. If all checks pass, the input is valid -For unupgraded nodes, witness version 2 outputs are treated as "anyone-can-spend" per BIP141 rules, which is safe as long as a supermajority of miners enforce the new rules. +Unupgraded nodes treat witness version 2 outputs as "anyone-can-spend" +under BIP141. They do not enforce the ML-DSA signature rule, and miner +signaling does not make them compatible with the higher block limits or +PQ witness discount. See the public activation plan below. #### 3.4 Script Size Limits @@ -327,13 +334,45 @@ The wallet estimates transaction size before signing using `DummySignTx`. For PQ At 1-minute block times, even Phase 1 provides thousands of PQ transactions per minute, far exceeding current real-world usage. +The 12 and 16 MWU limits are part of the original RIP-25 plan. Core 4.8.0 +rejects blocks above 8 MWU. The new acceptance range and the PQ witness +discount make the combined bit-12 activation incompatible with an unupgraded +Core 4.8.0 node. A local two-binary regtest accepted a 7,600,636-WU block +on both binaries, then accepted an 8,801,320-WU block only on the bit-12 +candidate; Core 4.8.0 returned `bad-blk-length`. This is a hard-fork +boundary for block validation, even though the witness-v2 signature rule +alone is restrictive. A separate block with a valid ML-DSA witness-v2 +spend weighed 8,000,697 WU under the old calculation and about +7,998,831 WU after the PQ discount. Core 4.8.0 returned +`bad-blk-weight`, while the candidate accepted it. The discount alone +also crosses the old validation boundary. + +#### 4.2.1 PQ Signature Operation Cost + +Starting at bit-12 ACTIVE, each native or P2SH-wrapped witness-v2 ML-DSA +input contributes two units to the block signature-operation cost. Before +activation it contributes zero under this rule. The block limit remains +80,000 cost units, and the miner uses the same contextual count as block +validation. The value is a fixed consensus constant, independent of the +CPU or liboqs backend used by a node. + +Five paired portable-backend benchmark runs measured a median ML-DSA to +ECDSA verification-time ratio of 1.748 on the audited x86_64 system. Two +cost units cover that measured ratio; they are not a claim about every +future platform. Tests exercise native and wrapped inputs, pre-activation +counting, exact-limit acceptance, one-over rejection, and miner template +selection with stale cached costs. This cost adjustment is a consensus +change from the initial RIP-25 value of one and is included in the bit-12 +incompatible upgrade. + #### 4.3 Dust Threshold For PQ outputs, the spend cost increases proportionally to the ML-DSA witness size: ``` PQ witness input: mldsa_sig (2420) + mldsa_pk (1312) = 3732 bytes -With PQ discount: 3732 / 8 = ~467 weight units +With contextual PQ discount: 3732 / 8 = ~467 virtual bytes, +or about 1866 weight units for these bytes ``` ### 5. Activation Mechanism @@ -343,13 +382,18 @@ With PQ discount: 3732 / 8 = ~467 weight units ``` Deployment parameters: bit: 12 - nStartTime: <6 months after release> - nTimeout: <18 months after start> + mainnet nStartTime: 1814400000 (2027-07-01 00:00 UTC) + mainnet nTimeout: 1861920000 (2029-01-01 00:00 UTC) nOverrideRuleChangeActivationThreshold: 1714 (85% of 2016 blocks) - nOverrideMinerConfirmationWindow: 2016 (~33.6 hours) + nOverrideMinerConfirmationWindow: 2016 (~33.6 hours at the target spacing) ``` -The 85% threshold provides additional safety margin for this cryptographically significant upgrade. +The owner approved this 18-month mainnet window on 2026-10-13. BIP9 uses +median time past at window boundaries; the start timestamp is not an +activation date. At least 1714 signals in one complete STARTED window +lead to one LOCKED_IN window, then ACTIVE. If the timeout is reached +before lock-in, the deployment fails. The 85% threshold measures miner +signaling only; it does not establish readiness of validating services. ### 6. Implementation @@ -436,7 +480,7 @@ public: | Phase | Timeline | Description | |-------|----------|-------------| | **Phase 0: Preparation** | Months 1-6 | Software release with dormant PQ code. Community education. Testnet deployment. | -| **Phase 1: Activation** | Months 7-12 | Soft fork activates via BIP9. PQ addresses available. Block weight increases to 12 MWU. | +| **Phase 1: Activation** | Months 7-12 | Bit 12 activates after signaling and lock-in. PQ addresses are available. Block weight increases to 12 MWU, requiring upgraded validating nodes. | | **Phase 2: Encouraged** | Months 13-18 | Wallets default to PQ addresses for new keys. Warnings for legacy addresses. | | **Phase 3: Standard** | Months 19-24 | Block weight increases to 16 MWU. | | **Phase 4: Deprecation** | Months 25-48 | Legacy-only transactions increasingly discouraged. | @@ -450,10 +494,10 @@ Users migrate native RVN by sending it from legacy addresses to new PQ addresses 3. Sign with existing ECDSA key (standard legacy transaction) 4. Broadcast and confirm -After migration, native RVN change outputs can go to PQ addresses. On default -public-network parameters, asset-bearing change still uses the legacy asset -format. The separate dormant bit-13 extension defines protected asset change -for a future activation. +After migration, native RVN change outputs can go to PQ addresses. Until +the separate bit-13 rule activates, asset-bearing change still uses the +historical format. After activation, newly created spendable asset change +must use the PQ-only asset format. #### 7.3 Asset Scope @@ -466,16 +510,22 @@ RIP-25 witness-v2 protects native RVN outputs only. It does not change the Raven - qualifier and sub-qualifier assets. This scope statement describes the approved bit 12 baseline and the default -public-network state. A 4.8.1 working branch has a separate, dormant bit 13 -asset candidate with a classical-plus-PQ ownership rule. That candidate is -not included in the approved PR #1281 semantics and is not public-network -release-qualified. Its local wallet tests do not protect existing holdings -without an authorized migration. See the linked -asset extension note for its constraints and release blockers. +public-network state. A 4.8.1 working branch has separate, approved future +bit-13 windows for testnet and mainnet. Once bit 13 activates, new asset +and owner-token outputs +commit to a PQ program. Spending them requires a matching native RVN +witness-v2 input signed with ML-DSA-44, without an ECDSA signature. Asset +and owner-token outputs use an asset carrier keyed by the PQ program, not a +direct witness-v2 script. Outputs created before activation remain classically +spendable. Their owners may migrate voluntarily by sending them to new +PQ-protected outputs; activation does not protect historical holdings +retroactively. This candidate is outside the approved PR #1281 semantics +and is not yet qualified for public-network release. See the linked asset +extension note for its constraints and release blockers. A wallet or raw-transaction RPC must reject a witness-v2 PQ destination when constructing an asset-bearing output. Appending `OP_RVN_ASSET` data to an `OP_2 <32-byte-program>` script does not create a PQ asset output: it makes the script cease to be a witness program, and current consensus rejects the misplaced asset opcode. -Asset owners therefore retain a post-quantum exposure until a separately specified and activated PQ asset extension exists. In particular, theft of `ASSET!` can transfer administrative control and can authorize reissuance where the asset remains reissuable. See [RIP-25 PQ Asset Extension Design Note](RIP-0025-PQ-Assets-Followup.md). +Under the default public-network parameters, asset owners retain a post-quantum exposure. Even after bit 13 activation, historical outputs retain that exposure until their owners migrate them. In particular, theft of `ASSET!` can transfer administrative control and can authorize reissuance where the asset remains reissuable. See [RIP-25 PQ Asset Extension Design Note](RIP-0025-PQ-Assets-Followup.md). #### 7.4 Emergency Response Plan @@ -489,12 +539,66 @@ If ECDSA is broken before migration completes: ## Backwards Compatibility -This proposal is a **soft fork**. Backwards compatibility is maintained as follows: +The original draft described the whole proposal as a soft fork. That +description applies to the witness-v2 signature restriction alone, not to +the higher block limits or PQ witness discount. Core 4.8.0 validating nodes +can reject blocks that a post-activation node accepts. All validating +services that intend to follow the upgraded chain must install compatible +software before bit 12 becomes active. Miner signaling measures miner +readiness; it does not upgrade old nodes or guarantee their acceptance of +12/16 MWU blocks. The public activation plan below is an incompatible +consensus upgrade, approved for documentation by the owner. Release and +activation still depend on the listed qualification gates. + +### Public network activation plan for bit 12 + +1. Distribute the qualified 4.8.1 release before the mainnet bit-12 + STARTED window beginning no earlier than 2027-07-01 00:00 UTC by + median time past. Its timeout is 2029-01-01 00:00 UTC. Announce the + exact start, timeout, 1714/2016 threshold, and 2016-block window with + binaries, checksums, upgrade instructions, and the native RVN migration + path. Tell miners, pools, validating nodes, exchanges, custodians, and + explorers that Core 4.8.0 can reject blocks accepted by upgraded nodes. +2. Before requesting bit-12 signaling, validate the complete shared testnet + history against the network-bound ML-DSA rule. Repeat the two-binary + block tests around the Core 4.8.0 8 MWU boundary, including a PQ witness + spend whose old and new weights straddle that boundary. Qualify the + final versioned SHA with mandatory `make check`, all release targets, + and the Final Gate. If a required gate is missing or the network is not + ready, change the public parameters in a reviewed release before the + STARTED window rather than relying on miner signaling to protect old + validators. +3. Bit-12 BIP9 signaling measures miner readiness. LOCKED_IN is followed + by ACTIVE after the next full window. All validating services that need + to follow the upgraded chain must update before ACTIVE. Signaling does + not measure their adoption, and there is no automatic rollback to the + Core 4.8.0 weight rules after ACTIVE. +4. At the bit-12 activation height `H12`, the limit becomes 12 MWU and + the contextual PQ witness discount takes effect. At `H12 + 525600` + blocks, one nominal year at the 60-second target spacing, the limit + becomes 16 MWU. This second phase is height-based, not a fixed calendar + date. Operators must treat both phases as part of the same incompatible + network upgrade. +5. The bit-13 mainnet STARTED timestamp is also 2029-01-01 00:00 UTC. + Bit 13 cannot enforce PQ-only asset spending until bit 12 and the + transfer-script deployment on bit 8 are ACTIVE. + If bit 12 fails or activates too late for a safe asset rollout, review + the bit-13 window in a separate release before it starts. Do not treat + the shared timestamp as evidence that bit 12 has activated. + +The old and new binaries agreed below 8 MWU in the recorded regtest +comparison. Core 4.8.0 rejected a larger candidate-accepted block and a +separate discounted PQ-witness block just across its old 8 MWU boundary. +The exact blocks, weights, and outcomes are recorded in FINDING-160 of the +security remediation register. Approval of this plan does not assert that +testnet history or final release qualification has already passed. + +The remaining transaction compatibility properties are: - **Unupgraded nodes**: See witness v2 outputs as "anyone-can-spend" per BIP141 rules - **Legacy addresses**: Continue to work indefinitely - **Legacy transactions**: Continue to be valid. No existing transaction type is modified -- **Asset transactions**: Unchanged by the approved bit-12 baseline. The separate dormant bit-13 extension retains the legacy P2PKH condition and adds a required PQ anchor for newly created asset outputs after its own activation +- **Asset transactions**: Unchanged by the approved bit-12 baseline. The separate bit-13 rule requires a PQ-only carrier and matching native ML-DSA anchor for newly created spendable asset outputs after its own activation. These outputs need no ECDSA signature; historical outputs retain their classical rule - **Migration**: Voluntary. Users migrate funds at their own pace The network context was added before mainnet RIP-25 activation. Mainnet has no diff --git a/doc/RIP-0025-v4.8-security-remediation-register.md b/doc/RIP-0025-v4.8-security-remediation-register.md index 8f4e887310..ecf0972612 100644 --- a/doc/RIP-0025-v4.8-security-remediation-register.md +++ b/doc/RIP-0025-v4.8-security-remediation-register.md @@ -949,7 +949,10 @@ from the demonstrated coverage gaps. encryption still rotates the legacy HD seed/keypool as before. The only new externally visible behavior is fail-closed recovery when the required plaintext-slack removal has not completed. -- **Final status:** FIXED +- **Final status:** FIXED under FINDING-168 at `9151946ad`. Quarantine and + restart recovery remain intact; the patched Berkeley DB now clears full + pages and the wallet rejects an unsafe rewrite dependency. Both required + CI runs passed on that SHA; final release-SHA qualification remains due. ### FINDING-019 : Nested witness deserialization expands 16 MB to hundreds of MiB @@ -1086,6 +1089,14 @@ from the demonstrated coverage gaps. corruption regressions. The broader checker still relies on structural fragments for properties without mutation controls. The revised workflow has not run on GitHub, so this finding is not closed. + `0e1399a84` adds six workflow negative controls: a dependency hidden in a + YAML comment, a non-executing `make check`, and pre/post exact-SHA checks + hidden in comments for both Build Raven and Final Gate. All six fail when + mutated and pass on the restored workflows. The same change requires the + new reward-restart and PQ wallet-recovery regressions in both workflows and + the local behavioral gate. These controls cover only the named workflow + properties; the broader mutation matrix and final exact-SHA CI are still + due. - **Final status:** OPEN, partially mitigated. The false two-platform release assertion is removed, but the checker cannot yet certify every security property it describes, and remote artifact-level evidence is absent. @@ -1172,9 +1183,13 @@ from the demonstrated coverage gaps. contains `-DCMAKE_SYSTEM_PROCESSOR=aarch64`; the native pinned depends build, exact configure probe, `make check`, and the invariant gate pass. A complete aarch64 node cross-build remains part of FINDING-065 CI qualification. -- **Final status:** MITIGATED. The configuration defect is corrected, but this - host has no aarch64 cross compiler and therefore cannot supply the final - artifact-level proof locally. +- **Final status:** FIXED. The configuration correction passed the aarch64 + cross-build in Build Raven run `38885751826` and Final Gate run + `38885752445` at `1e58379df7c3ede470f8562711070d2b8152d10a`. + The downloaded aarch64 package verified against its SHA-256 manifest, + embedded source commit, and the common five-target source digest + `ecc323108253549a6728521aedaf7bb2c9ffc9d62259c9423555392bed661b73`. + The final release SHA still requires the same full-matrix qualification. ### FINDING-023 : Release workflows omit documented supported artifacts @@ -1230,6 +1245,18 @@ from the demonstrated coverage gaps. artifact-level release qualification remains open until the remote matrix and detached signing/assembly are verified at the final audited SHA. +**2026-10-17 artifact checkpoint:** Build Raven run `38885751826` and Final +Gate run `38885752445` passed at exact commit +`1e58379df7c3ede470f8562711070d2b8152d10a`. The five Build Raven +packages for Linux x86_64, ARM32, AArch64, Windows, and macOS were +downloaded. Every package manifest, contained file digest, target/host +provenance, and embedded commit matched; the common source archive digest +was `ecc323108253549a6728521aedaf7bb2c9ffc9d62259c9423555392bed661b73`. +This satisfies the previously missing five-target unsigned artifact proof +for that SHA. FINDING-023 remains MITIGATED until the final release SHA is +qualified and detached signing, installer/DMG assembly, and signed checksums +are complete. + ### Regression-of-fixes conclusions | Earlier remediation | Second-audit result | @@ -2132,8 +2159,18 @@ was changed before these findings were frozen. - **Regression required:** Two independent mnemonic vectors must produce distinct, stable identifiers across reload; a legacy constant-ID wallet must migrate deterministically without changing any derived key. -- **Remediation commit:** PENDING -- **Final status:** OPEN +- **Remediation commit:** `64fb051f4`. +- **Verification:** Two different mnemonic vectors produce different stable + seed IDs; repeating one mnemonic reproduces the ID and first derived key. + Plaintext and encrypted legacy constant-ID wallets migrate the current + chain and only metadata whose stored public key matches rederivation from + the current seed. The encrypted test injects a Berkeley DB transaction-begin + failure and confirms the wallet remains locked with its original database + and in-memory metadata; a later unlock completes migration. The existing + derived key and chain counter remain unchanged. The red regression found + the former constant ID and lack of migration. All focused cases, the wallet + suite, and `make check -j4` passed after the fix. +- **Final status:** FIXED locally; final exact-SHA CI qualification remains due. FINDING-033 and FINDING-034 extend the unresolved HIGH list; FINDING-035 is MEDIUM and FINDING-036 is LOW. The supplemental verdict remains **FAIL**. @@ -2713,8 +2750,24 @@ audited commit and both authoritative baselines. passphrase, and seed while keeping a complete record set. Every case must return `DB_CORRUPT`, leave no usable seed, and advance no HD/keypool counter; an official Trezor tuple must reload and derive the fixed expected pubkey. -- **Remediation commit:** PENDING -- **Final status:** OPEN +- **Remediation commit:** `502a6ea673f5fe08f68bd0e9784eaf9813d4a8d6`. +- **Modified files:** `src/wallet/walletdb.cpp` and + `src/wallet/test/pq_wallet_tests.cpp`. +- **Red regression:** The new complete-tuple test failed eight assertions + before the loader correction across altered word hash, invalid mnemonic, + passphrase, and seed records. The valid Trezor tuple passed. An additional + negative control removed the mnemonic checksum check while retaining the + other checks; the invalid-mnemonic case then loaded and exposed a usable + seed, causing two test failures. +- **Green verification:** The loader stages plaintext records in secure + buffers, checks the word hash and mnemonic checksum, rederives the BIP39 + seed, compares it in constant time, and installs the tuple only after the + complete database scan validates. Corrupt tuples return `DB_CORRUPT` with + no usable seed or advanced HD/keypool counter. The focused test, all 60 + `pq_wallet_tests` cases, and `make check -j4` passed after restoration of + the checksum check. +- **Final status:** FIXED locally. Exact-SHA cross-platform CI qualification + remains required for the 4.8.1 release. FINDING-045 extends the unresolved MEDIUM list. The supplemental verdict remains **FAIL**. @@ -2939,8 +2992,16 @@ before these findings were recorded. - **Regression required:** Force `DB_RUNRECOVERY` (or another negative BDB result); `MakeMock` must throw and both initialization flags must remain false. -- **Remediation commit:** PENDING -- **Final status:** OPEN +- **Remediation commit:** `b2e5c8cafc295b71ab0d335a25416aa5a6960bec` + (shared with FINDING-050). +- **Verification:** `database_mock_negative_open_failure_renews_handle` + forces a negative `DB_RUNRECOVERY` result, checks that `MakeMock` throws, + and checks that neither initialized nor mock state is published. The test + failed before the correction and passed afterward under `make check` and + the mandatory invariant gate. The FINDING-050 checkpoint records the same + red-then-green evidence. +- **Final status:** FIXED. The negative-open path is covered by the + regression and the final exact-SHA `make check` jobs. FINDING-047 through FINDING-049 extend the unresolved LOW, MEDIUM, and INFO lists. The supplemental verdict remains **FAIL**. @@ -3719,23 +3780,42 @@ before closing that finding. one; activation gating was corrected by `3de7a3c111`. This is inherited from the approved RIP-25 cost model. - **Concrete exploitability:** On the audited portable backend, one ML-DSA - verification costs about 1.39 secp256k1 verifications. Cost one therefore + verification costs a measured median 1.748 secp256k1 verifications. Cost one therefore understates CPU while the 8x witness discount admits more PQ signatures per serialized byte. Weight remains an independent cap, so impact is MEDIUM. - **Expected behavior:** One documented, activation-gated, platform- independent fixed constant is at least the conservative measured ratio and applies identically to native and P2SH witness-v2 paths. - **Proposed remediation:** Add a reproducible benchmark and a named consensus - constant of two, subject to final protocol-owner review, with no runtime or - platform-dependent selection. + constant of two, with no runtime or platform-dependent selection. The owner + approved cost two with boundary regressions on 2026-10-19. - **Regression required:** Pre-activation zero; first active block exact cost; native/P2SH equality; malformed alternate encodings cannot bypass cost; - exact block-limit accepted and one-over rejected; miner and validator agree. + exact block-limit accepted and the first PQ step above rejected; miner and + validator agree. - **Consensus adaptation notice:** Raising the cost from one to two is an explicit consensus change from PR #1281, justified by portable benchmark evidence and requiring activation-boundary review. -- **Remediation commit:** PENDING -- **Final status:** OPEN +- **Remediation commit:** `af5783d7857ddb8ec2cf9d79b9dd4deae0837d01` + names the fixed consensus cost and applies it in the shared native/P2SH + witness-v2 counting path after bit 12 becomes ACTIVE. +- **Verification:** Before the change, the new unit assertions failed on + native and wrapped cost and the saturated boundary; miner admission and + stale-cache assertions failed; the old daemon accepted the 79,999-cost + block that the new rule must reject. Private red logs are + `evidence/f061-red-sigop.log`, + `evidence/f061-red-miner-admission.log`, + `evidence/f061-red-miner-stale-cache.log`, and + `evidence/f061-red-functional.log`. After the change, the focused unit, + miner, and full-chain regtest cases passed: 80,000 units including two PQ + inputs were accepted, 80,002 with three were rejected, and 80,000 legacy + units were accepted. `make check -j4` passed. The complete structural and + behavioral invariant gate passed all 14 required functional tests with + the locally available Python 3.10 runtime. The first local gate attempt + used Python 3.12 and stopped at old functional-test imports of removed + `asyncore`; this was an interpreter mismatch, not a node test failure. +- **Final status:** FIXED locally; paired exact-SHA CI and release artifacts + remain due under FINDING-067. ### FINDING-062: PQ consensus vectors do not cover the accepted byte boundary @@ -3772,17 +3852,40 @@ before closing that finding. one-long, bad key length, bad stack count, bad program/hash, wrong version, appended sighash byte, alternate mode, bit flips, pre-activation, boundary, reorg, and sigop-over-limit cases. -- **Remediation commits:** `2dc33e518`, `4ab366663`, and CI gate - `5ebeb2b09`. +- **Remediation commits:** `2dc33e518`, `4ab366663`, CI gate + `5ebeb2b09`, synthetic branch test `1e33543fd`, and full-chain + regression `6cab6706e`. - **Verification:** Static production-signed valid and one-bit-invalid PQ transactions now run through the existing `tx_valid` and `tx_invalid` JSON vector infrastructure with explicit mainnet context. A separate 13-case C++ mutation matrix checks malformed stack shape, signature, public key, program, and suffix behavior. Both polarities and the mutation matrix pass under `make check` and the mandatory behavioral gate. -- **Final status:** MITIGATED. Exact activation-height plus/minus two, - invalidate/reconsider, and PQ sigop-over-limit transaction vectors remain - outstanding; FINDING-061 covers the unsettled sigop cost. +- **Final status:** FIXED locally for the current two-unit PQ sigop rule. + Exact-SHA CI remains due. FINDING-061 records the owner's approved cost + change and its separate red-green evidence. + +Post-checkpoint extension: `1e33543fd` adds a synthetic BIP9 branch test for +the two candidate blocks before and after bit 12 activation. It checks the +8 MWU to 12 MWU weight and serialized-size transition, witness discount, +and a competing unsignaled branch. A deliberate mutation retaining the +8 MWU limit after activation failed six assertions; the restored source +passed the focused test and `make check -j4`. +The full-chain extension `6cab6706e` then verified a valid ML-DSA spend +in candidate block H432, rejection of a malformed spend at H432, acceptance +of that same spend on a pre-activation H431 side branch after active-chain +validation, invalidate/reconsider with `verifychain(4, 2)`, rejection at +80,001 sigops when the PQ input adds one, and acceptance of the 80,000 +legacy control. The malformed side-branch case failed on the old code and +passed after the regtest versionbits override was added. The focused test +passed twice; `make -j4 check` passed after the final code was built. +FINDING-062 is FIXED locally for the current consensus cost, with exact-SHA +CI qualification still pending. + +The later `af5783d78` update changes the active PQ sigop cost to two. Its +full-chain regtest accepted 80,000 units including two PQ inputs, rejected +80,002 units including three PQ inputs, and accepted a legacy-only 80,000 +control. The frozen one-unit results above remain historical evidence. ### FINDING-063: Repository KAT coverage cannot detect backend drift @@ -3829,6 +3932,18 @@ production signature through the real script path. These tests pass under signature byte vector, nor does it execute full `ConnectBlock`, so the finding remains MITIGATED rather than fully closed. +Post-checkpoint extension: `84a5e350b` pins every byte of the ML-DSA-44 +deterministic signature for the liboqs 0.12.0 FIPS 204 ACVP sigGen group 1, +case 4 vector against the pinned 0.16.0 backend. The expected signature was +deliberately changed for a red test, then restored for a green focused test +and `make check -j4`. A full `ConnectBlock` vector remains outstanding, so +FINDING-063 remains MITIGATED. + +The `af5783d78` regtest extension pins a mnemonic-derived regtest PQ address +and takes its signed spend through full `ConnectBlock` at the bit-12 boundary. +It does not pin the full block's deterministic transaction ID and sighash +across all three networks, so the broader KAT finding remains MITIGATED. + ### FINDING-064: Fuzzing never reaches the real ML-DSA verifier - **Severity:** MEDIUM @@ -4100,9 +4215,9 @@ documentation, policy, wallet, or consensus change. explicit PQ asset change rejects, raw native-RVN PQ output construction remains available, and PQ native-RVN change is separated from automatically generated P2PKH asset change. Structural invariant lint passed. -- **Final status:** MITIGATED. The false security claim and unsafe construction - behavior are corrected. Asset ownership itself remains deliberately outside - RIP-25 and requires the separately specified follow-up consensus extension. +- **Final status:** OPEN for the requested asset-inclusive 4.8.1 release. + The earlier native-RVN-only scope corrected the false security claim and + unsafe construction behavior, but did not protect asset ownership. **4.8.1 continuation at `1416e69ea20e636cef46c663bba421a912c9f0e1`:** The owner subsequently required an asset-inclusive RIP-25 extension and @@ -4345,6 +4460,510 @@ documented limitations, not a consensus bypass. FINDING-067 remains OPEN for public activation, cross-platform CI, exact-SHA artifact qualification, and independent review; no 4.8.1 asset-protection release is certified here. +**2026-10-09 asset release checkpoint at 870a0bee3:** Independent review of +bit-13 consensus, wallet custody and recovery, raw RPCs, and Qt is complete. +FINDING-148 and FINDING-150 through FINDING-155 were frozen before their +respective remedies or documented limitations. FINDING-067 remains OPEN +until all of these release conditions hold: + +1. Final Gate, including mandatory make check, and all five Build Raven + artifacts pass at one exact final release SHA. The macOS runner loss in + FINDING-156 must be resolved by a successful artifact build at that SHA. +2. FINDING-143 through FINDING-147 receive cross-platform CI evidence on + that SHA. FINDING-154 needs an interactive pinned-Qt list, tree, and + clipboard regression with two distinct PQ programs sharing one + classical key; the local pinned Qt build alone is insufficient. +3. The owner approves the bit-13 mainnet and testnet BIP9 parameters after + checking the bit-12 prerequisite and historical testnet PQ signatures. + The public-network parameters remain dormant in this checkpoint. +4. The legacy asset and owner-token migration instructions remain explicit. + Exact-fee, multiple-program, explicit-native-change, generic-native-spend, + raw-funding, index, and partial-GUI migration limits remain identified + with fail-safe regression evidence and a documented recovery route. +5. FINDING-158 is resolved with a qualified PQ-only spending rule for new + asset and owner-token outputs. The current candidate requires both a + classical asset signature and an ML-DSA anchor, so it does not meet the + owner's confirmed rule. Historical outputs retain classical spending + and migrate voluntarily, as recorded below. + +The 4.8.1 version bump, final-SHA rerun, tag, and GitHub release are +separate release steps. No tag or release is authorized by this checkpoint. + +**2026-10-09 parameter decision:** The owner approved a bit-13 proposal, +conditional on CI qualification: testnet starts at 1806537600 and times +out at 1853971200 with threshold 1310/2016; mainnet starts at +1861920000 and times out at 1909094400 with threshold 1714/2016. +The owner also approved extending the proposed mainnet bit-12 timeout +from 1830297600 to 1846022400, which makes the documented window +18 months after its 1798761600 start. These are approval records, +not committed consensus parameters. Historical testnet PQ-context +compatibility still needs evidence. The owner requested discussion +before deciding whether the dual-signature candidate meets the asset +release objective, so FINDING-067 remains OPEN. + +**2026-10-09 asset ownership decision:** The owner confirmed that the asset +rule must follow the native RVN migration model: new asset and owner-token +outputs must be spendable with ML-DSA alone, without a classical asset +signature. Historical asset and owner-token outputs remain spendable with +their existing classical keys and may be migrated voluntarily. There is no +legacy-signature sunset in the 4.8.1 scope. This supersedes the earlier +sunset request in FINDING-157 and exposes the separate candidate defect in +FINDING-158. The previously approved bit-13 dates and thresholds applied +to the dual-signature candidate; do not activate a redesigned PQ-only rule +until its compatibility, regressions, and deployment parameters are reviewed. +FINDING-067 remains OPEN. + +**2026-10-13 exact-SHA qualification checkpoint:** Build Raven run +`38296258242` and RIP-25 v4.8 Final Gate run `38296262444` both succeeded +at `9151946ad27857d4d4c0016c74819476d1296557`. The mandatory +`make check`, wallet rewrite and full-page-clear regressions, nine focused +security functional tests, and the 32-case functional matrix passed in the +required security jobs. Final Gate also completed its seven build jobs. +Build Raven produced five unsigned artifacts on that SHA: Linux x86_64 +`11770728793`, aarch64 `11771315730`, arm32v7 `11770548092`, Windows +MinGW `11771318535`, and macOS `11771367693`. The Linux artifact's +published SHA-256 file verified locally, and its provenance names the +same full SHA. The unmodified Linux `raven-qt` artifact launched without +the workaround needed by the local GCC 13 source build. On a private +regtest fixture with two different PQ-only recipients, Asset Control +showed separate rows and tree groups; Copy address returned each complete +descriptor. This qualifies FINDING-089, 143-147, 152, 154, 159, 161-165, +167, and 168 on this candidate SHA. FINDING-148, 150, and 151 remain +documented fail-safe limitations with passing recovery regressions. +FINDING-158's implementation passed CI. A separate two-binary regtest +confirmed that Core 4.8.0 accepts ordinary-sized candidate blocks with +PQ-only asset outputs and later anchored spends. Public activation remains +blocked by FINDING-160 and the release steps below. The old unexplained +`make check` failure in FINDING-166 remains separately open. This is an +intermediate 4.8.0-versioned SHA, not the final 4.8.1 release SHA: +approved public deployment parameters, the 4.8.1 version change, final +same-SHA artifact qualification, and release approval remain outstanding. + +**2026-10-13 approved bit-13 parameter implementation:** Commit +`1dfae5b21` sets the owner-approved PQ-only asset deployment windows in +`src/chainparams.cpp`: testnet 2027-04-01 to 2028-10-01 UTC at +1310/2016, mainnet 2029-01-01 to 2030-07-01 UTC at 1714/2016. +Regtest remains dormant by default with an explicit test override. The +`pq_asset_public_deployment_parameters` unit regression failed against +the prior all-dormant values with four mismatches, then passed after the +change. Local `make -j8 check` passed, and the full RIP-25 invariant gate +passed with all nine focused functional tests. This commit follows the +green CI pair at `9151946ad`, but the new SHA has not yet received its +own Build Raven and Final Gate qualification. + +At the `1dfae5b21` checkpoint, FINDING-067 remained HIGH and OPEN. Its +then-remaining release conditions were: + +1. Carry the owner-approved FINDING-160 incompatible network upgrade plan + into the versioned release notes and operator instructions. The plan + preserves the original 8 to 12 to 16 MWU transition and contextual PQ + witness discount. Core 4.8.0 rejects allowed post-bit-12 blocks; + miners, pools, validating nodes, and services must upgrade before + ACTIVE. Repeat the boundary checks on the final SHA and establish + network readiness before requesting signaling. +2. Finalize the mainnet bit-12 start date with the owner, then apply the + approved 18-month timeout relative to that date and requalify its + consensus parameters. No bit-12 public parameter change is included + in `1dfae5b21`. +3. Revalidate the shared testnet history for the earlier empty-context PQ + signature rule and document a compatible deployment boundary if needed. +4. Bump the version to 4.8.1, rerun mandatory `make check`, all five + Build Raven artifacts, and Final Gate on one exact final SHA, then + complete release notes, checksums, and migration guidance. The final + private audit report and explicit tag and release approval follow. + +The owner accepted FINDING-166's evidence-based historical disposition on +2026-10-13: the original test failure cannot be classified more narrowly +without the missing log, while the diagnostic workflow defect is fixed. +This does not waive mandatory `make check` on the final release SHA or +permit a future failing gate to be ignored. + +The legacy asset and `ASSET!` migration path is voluntary and has no +sunset. Newly created post-bit-13 asset outputs need an ML-DSA anchor +and no ECDSA signature; the synthetic asset identifier has no classical +private key. FINDING-148, 150, 151, and 155 remain documented fail-safe +operational limits rather than closed automatic funding features. A holder +reaches PQ-only custody after migrating every historical RVN, asset, and +owner-token UTXO they hold. Bits 12 and 13 do not globally disable +classical spends of unmigrated historical outputs. + +**2026-10-13 bit-13 parameter CI checkpoint:** Build Raven run +`38335505917` and RIP-25 v4.8 Final Gate run `38335508527` both passed +on exact SHA `6bdbae2b485eaf21435e1b9b6ed3f8b542fdd384`, which includes +the approved bit-13 timing and the current PQ-only documentation. Both +required security jobs passed mandatory `make check`, behavioral invariants, +and focused functional tests; Final Gate also completed all eight build +jobs. Build Raven produced all five unsigned targets: Linux x86_64 +artifact `11785747342`, arm32v7 `11785807889`, Windows MinGW +`11786074980`, aarch64 `11786238569`, and macOS `11786669670`. +The downloaded Linux archive's published SHA-256 file verified locally; +its digest is `17a869288918a0576ff7c355a00dacae70246d76bd70b123dc310b3915797e95`, +and `PROVENANCE.txt` names the full SHA. These remain version 4.8.0 +candidate artifacts. The final 4.8.1 versioned SHA still needs a paired +rerun after the open network and testnet gates are resolved. + +All five artifacts were subsequently downloaded. Every file listed in +each `SHA256SUMS` matched its downloaded bytes; each standalone SHA-256 +file matched the corresponding manifest; every `PROVENANCE.txt` named the +same full commit SHA and the correct target. The common source archive +digest was `03876a8c194ea861a1a5699e463db8641ed07f1f81f257599aace46b62e1ff5d`. +The primary target digests were: arm32v7 +`acc775be63b88652404eb85d98ee102357b605d8a3458aa686b5285bd3701203`, +aarch64 `cfcf7009e04ee8d39eebee3f328dd50181ed5933bb8e42c947bf6eedfc4d4228`, +Windows win64 ZIP `ca0fc7b6697a14920c84fc43722e4b735001da12b3214a6da967bf656fdf4195`, +and macOS osx64 archive +`73e787b29745b561a1c286406fb62ecd5c4eb2f6c6a1f867869d621f8e328e34`. +The Windows unsigned installer and macOS unsigned DMG also matched their +published manifest entries. This verifies artifact integrity and source +identity; it does not replace execution on each target platform. + +**2026-10-13 testnet-history scan:** The isolated, unpruned, no-wallet +testnet node synchronized through height 2551767, hash +`00000b4e32c9846b4cdbd341a80cd8a5294412bec7199621843b6dc9b8cf1105`. +The private block scanner examined all 2551768 blocks from genesis through +that hash, containing 2772158 transactions. It checked native witness-v2 +program lengths 2 through 40, their spends, P2SH-wrapped witness-v2 +redeems, non-32-byte programs, and malformed redeem candidates. All five +candidate counts were zero. Its 1267 saved checkpoints and the final block +hash agreed with the node, and the source and completion tips had the same +chainwork `000000000000000000000000000000000000000000000000000f229e8df3b0e5`. +The private evidence is `testnet-history-scan.json`, completed at +2026-10-13 12:32:13 UTC. This excludes historical empty-context PQ spends +on the examined active testnet chain. The synchronizing node identified +itself as `v4.8.0.0-a809f6757-dirty`; its exact clean source provenance +is not established. A full chainstate revalidation with a clean final +4.8.1 binary and `-assumevalid=0` remains required before release. + +**2026-10-13 clean 4.8.1 testnet replay checkpoint:** A disconnected, +unpruned, no-wallet testnet node rebuilt the chainstate with +`-reindex-chainstate -assumevalid=0` from genesis through height 2551779. +The running binary was the clean 4.8.1 build from `17e2d5c97`, SHA-256 +`6abe722ad054020baebd48e9e223e966dbebb4aeaf6397bbee01e17036cc46fe`. +It reached the known headers with no RPC warnings, at hash +`000006bfde295576c7ed2dcb3f832f3ca9331c16644b35099a8591250917070f` +and chainwork +`000000000000000000000000000000000000000000000000000f229e8eb57277`. +At height 2551767 the block hash matched the earlier full-block scan. +The private provenance is `evidence/testnet-reindex-17e-final.json`. +This validates the scanned history with a clean 4.8.1 candidate; +the final source SHA must repeat the replay before release. + +**2026-10-13 bit-12 mainnet parameter decision and implementation:** The +owner selected 2027-07-01 00:00 UTC (`1814400000`) for STARTED and +2029-01-01 00:00 UTC (`1861920000`) for timeout, an 18-month window at +1714/2016. This supersedes the earlier approval of an 18-month timeout +measured from the provisional 2027-01-01 start. Commit `2df4d2d25` +sets the two mainnet parameters and adds +`versionbits_tests/pq_hybrid_mainnet_activation_test`. Before the code +change, the new test failed on both old timestamps, `1798761600` and +`1830297600`. After the change it passed, including median-time-past +STARTED, the 1713/1714 signal boundary, LOCKED_IN, ACTIVE, and timeout +FAILED transitions. The complete `versionbits_tests` and +`rip25_versionbits_tests` suites passed, as did local `make -j8 check`. +The bit-12 dates passed the versioned paired CI checkpoint below, +but final release-SHA qualification remains due. The bit-13 mainnet +STARTED timestamp +equals the bit-12 timeout; this does not imply bit 12 has activated. +If bit 12 fails or activates too late for the asset rollout, the bit-13 +window must be reviewed in a separate release before it starts. Commit +`948b1ea20` records the approved schedule and operator plan in the +protocol and asset notes and adds draft versioned 4.8.1 release notes. + +**2026-10-13 versioned 4.8.1 paired CI checkpoint:** Build Raven +`38433449366` and Final Gate `38433455013` both passed on exact SHA +`bfa2efbb3f47dca3e32df3c73b74625d5c64b5ee`. Both required +security jobs completed mandatory `make check` and behavioral tests; +Final Gate completed all seven builds. Build Raven produced all five +release targets: Linux x86_64 artifact `11835601861`, arm32v7 +`11836926163`, aarch64 `11837205453`, Windows MinGW `11835885092`, +and macOS `11836521468`. All files named by their downloaded +`SHA256SUMS` matched their bytes, each standalone SHA-256 file agreed, +each `PROVENANCE.txt` named this exact commit and target, and all five +targets contained the same source archive digest +`94ed2505ad5f64482faf979133531514ba5b2a2bfb6fa575c410e77effaa0d98`. +The Windows unsigned installer and macOS unsigned DMG also matched +their manifests. The private detailed evidence is +`evidence/ci-bfa2efbb3/artifact-verification.json`. This candidate +precedes the optional `raven-tx` repair in FINDING-172 and the latest +documentation; it is not the final release SHA. + +FINDING-067 remains HIGH and OPEN. The outstanding gates are clean +final-SHA revalidation of the scanned testnet history, final 4.8.1 source and artifact +qualification at one SHA with mandatory `make check`, a repeat of the +8 MWU old/new block boundary evidence on that SHA, final release notes +and checksums, operator readiness before bit-12 ACTIVE, and the separate +owner approval for the tag and release. The bit-12 parameter decision +itself is no longer an open gate. Versioned 4.8.1 release notes are a +review draft until the final SHA and artifacts are known. + +**2026-10-13 bit-12 CI checkpoint:** RIP-25 v4.8 Final Gate run +`38366157600` passed on exact SHA `7c0d43c85550afc310a7a3a2e458fda3a274fc4f`, +including its security job and all seven build jobs. Paired Build Raven +run `38366146893` failed while creating the functional test cache in its +required behavioral security step. Its five artifact jobs were skipped. +The cause and remediation are tracked in FINDING-169. This is not a green +cross-platform qualification pair. Commit `7c7ef25b9` subsequently +clarified that miner signaling measures signaling only; it is documentation +only and is not included in those two runs. + +**2026-10-13 release translation preparation:** Commit `26726e6a8` +regenerates the English Qt source catalog and extracted core strings +from the current 4.8.1 candidate, using GNU gettext 0.21 and the pinned +Qt 5.12.11 `lupdate`. The pinned `lrelease` accepted the updated catalog; +local `make -j8 check` passed. This is an English source refresh, not a +pull of translated language catalogs from Transifex. The latter remains +a release-process step because the `tx` client is not installed here. + +**2026-10-13 pinned local Qt and manual preparation:** The local 4.8.1 +candidate at `0d2552fad` configured with the depends `config.site`, +Qt 5.12.11, static Qt libraries, and the QMinimal plugin. Its full +build and mandatory `make -j4 check` passed. The daemon, CLI, and Qt +reported `v4.8.1.0-0d2552fad`. Commit `a468e2ab2` regenerates all four +4.8.1 manpages from those current utilities with help2man 1.49.3; +the Qt help command ran under a virtual X display. The optional +`raven-tx` manpage came from a manually linked copy of the same current +sources and libraries before the Automake repair in FINDING-172. +Manual end-user GUI interaction on the final SHA remains due. + +**2026-10-14 exact-candidate qualification checkpoint:** Build Raven +38475261912 and RIP-25 v4.8 Final Gate 38475261551 both completed +successfully on the same versioned 4.8.1 source commit +a16a05bdb925985bc33694455e582cd7d2261958. Both required security +jobs passed mandatory make check and the optional raven-tx build. +Final Gate passed its security job and all seven build jobs. Build Raven produced Linux x86_64 +artifact 11858960806, aarch64 11858772223, arm32v7 11858057542, +Windows MinGW 11857481025, and macOS 11856949763. Every downloaded +file matched its target SHA256SUMS manifest and standalone checksum; +each PROVENANCE.txt named the exact source commit and target. All five +targets contained the same source archive with SHA-256 +e4d5ec80c1808c2c6ddfc659e9d036c0624d5e3594c1bba62a671a2a07434a32. +The unsigned Windows installer and macOS DMG also matched their +manifests. The detailed private result is +evidence/ci-a16a05bdb/artifact-verification.json. These checks prove +package integrity and provenance, not GUI behavior on every platform. + +The clean a16a05bdb daemon, binary SHA-256 +48610aafe1d145586419fda9920517442930743f8b1b937ca4c5b56df289267e, +replayed the available non-pruned testnet chainstate from genesis with +-reindex-chainstate and -assumevalid=0. It reached height 2,551,779, +or 2,551,780 locally known headers including genesis, tip +000006bfde295576c7ed2dcb3f832f3ca9331c16644b35099a8591250917070f, +chainwork 000000000000000000000000000000000000000f229e8eb57277, +and no RPC warnings. The first history scan had read heights 0 through +2,551,767; the clean replay validated those blocks and matched its last +checkpoint. + +The same binary then connected to four public testnet peers and +validated 897 more blocks. At the frozen 2026-10-14 04:49:44 UTC +snapshot, blocks and headers both equaled 2,552,676, tip +0000066f4e0492e34a25315b1500952a3f6e5d3818ffae785f7e364a702c5456. +The scanner had covered all 2,552,677 active-chain blocks and +2,773,068 transactions. It found zero native witness-v2 outputs, +native spends, wrapped spends, and malformed candidates, with 1,271 +verified checkpoints. Exactly 2,552,677 block records were stored; +the maximum serialized length was 1,998,152 bytes. The upper bound +of four times that size is 7,992,608 WU, below Core 4.8.0's 8-MWU +limit. This size bound supplements, but does not replace, consensus +replay. A deep verifychain level 4 over the last 2016 blocks +completed with no coin database inconsistencies in 2017 blocks. +The UTXO-set digest was +f751e2ca4150daeb14bb0ec964089dc939273ad1b23bcfc4856f8aa678cce281. +The attempted all-history verifychain level 2 was interrupted after +about 11 minutes to allow live synchronization. Its true RPC response +on daemon shutdown is not counted as a passing check. Private evidence +is in testnet-live-sync-a16-final.json, +testnet-history-scan-a16-live.json, testnet-block-record-size-a16-live.json, +and testnet-verifychain-a16.json. + +A later 2026-10-14 09:46:57 UTC snapshot, with the same clean a16a05bdb +binary still running, reached height 2,552,938 and tip +00000f98a1138d092840a074b6896b1e50fc3b492f43e9f3bcfc9235d2a45f7d. +The complete active-chain scan covered 2,552,939 blocks and 2,773,330 +transactions, again with zero native witness-v2 outputs or spends, +wrapped spends, and malformed candidates; it checked 1,272 checkpoints. +The corresponding 2,552,939 block records still had a maximum size of +1,998,152 bytes, yielding the same 7,992,608-WU old-rule upper bound. +A fresh verifychain level 4 over the last 2016 blocks returned true in +24.3 seconds while the node remained running. The private records are +testnet-history-scan-a16-later.json, +testnet-block-record-size-a16-later.json, and +testnet-verifychain-a16-later.json. This later scan strengthens the +active-chain evidence but does not demonstrate public PQ spending or +unknown side branches. + +At 2026-10-14 15:52 UTC the same unpruned clean-binary node reached +height 2,553,271, tip +00000b023f5da9e8593dbf7a3a3552e47e07cb415760364e12d551a1ae80802c. +The resumed scanner covered 2,553,272 active-chain blocks and +2,773,663 transactions with zero witness-v2 outputs or spends, +wrapped spends, and malformed candidates in its defined classes. +It verified 1,273 checkpoints. All 2,553,272 stored block records +remained below 2,000,000 serialized bytes; the maximum remained +1,998,152 bytes, yielding the 7,992,608-WU old-rule upper bound. +A fresh verifychain level 4 over 2016 blocks returned true in +23.9 seconds at the same tip. Private records are +testnet-history-scan-a16-current.json, +testnet-block-record-size-a16-current.json, and +testnet-verifychain-a16-current.json. A path comparison from +the clean binary source a16a05bdb to fecdcdaee found no changed +production source under src/; workflow, depends, documentation, +and test paths changed. The private comparison is +evidence/a16-to-fecdc-source-diff.json. + +At 2026-10-15 09:17 UTC, the same running clean a16a05bdb daemon was +at testnet height 2,554,210, tip +00000907a0a6ddbc93cff6b51cfe9805315fffc3ca4c36acf97e9a797877d9e8. +The incremental active-chain scanner covered 2,554,211 blocks and +2,774,605 transactions with zero witness-v2 outputs or spends, +wrapped spends, and malformed candidates in its defined classes. It +verified 1,274 checkpoints. All 2,554,211 stored block records remained +below 2,000,000 serialized bytes; the maximum was still 1,998,152 +bytes, giving the same 7,992,608-WU old-rule upper bound. A fresh +level-4 `verifychain` over the last 2016 blocks returned true in 24.8 +seconds at the same tip. The private records are +evidence/testnet-history-scan-a16-20261015.json, +evidence/testnet-block-record-size-a16-20261015.json, and +evidence/testnet-verifychain-a16-20261015.json. The live daemon's +executable digest matches the previously recorded clean a16a05bdb +binary, as recorded in +evidence/testnet-daemon-provenance-a16-20261015.json. The separate CLI +path used by the record-size scanner does not establish daemon +provenance. The scan still covers only the known +active chain and cannot test future bit-13 activation. + +The live RPC warning said unknown new rules activated, versionbit 11. +Bit 11 is the known transfer-overflow deployment, ACTIVE since height +2,493,792. None of the latest 2016 block headers signaled bits 11, +12, or 13. The warning-checking code exists unchanged in official +Core 4.8.0. This historical warning is recorded in +testnet-versionbit11-a16-final.json; it is not evidence that bit 12 +or bit 13 activated. Public testnet bit 13 remains DEFINED until its +2027 window. The scan covers the locally available active chain, +not unknown side branches or the future public activation. + +The live testnet `getblocktemplate` at height 2,552,686 advertised +16,000,000 for both size and weight, with bit 12 available for +signaling while the raw `pq_hybrid` BIP9 state was STARTED. This is the +intentional testnet `nPQHybridEnabled=true` override: native RIP-25 is +effective from height zero and its nominal-year phase 2 has already +elapsed. The assembler and validator use this contextual forced-active +limit. Current public testnet therefore cannot demonstrate the native +8-to-12-MWU BIP9 transition; the controlled regtest boundary vectors +provide that proof. The operator plan must identify this testnet/mainnet +difference. The private live chain, peer, and template record is +`evidence/testnet-live-template-a16.json`. +A fresh 2026-10-15 template for height 2,554,219 again advertised +16,000,000 for both limits and bit 12 as available for signaling. Its +private record is evidence/testnet-live-template-a16-20261015.json. + +The exact-candidate two-binary FINDING-160 tests also passed: both +Core 4.8.0 and a16a05bdb accept a 7,600,636-WU control block; +Core 4.8.0 rejects an 8,801,324-WU block and an 8,000,697-WU +old-accounting PQ witness block that the candidate accepts after +bit-12 activation. The approved incompatible-upgrade plan remains +necessary. + +FINDING-067 remains HIGH and OPEN. CRITICAL FINDING-104 through +FINDING-107 remain release-open despite the expanded mined and +side-branch boundary tests recorded below; the complete coverage +disposition and a green final-SHA CI pair are still due. The +active-chain testnet replay cannot close unknown side-branch gaps. +FINDING-112 remains OPEN for wallet test-gate reliability. The latest +paired CI attempt at fecdcdaee had a green Build Raven and a Final +Gate stopped before `make check` by the separate ccache race in +FINDING-174. The repaired workflow must pass on one final SHA. +Other unresolved inherited wallet and asset limitations retain their +individual statuses below. Remaining rollout gates are operator +readiness and published upgrade instructions for bit 12, manual GUI +exercise on release operating systems, final release notes and signed +checksums, artifact provenance on the actual release commit, and the +owner's separate approval before a tag or GitHub release. + +At the 2026-10-15 `0c33cc92c` checkpoint, Build Raven `38713686026` +passed its mandatory security job and all five release targets; Final Gate +`38713685824` passed all eight jobs on the same SHA. The H-2 through H+2 +matrices, connected alternate branches, restart and rollback tests, and +clean testnet replay complete the scoped proof for FINDING-104 through +FINDING-107, now marked FIXED below. Artifact downloads and provenance +checks for this SHA subsequently passed on all five packages, including +individual SHA-256 manifests and one common source archive. The private +record is `evidence/ci-0c33cc92c-verification.json`. A prior Build +Raven on `ee30148e7` failed only its Linux x86_64 artifact job on the +inherited xproto source +timeout, recorded as FINDING-175. The libXau dependency had the same +latent missing fallback, recorded as FINDING-176. Both have verified +local fail-closed fixes but require the final exact-SHA CI pair. + +On 2026-10-15 the owner accepted the separate FINDING-112 disposition: +its two historical wallet recovery test failures could not be +reconstructed from retained logs, five later full 511-case suites +passed without source changes, and final-SHA `make check` remains +mandatory. This does not waive a renewed failure. + +The same unpruned clean testnet node later reached height 2,554,504, +hash `00000c918af9255f60483c82131231f8c5c1ef54cb74e63647e595504eaa1924`. +The full active-chain scan covered 2,554,505 blocks and 2,774,899 +transactions with zero witness-v2 outputs or spends, zero wrapped +spends, and zero malformed candidates in the scanner's defined classes. +It verified 1,278 checkpoints. Every stored block record remained below +2,000,000 bytes, with maximum 1,998,152 and old-rule weight upper +bound 7,992,608 WU. `verifychain 4 2016` passed in 23.4 seconds on +the same tip. Private records are +`evidence/testnet-history-scan-fec5-20261015.json`, +`evidence/testnet-block-record-size-fec5-20261015.json`, and +`evidence/testnet-verifychain-fec5-20261015.json`. + +FINDING-067 remains HIGH and OPEN. Its exact remaining gates are: +a wholly green Build Raven and Final Gate pair on the final release +SHA after this register update, with mandatory `make check`, the +Linux wallet-disabled Qt startup and Options smokes, and five +newly checked artifact packages; +deployment preparation and published upgrade +instructions for the incompatible bit-12 block-weight change; manual Qt +exercise on Windows and macOS release systems; completion of release +notes, checksums, and signatures on the final commit; and the owner's +separate approval before creating a tag or GitHub release. The clean +testnet historical replay and scans are complete at their recorded +heights, while future public bit-12 and bit-13 activation cannot yet be +observed on the public chain. + +At the later `35fa04a40d4f61cae45d80f51920d36c639e838f` +checkpoint, Build Raven `38753089034` and Final Gate +`38753089260` passed every required job on that same SHA. +The five downloaded release packages passed their provenance and +individual SHA-256 checks, and shared source archive digest +`ff654a71e9e6d68d4480c9fe2e8f5bf00f9209468c32010ba87b79db3b6d4ec0`. +Private evidence is +`evidence/ci-35fa04a40/artifact-verification.json`. This closes +the xproto and libXau package-source defects FINDING-175 and +FINDING-176 on that tested SHA. The later Qt corrections and release +tests have not yet passed an exact-SHA CI pair, so FINDING-067 +stays OPEN. + +At the later `77d8f4c65791fb53351f1b318e7f593cfe7559b0` +checkpoint, Build Raven `38853895481` and Final Gate +`38853895080` completed successfully on the same source SHA. +Build Raven passed its required security job, including the +wallet rewrite recovery test and all 33 required functional tests. +Its Linux release job passed the dynamic-library preflight and +both wallet-disabled pinned-Qt startup and Options smokes without +the local diagnostic shim. All five release targets built and +uploaded packages. Final Gate passed its security job and all +seven build configurations. The five downloaded Build Raven +packages passed SHA-256 manifests, provenance, embedded source +commit verification, and a common source archive digest of +`4913098eba47fc787745bedd503e1bbb3a84fc062553957bb5082b2296bea7d8`. +Private records are +`evidence/ci-77d8f4c65/build-raven-metadata.json`, +`evidence/ci-77d8f4c65/final-gate-metadata.json`, +`evidence/ci-77d8f4c65/build-linux-job.log`, and +`evidence/ci-77d8f4c65/artifact-verification.json`. +This closes the tested-source gates for FINDING-177, FINDING-178, +FINDING-181, FINDING-182, and FINDING-183. The source SHA will +change when this register update is committed, so FINDING-067 +still requires a new exact-SHA CI pair and package inspection +along with the operator and release gates listed above. + ## Finding frozen during the second adversarial review The network-context remediation was reviewed independently after its technical @@ -4516,8 +5135,27 @@ rebuild logic was added. than zero, discard the wallet database, restore only the mnemonic, invoke the recovery mechanism, rescan, and prove the UTXO is found and spendable. Keep the pre-FINDING-057 random-key limitation explicitly documented. -- **Remediation commit:** PENDING -- **Final status:** OPEN +- **Remediation commit:** `b8dffce5a` adds `recoverpqkeys` with inclusive + index and block-height bounds, transactional per-key persistence, an + advanced allocation counter, and a rescan. A failed or interrupted rescan + leaves the keys and counter persisted so the same request can be retried. +- **Verification:** Before the fix, the mnemonic-only functional case failed + at the missing RPC. After the fix, `wallet_pq_recovery.py` restores an + index-two funded key from mnemonic words, finds and spends its UTXO, proves + repeat calls are idempotent, and preserves index-three allocation over a + restart. A focused encrypted-wallet unit case proves ciphertext-only + persistence and a locked-wallet rejection. `make check -j4` and the local + required-functional and structural gates pass. Both CI workflows now + require the functional case; exact-SHA CI remains due. +- **Operator procedure:** Restore the mnemonic into a fresh wallet, unlock it + if encrypted, then call `recoverpqkeys start_index end_index start_height + stop_height` in chunks of at most 100 indices and 100000 blocks. Include + the funding block and repeat the same range after an interruption. Inspect + `listunspent` before spending. Indices beyond `end_index` remain available + for new addresses. Keys generated randomly before deterministic PQ + derivation still require the original wallet-file backup. +- **Final status:** FIXED locally for deterministic key recovery; exact-SHA + CI qualification remains due. ## Berkeley DB retry remediation checkpoint @@ -4676,10 +5314,11 @@ rebuild logic was added. `bench/bench_raven` target, and ran all 66 benchmarks successfully. Its integrated optimized result was 20.429 microseconds for ML-DSA versus 40.272 microseconds for ECDSA. -- **Current status:** BENCHMARKED, consensus remediation OPEN. A protocol - owner must explicitly approve a deterministic cost and boundary tests - before changing the approved RIP-25 consensus value. Cross-platform - release measurements remain outstanding. +- **Current status:** The owner approved fixed cost two with boundary tests + on 2026-10-19. Commit `af5783d78` applies that cost after bit-12 + activation, and local red-green regressions pass as recorded in the + canonical FINDING-061 entry. Cross-platform exact-SHA release builds + and measurements remain outstanding under FINDING-067. ## Sanitizer finding frozen before remediation @@ -5220,8 +5859,11 @@ rebuild logic was added. `mac_alias`. Both pinned directories permit `biplist`, `mac_alias`, and `ds_store` imports; the new preflight confirms each resolved module path is inside the depends native prefix. `bash -n` and diff whitespace checks - pass. The exact-SHA GitHub macOS package build remains pending. -- **Final status:** FIXED locally; GitHub macOS artifact pending. + pass. Build Raven run `37928136449` completed the macOS package job and + uploaded artifact `raven-unsigned-osx-870a0bee36f4ec4b4c52f1c0e238df83834025e2`. + Paired Final Gate run `37928133299` passed at the same SHA. +- **Final status:** FIXED for the tested candidate SHA; final release-SHA + qualification remains pending. ## Findings frozen during the 2026-10-08 asset-wallet continuation @@ -5229,6 +5871,14 @@ The defects below were reproduced or established against the uncommitted asset-wallet continuation based on `1eb238129`, before their corrections were committed. They do not change the frozen status of older findings. +Cross-platform candidate verification: Build Raven run `37928136449` and +Final Gate run `37928133299` both succeeded at +`870a0bee36f4ec4b4c52f1c0e238df83834025e2`. Final Gate passed its +required `make check`, functional security suite, and seven build-matrix +jobs. Build Raven passed its required security job and produced all five +Linux, ARM, Windows, and macOS artifacts. The later PQ-only asset redesign +and final release SHA require a new qualification pair. + ### FINDING-143: Protected administration can select the wrong authority coin - **Severity:** MEDIUM. @@ -5256,7 +5906,9 @@ were committed. They do not change the frozen status of older findings. - **Regression:** `feature_pq_asset_authority_selection.py` creates two authority UTXOs, funds only one matching anchor, verifies selection, and verifies the missing-anchor rejection. Passed in the 32-case matrix. -- **Final status:** FIXED locally; cross-platform CI remains pending. +- **Final status:** FIXED. The PQ-only authority functional test passed in + both required security jobs at `9151946ad`; all five artifacts built on + the same SHA. ### FINDING-144: Refreshed PQ anchor appears as a received payment @@ -5284,7 +5936,9 @@ were committed. They do not change the frozen status of older findings. change classification. - **Regression:** `feature_pq_asset_anchor.py:250-268` failed before the fix, then passed before and after node restart. -- **Final status:** FIXED locally; cross-platform CI remains pending. +- **Final status:** FIXED. The PQ-only anchor accounting test passed in + both required security jobs at `9151946ad`; all five artifacts built on + the same SHA. ### FINDING-145: Key-only salvage loses protected asset destination pairing @@ -5312,8 +5966,10 @@ were committed. They do not change the frozen status of older findings. - **Regression:** `pq_wallet_tests/protected_asset_destination_pair_survives_key_only_recovery` failed before the filter change and passes after it. Mismatched, malformed, oversized, trailing, and unrelated rows are rejected. -- **Final status:** FIXED locally; seed-only association reconstruction - remains a separate wallet-recovery limitation. +- **Final status:** FIXED. The wallet recovery unit tests passed in both + required security jobs at `9151946ad`, and all five artifacts built on + the same SHA. Seed-only association reconstruction remains a separate + wallet-recovery limitation. ### FINDING-146: History can imply PQ protection for an old asset output @@ -5343,8 +5999,9 @@ were committed. They do not change the frozen status of older findings. covers historical, active, unconfirmed, disabled, and missing-tag cases; `feature_pq_asset_wallet_ownership.py` checks a real historical output. Both pass, and the Qt executable compiles and starts with `-version`. -- **Final status:** FIXED locally; GUI interaction testing in the release - matrix remains pending. +- **Final status:** FIXED. The origin-height history regression passed in + both required security jobs at `9151946ad`. The unmodified Linux Qt + artifact was also exercised under FINDING-154. ### FINDING-147: Asset history RPC drops the PQ destination component @@ -5371,7 +6028,9 @@ were committed. They do not change the frozen status of older findings. - **Regression:** `feature_pq_asset_anchor.py:255-258` was red before the fix and passes afterward; the historical vector in `feature_pq_asset_wallet_ownership.py:92-94` remains classical. -- **Final status:** FIXED locally; cross-platform CI remains pending. +- **Final status:** FIXED. The PQ-only anchor and wallet ownership + functional tests passed in both required security jobs at `9151946ad`; + all five artifacts built on the same SHA. ### FINDING-148: Some wallet spends consume an anchor without refreshing it @@ -5394,13 +6053,27 @@ were committed. They do not change the frozen status of older findings. returned protected program inside fee and coin selection, or explicitly reject a spend that would leave no usable anchor. The latter would also reject some valid multi-program and explicit-change workflows. -- **Proposed remediation:** Design and boundary-test per-program native - anchor reservation without altering consensus or silently breaking - explicit change. Document the current manual top-up procedure until then. +- **Release decision:** Do not add automatic refresh to 4.8.1. Reserving a + funded output for every returned PQ program changes fee accounting, coin + selection and explicit native-change behavior. The bounded release path + documents a matching RVN top-up for each consumed anchor and proves that + an unanchored spend fails safely. A later wallet design may add automatic + reservation without changing consensus. - **Regression required:** Exact-fee, dust, two-program, and explicit-change spends, followed by another protected spend without external funding. -- **Final status:** OPEN; non-release-blocking wallet availability limitation, - but a condition for a complete seamless asset wallet. +- **Verification:** `3424ddc5e` adds the two-program and explicit-change + cases. `d64ed5a81` adds an exact-fee raw spend with no native return + output. Each retains wallet asset custody, rejects the next unanchored + protected spend, then succeeds after a matching RVN top-up. The focused + `feature_pq_asset_anchor.py` passed locally with Python 3.10 after + `d64ed5a81`. `b8f88f126` adds a dust boundary: a 1,000-satoshi native + remainder becomes fee, no replacement anchor is made, the next spend is + rejected, and a matching RVN top-up restores it. The full focused test + passed locally on 2026-10-12. The manual recovery procedure is documented + by `193d26483`. Both required security jobs passed on `9151946ad`, + including the dust and recovery vectors. +- **Final status:** DOCUMENTED LIMITATION for 4.8.1, fail-safe on the + qualified candidate SHA; final release-SHA qualification remains due. ### FINDING-142: Pinned native ccache source is unavailable to exact-SHA CI @@ -5866,7 +6539,8 @@ were committed. They do not change the frozen status of older findings. - **Regression required:** Wallet and functional vectors for normal, owner, reissuable, unique, restricted, and qualifier assets, missing PQ key, missing anchor, change, migration, and encrypted recovery. -- **Current status:** MITIGATED, still OPEN for release. The height-aware +- **Status at the first remediation checkpoint:** MITIGATED, still OPEN for + release. The height-aware filters in commit `8fb90c0aa` now exclude a post-activation asset unless the wallet has its tagged PQ key. The two-node `feature_pq_asset_wallet_ownership.py` regression reproduces @@ -5886,6 +6560,18 @@ and missing-PQ-key regressions pass, alongside the 32-case required functional matrix and `make check`. The wallet-code defect is FIXED locally. Cross-platform CI and public deployment remain separate release gates. +**2026-10-17 final disposition:** FIXED for the wallet ownership defect. +The owner-approved PQ-only asset rule supersedes the initial hybrid wording: +a new protected asset is spendable only through its matching ML-DSA key and +funded witness-v2 anchor, without an asset ECDSA key. The missing-PQ-key and +pre-activation ownership regressions failed before the ownership correction +and passed afterward. The exact-SHA Build Raven run `38885751826` and Final +Gate run `38885752445` passed the mandatory `make check` and required +functional suite at `1e58379df7c3ede470f8562711070d2b8152d10a`. +Anchor funding, migration availability, and public release readiness are +tracked separately in FINDING-067, FINDING-148, and FINDING-155. This status +does not close those release conditions. + ### FINDING-137: Invalid locked asset outpoint is dereferenced before its bound check - **Severity:** MEDIUM. Authenticated local RPC input can cause an @@ -6236,7 +6922,30 @@ Cross-platform CI and public deployment remain separate release gates. - **Regression required:** Fail a reward for low funds, restart, fund the initiating wallet, and verify a single payout from that wallet. Verify old unbound records do not charge another wallet. -- **Final status:** OPEN. +- **Remediation commit:** `b3803bbc0` binds new reward requests to a unique + wallet key, genesis, snapshot block and snapshot contents. Startup reloads + those requests. Each payout batch persists its transaction ID before wallet + commit. A missing or unconfirmed transaction outside the mempool, duplicate + wallet identity, changed snapshot, or old unbound request cannot trigger an + automatic replacement payout. The changed snapshot receives status 10 for + operator reconciliation; an uncertain transaction receives status 6. +- **Verification:** Before the fix, the low-funds restart scenario failed + because funding after restart produced no payout. The dedicated + `feature_rewards_restart.py` now passes the single-payout restart and + snapshot replacement cases. The original `feature_rewards.py` and + `make check -j4` pass. The dedicated test is mandatory in both CI + workflows. The first local behavioral gate could not launch that new test + because its executable bit was missing; `405b4d55d` sets the bit. A + subsequent gate passed the new test, then stopped on an unrelated local + Python 3.12 `asyncore` import limitation. Exact-SHA CI qualification is + pending. +- **Residual limitation:** Requests created before the new binding format and + uncertain committed transactions require manual reconciliation. A retry + occurs when the next block is connected after startup, not immediately at + wallet load. Automatic reassignment of legacy requests is intentionally + disallowed because the original wallet identity was never persisted. +- **Final status:** MITIGATED locally; exact-SHA CI and operator recovery + instructions remain due. ### FINDING-101: Restricted and qualifier wallet state uses wallet zero @@ -6263,7 +6972,27 @@ Cross-platform CI and public deployment remain separate release gates. - **Regression required:** Two wallets in both load orders receive distinct tag/freeze events. Each RPC and GUI view shows only its own data before and after restart. -- **Final status:** OPEN. +- **Remediation commit:** `4f026d02b` records tag and freeze events in a + node-global index independent of wallet load order. The selected wallet + filters RPC and Qt rows by address ownership. Startup rebuilds historical + events before loading wallets; a missing pruned block marks the index + incomplete and the wallet RPC refuses to show a partial history. Per-block + undo restores rows on reorg; an index mismatch invalidates the index and + queues a Qt model reload. Reindex explicitly resets the index state. +- **Verification:** The new `wallet_restricted_multiwallet.py` failed on the + prior binary because wallet B inherited wallet A's tagged address. It + passes after the fix across both wallet load orders, restart, index deletion + and rebuild, reorg and reconsideration, and full reindex. Existing + `feature_restricted_assets.py` and `feature_restricted_verifydb.py` pass. + `make -j4` and mandatory `make check -j4` pass. The new regression is + registered in the test runner and both CI workflows. A separate review of + the fallback reorg path found and then cleared a stale Qt cache risk. +- **Residual limitation:** Qt's test entry point is disabled in this branch, + so the GUI display itself still needs manual qualification. Pruned nodes + without the required historical blocks cannot rebuild the wallet view; + they report incomplete history instead of presenting a partial result. +- **Final status:** FIXED locally; exact-SHA CI and manual GUI qualification + remain due under FINDING-067. ### FINDING-102: Message channel subscription uses wallet zero @@ -6288,7 +7017,34 @@ Cross-platform CI and public deployment remain separate release gates. the selected wallet for automatic subscription and display filtering. - **Regression required:** Two wallets with different channels in both load orders, including wallet addition after startup and restart. -- **Final status:** OPEN. +- **Remediation commit:** `212610f62` gives each wallet a file-bound random + scope, records scoped channel preferences, automatic subscription events, + seen addresses, messages, tombstones, and scan checkpoints, and uses the + selected wallet for messaging RPCs. Startup replays the required history + before certifying a wallet inbox. Asset reorgs durably invalidate all + persisted wallet scopes, including unloaded wallets. A full replay first + orphans saved messages, then restores active matches. Message rows and + channel rows are synced before a single synced checkpoint and completion + batch is published. Manual subscribe and unsubscribe events retain their + order and block anchors; an orphaned unsubscribe is reanchored with its + suppression state. Automatic events are seeded at their original heights + during incremental scans. Block-template probes cannot record automatic + channel events as confirmed blocks. +- **Verification:** The original multiwallet regression failed before wallet + scoping. Targeted red runs separately exposed an unsafe scan checkpoint, + an incomplete reorg replay, a missed automatic resubscription after manual + unsubscribe, and stale suppression after its later resubscription was + orphaned. The final `wallet_messaging_multiwallet.py` passes with two + wallets, reversed load order, late loading, copied wallet isolation, + manual and automatic subscriptions, reorg, unloaded-wallet recovery, and + a deterministic crash after coins flush but before message flush. + `make check -j4` and the structural and behavioral invariant gate passed; + the new regression is mandatory in both CI workflows. +- **Residual limitation:** A full historical scan requires unpruned block + data. A missing block leaves the wallet inbox explicitly incomplete. + Manual Windows and macOS GUI validation and exact-SHA CI remain release + gates under FINDING-067. +- **Final status:** FIXED locally; exact-SHA CI qualification pending. ### FINDING-103: Asset test fails to compile in no-wallet builds @@ -6371,7 +7127,35 @@ Cross-platform CI and public deployment remain separate release gates. also passes. The parser is still tip-contextual in candidate-block and disconnect paths, so this is not a complete consensus correction. - **Mitigation commit:** `cb86a3ca566bfc884313eb747cbae2cd6d224b65`. -- **Final status:** MITIGATED locally, OPEN for release qualification. +- **Additional boundary regression:** `edcd4ddbb` checks the identical + raw `OP_PUSHDATA1` transfer at LOCKED_IN tips 861 and 862, at the + first ACTIVE next-block state at tip 863, and after a real transfer is + mined at height 864. Both pre-ACTIVE tips reject the transfer payload; + the ACTIVE tips reach the expected missing-input result. Invalidation, + restart, reconsideration, and the real transfer block remain in the + same passing functional test. The private run log is + evidence/f104-expanded-boundary-final.log. This extends the previously + recorded vulnerable-binary red and corrected-binary green regression. It does + not yet supply a mined `OP_PUSHDATA1` accept/reject vector at every + requested height around the boundary. +- **Additional mined positive boundary regression:** `87965b3a4` extends + the same functional test with actual long-name transfer outputs mined + at the first three ACTIVE heights, 864 through 866, and checks those + outputs through rollback, restart, reconsideration, and `verifychain`. + Its direct Python 3.10 run passed; private log: + `evidence/f104-hminus2-hplus2-local.log`. The earlier pre-ACTIVE + candidate-block rejection and vulnerable-binary red test remain in + place. This does not claim a connected pre-ACTIVE transfer block. +- **Final coverage disposition:** The bit-8 regression now checks the same + long transfer at the two pre-ACTIVE tips, rejects pre-ACTIVE mined + side-branch candidates, and connects the first three ACTIVE transfer + heights. Rollback, restart, reconsideration, mempool evaluation, and + `verifychain` pass. The original vulnerable-binary red result and the + corrected-binary green result remain preserved. All 33 mandatory + functional tests, local `make -j4 check`, Build Raven `38713686026`, + and Final Gate `38713685824` passed on `0c33cc92c`. +- **Final status:** FIXED for the bit-8 contextual activation defect. + Final release-SHA qualification remains tracked by FINDING-067. ### FINDING-105: Asset deployment activation survives a reorg incorrectly @@ -6417,7 +7201,50 @@ Cross-platform CI and public deployment remain separate release gates. `make check` also passes. Candidate-block and disconnect paths still need explicit parent context, so this is not a complete consensus correction. - **Mitigation commit:** `cb86a3ca566bfc884313eb747cbae2cd6d224b65`. -- **Final status:** MITIGATED locally, OPEN for release qualification. +- **Additional mined boundary regression:** `241d1517f` mines a signed + issue in the first block after the tip reports ACTIVE and verifies the + next block. On rollback, a hand-built issue block at the pre-ACTIVE + boundary is rejected with `bad-txns-assets-not-active`. The same raw + issue is rejected in the mempool at two earlier tips, then checked + after restart and accepted again when the ACTIVE branch is + reconsidered. The direct Python 3.10 functional run passed. The + private run log is evidence/f105-expanded-boundary-final.log. The + original vulnerable-binary red and corrected-binary green reorg + comparison remains recorded above. A complete mined H-2 through H+2 + and alternate-side-branch matrix is still due. +- **Additional side-branch boundary regression:** `9ded737c7` mines + real issue outputs at the first three ACTIVE heights and submits the + same signed issue in pre-ACTIVE sibling candidates at heights H-2 and + H-1 while the active tip is past activation. Those short branches + remain `inconclusive` until eligible for connection; after rewinding + the main chain, they are marked invalid. A separate candidate built + on the current pre-ACTIVE parent rejects with + `bad-txns-assets-not-active`. Restart, reconsideration, and + `verifychain` pass. The direct Python 3.10 run passed; private log: + `evidence/f105-hminus2-hplus2-local.log`. The deferred short-branch + result is recorded explicitly rather than treated as immediate + block-connection proof. +- **Connected active competing branch:** `2fd13fc9e` builds a distinct + issue block at the first bit-6 active height on a side branch, extends + that branch beyond the original three-issue tip, and verifies its connected + asset state before and after restart. Invalidating the side branch + restores the original two later asset issues. Reconsidering both + branches after a rollback to LOCKED_IN again selects the longer branch, + then restores the original branch on invalidation. The direct Python + 3.10 test, all 33 mandatory functional tests, and local `make -j4 check` + passed. Private logs are + `evidence/required-functional-f107-sidebranches-local.log` and + `evidence/local-make-check-f107-sidebranches.log`. +- **Final coverage disposition:** The bit-6 test rejects the same signed + issue on pre-ACTIVE candidate parents, mines real issues at H through + H+2, connects a distinct longer ACTIVE side branch, and compares asset + state after rollback, restart, and reconsideration. The original + vulnerable-binary red and corrected-binary green results remain + preserved. All 33 mandatory functional tests, local `make -j4 check`, + Build Raven `38713686026`, and Final Gate `38713685824` passed on + `0c33cc92c`. +- **Final status:** FIXED for the bit-6 contextual activation defect. + Final release-SHA qualification remains tracked by FINDING-067. ### FINDING-106: Additional Core 4.8.0 deployment latches survive reorgs @@ -6477,7 +7304,56 @@ Cross-platform CI and public deployment remain separate release gates. - **Mitigation commits:** `1a7a2030a8f6fde4d3e04e6140df75463cd16b0e` (RIP5), `8a4cd56cac2af9d124033118c69406d29e692ec4` (boundary test), `b912239dfd494bcb594cb0190161140b45fcb15d` (value and coinbase). -- **Final status:** OPEN for release qualification. +- **Additional candidate-block boundary regression:** `209cd937a` extends + the synthetic VersionBits test to the last LOCKED_IN parent, first ACTIVE + parent, and two later ACTIVE parents for all three predicates. It also + isolates bit 7 from already-active bit 6 in the RIP5 functional test: + the same valid signed restricted issue is rejected by a mined candidate + block immediately before bit 7 activation, accepted and mined at its + first active height, then verified after rollback, restart, and + reconsideration. Nine VersionBits unit cases and the direct Python 3.10 + functional run passed. Private logs are + evidence/f106-107-versionbits-241d-final.log and + evidence/f106-rip5-boundary-241d-final.log. Enforced-value and + coinbase mined-block vectors are still due; this finding remains open. +- **Mined enforced-value and coinbase regression:** `fde4d21e8` adds + mandatory `feature_enforced_coinbase_activation.py`. A signed + nonzero-value reissue candidate is accepted at height 575 and + rejected at the first bit-9 LOCKED_IN height 576 with + `bad-txns-asset-reissued-amount-isn't-zero`. A coinbase asset + candidate is accepted at height 1499 and rejected at the first + bit-10 ACTIVE height 1500 with + `bad-txns-coinbase-contains-asset-txes`. The test includes sibling + branches, rewind, restart, reconsideration, and `verifychain`. + Its direct Python 3.10 run and all 33 required functional cases + passed locally, as did `make -j4 check`; private logs: + `evidence/f106-f107-enforced-coinbase-local.log`, + `evidence/required-functional-local-final-candidate.log`, and + `evidence/local-make-check-f173-test-candidate.log`. Build Raven + `38592937851` passed its security job, but the paired Final Gate + stopped before tests for the separate FINDING-174 ccache race. +- **Expanded mined boundary matrix:** `76983ad18` submits the same signed + nonzero reissue at bit-9 heights 574 and 575, where the old rule accepts + it, and at 576, 577, and 578, where the enforced-value rule rejects it. + It also submits an asset-bearing coinbase at bit-10 heights 1498 and + 1499, where the old rule accepts it, and at 1500, 1501, and 1502, where + the active rule rejects it. The test connects competing pre-activation + branches, rewinds, restarts, restores the active branch, and verifies + chain state. The direct Python 3.10 run passed; all 33 mandatory + functional tests and local `make -j4 check` passed afterward. Private + logs are `evidence/f107-bit9-bit10-matrix-local.log`, + `evidence/required-functional-f107-expanded-local.log`, and + `evidence/local-make-check-f107-expanded.log`. +- **Final coverage disposition:** The signed enforced-value reissue and + asset-bearing coinbase candidates now cover H-2, H-1, H, H+1, and H+2 + under their respective old and new rules. The bit-7 restricted-asset + test covers the same boundary, real ACTIVE issue outputs, competing + branches, rollback, restart, and reconsideration. The unit test + preserves the enforced-value LOCKED_IN rule. All 33 mandatory + functional tests, local `make -j4 check`, Build Raven `38713686026`, + and Final Gate `38713685824` passed on `0c33cc92c`. +- **Final status:** FIXED for the three inherited deployment latches. + Final release-SHA qualification remains tracked by FINDING-067. ### FINDING-107: Context-free block checks consume active-tip asset state @@ -6571,10 +7447,70 @@ Cross-platform CI and public deployment remain separate release gates. `ravend` selected explicitly. One initial direct invocation accidentally selected an older executable on `PATH` and returned `inconclusive`; that was a test-command error, not a result from the audited binary. -- **Final status:** MITIGATED, still OPEN for release qualification. Nested - candidate-parent propagation and side-branch connection after overtaking - are tested. Broader historical replay and second-audit qualification remain - due. +- **Additional side-branch boundary regression:** `3e2d65d89` submits the + same signed long-name transfer in candidate blocks at heights 862 and + 863 while the verifier's active tip is past activation; both candidates + are rejected with the old-parser reason. The competing branch mines + the transfer at height 864 and the verifier checks its asset name, + amount, message, expiry, and `OP_PUSHDATA1` encoding after reorganization. + The direct Python 3.10 functional run and nine VersionBits unit cases + passed; private log: evidence/f107-sidebranch-boundary-241d-final.log. + Side-branch block vectors around the other deployment + boundaries remain due; this finding remains open. +- **Additional cross-deployment side-branch evidence:** `9ded737c7` + covers bit-6 issue candidates on pre-ACTIVE siblings and confirms + they become invalid when that branch is connected. `fde4d21e8` + covers bit-9 enforced-value and bit-10 coinbase candidates on both + sides of their respective boundary, with rewind, restart, and + reconsideration. These complement the earlier bit-7 RIP5 and bit-8 + transfer candidates. Direct functional runs passed, and all 33 + required functional cases passed locally. The complete active-chain + testnet replay is separate evidence for historical compatibility; + it cannot establish unknown side-branch behavior. Private logs: + `evidence/f105-hminus2-hplus2-local.log`, + `evidence/f106-f107-enforced-coinbase-local.log`, and + `evidence/required-functional-local-final-candidate.log`. +- **Expanded bit-7 side-branch matrix:** `65461d0b5` rejects the same + signed restricted-issue transaction in candidate blocks at heights + 574 and 575 while the process has already observed bit 7 ACTIVE. + Distinct restricted issues then enter connected blocks at the first + three active heights 576, 577, and 578. A rewind across the boundary, + restart under the old rule, reconsideration, and `verifychain` preserve + all three issues and reject pre-activation admission. The direct Python + 3.10 run, all 33 mandatory functional tests, and local `make -j4 check` + passed. `76983ad18` separately extends the bit-9 and bit-10 competing + branch checks to H-2, H-1, H, H+1, and H+2. Private logs are + `evidence/f107-bit7-matrix-local.log`, + `evidence/required-functional-f107-expanded-local.log`, and + `evidence/local-make-check-f107-expanded.log`. +- **Connected bit-7 side branches:** `cb9bbbbe8` submits restricted-issue + candidates at H-2 and H-1 while an unrelated active tip is at H; + each candidate remains on a short side branch, then becomes invalid + when its competing main-chain child is removed. It also connects a + distinct valid issue branch at H, extends it beyond the original H+2 + tip, verifies its restricted asset state after restart, and restores + the original H+1 and H+2 issues after invalidation. Reconsideration + after a rollback to LOCKED_IN again selects the longer valid branch. + The direct Python 3.10 test, all 33 mandatory functional tests, and + local `make -j4 check` passed. Private logs are + `evidence/required-functional-f107-sidebranches-local.log` and + `evidence/local-make-check-f107-sidebranches.log`. +- **Final coverage disposition:** Bits 6, 7, 8, 9, and 10 now have + candidate-parent boundary and competing-branch regressions. The bit-6 + and bit-7 valid alternate branches actually overtake the selected + chain and survive restart; invalid pre-ACTIVE candidates are judged + when their own branches become eligible for connection. The bit-8 + long-transfer branch also connects and reorganizes. All 33 mandatory + functional tests and local `make -j4 check` passed. The clean a16a05bdb + binary replayed public testnet history from genesis with + `-assumevalid=0`, then passed a full active-chain scan and a deep + 2016-block check. No production source file outside `src/test/` + changed between that build and `0c33cc92c`, whose Build Raven + `38713686026` and Final Gate + `38713685824` both passed. The previously open scoped boundary and + side-branch proof obligations are met. +- **Final status:** FIXED for the candidate-parent consensus-context + defect. Final release-SHA qualification remains tracked by FINDING-067. ### FINDING-108: Synthetic VersionBits test reuses stale cache entries @@ -6747,8 +7683,8 @@ Cross-platform CI and public deployment remain separate release gates. complete `test_raven` run and third `make check` passed without a source change between the failures and successes. - **Affected file/function/lines:** - `src/wallet/test/pq_wallet_tests.cpp:1485-1503,1540-1551`, recovery tests; - `src/wallet/db.cpp:285-465`, `CDB::RecoverInternal`. The failing return + `src/wallet/test/pq_wallet_tests.cpp:1640-1666,1699-1709`, recovery tests; + `src/wallet/db.cpp:285-523`, `CDB::RecoverInternal`. The failing return branch has not yet been identified. - **Expected behavior:** Identical source and fixtures produce consistent pass/fail results, with enough failure detail to diagnose a recovery @@ -6759,7 +7695,21 @@ Cross-platform CI and public deployment remain separate release gates. regression. Do not skip or relax either test. - **Regression required:** Repeated full wallet `make check` runs and the affected recovery cases with the original assertions intact. -- **Final status:** OPEN pending root-cause investigation. +- **Additional evidence:** Five later full `make check` runs passed all 511 + unit cases without a source change. Private logs are + `evidence/f112-repeat-1.make.log` through + `evidence/f112-repeat-5.make.log`, with matching `.unit.log` files. + Neither historical failure yielded a retained stage-specific Berkeley + DB diagnostic, so the passing repeats do not establish a root cause. +- **Owner disposition:** On 2026-10-15 the owner accepted classifying the + historical cause as not reconstructible from the retained evidence, + documenting the residual test-gate risk, and requiring a green + mandatory `make check` on the final 4.8.1 SHA. This disposition is + separate from FINDING-166 and does not prove a wallet production + defect or waive either recovery assertion. +- **Final status:** DISPOSED, historical cause unknown. Any renewed + recovery failure must be diagnosed and tracked before release; + FINDING-067 retains the final-SHA `make check` gate. ### FINDING-113: Crash replay rolls UTXO state forward without asset state @@ -7644,6 +8594,1821 @@ Cross-platform CI and public deployment remain separate release gates. - **Verification:** Direct make evaluation proves an exported environment `PYTHONPATH` loses to the configured Makefile value, while a command-line make assignment wins. The macOS package script now uses that assignment - for both affected targets. `bash -n` and diff whitespace checks pass; - the exact-SHA macOS artifact rerun is pending. -- **Final status:** FIXED locally; GitHub macOS artifact pending. + for both affected targets. `bash -n` and diff whitespace checks pass. + Build Raven run `37852896085` passed its macOS build and packaging job + `113576631280` and uploaded artifact `11585626159`, named + `raven-unsigned-osx-72a68f0f898ec794f3bdc38d0621124e2f88b54c`. + The job log reports packaged source commit + `72a68f0f898ec794f3bdc38d0621124e2f88b54c` and artifact zip digest + `2704829474edb114b249899c945b6649aa35452233a385f3988e9295d3ea032b`. + This was the pull-request merge SHA. Build Raven run `37928136449` + subsequently succeeded on `870a0bee36f4ec4b4c52f1c0e238df83834025e2` + with all five release targets, including the macOS artifact named + `raven-unsigned-osx-870a0bee36f4ec4b4c52f1c0e238df83834025e2`. + Paired Final Gate run `37928133299` also succeeded at that exact SHA. + Build Raven `38296258242` subsequently built the macOS artifact + `11771367693` on the PQ-only candidate SHA `9151946ad`; paired Final + Gate `38296262444` succeeded at that exact SHA. The eventual 4.8.1 + release SHA still requires artifact qualification. +- **Final status:** FIXED on the PQ-only candidate SHA `9151946ad`; + final release-SHA artifact verification remains due. + +## Findings frozen after the merged asset-phase CI review + +### FINDING-149: Transfer crash-recovery test checks the tip before peer synchronization + +- **Severity:** MEDIUM. The mandatory Final Gate fails and blocks release + qualification. The available evidence does not show a consensus bypass. +- **Frozen initial status:** OPEN at merged commit + `7dc44845309ae36e33a55d3122e62603fcaed9dd`. +- **Affected RIP-25 invariant:** The release gate must verify that a node + recovers the asset transfer state after an interrupted coins flush. +- **Affected Core 4.8.0 fix:** The coins and asset database durability repair + addresses an inherited Core 4.8.0 defect. This test timing problem was + introduced by the later integration test in `ecd4ca077`. +- **Observed failure:** Final Gate runs `37852895999` at + `1e1c4533f0b2d70ed4a8f326cd30bbdaf6a5b498` and `37852951929` at + `7dc44845309ae36e33a55d3122e62603fcaed9dd` both fail at + `feature_asset_dbcrash_transfer.py:66`: the restarted source node reports a + different tip immediately after RPC startup. The preceding asset database + recovery log assertions pass. The other required functional tests pass. +- **Likely root cause:** The test restarts the source node and reads its tip + before it has synchronized the transfer block from its still-running peer. + Related crash-recovery tests use a bounded wait for the expected tip. + This hypothesis needs a green rerun; a timeout would instead expose a + remaining recovery defect. +- **Affected file/function/lines:** + `test/functional/feature_asset_dbcrash_transfer.py:59-75`, recovery check. +- **Expected behavior:** Wait a bounded time for the exact transfer block, + then retain the UTXO and asset balance comparisons. A missing block must + still fail the test and the gate. +- **Regression required:** Treat both failed Final Gate runs as the red + regression. Rerun the transfer recovery test after the bounded wait and + confirm the complete Final Gate passes at the same source SHA as the + cross-platform Build Raven artifacts. Keep `make check` mandatory. +- **Remediation commit:** `8d4e86d4b` adds a bounded wait for the exact tip. +- **Verification:** The focused crash-recovery test passed locally. Final + Gate run `37888456288` at + `b8bc089f3e3d2b395279e23ec5fdd8e1a9036633` passed its required + functional tests, mandatory `make check`, fuzz smoke, invariants and all + seven matrix builds. The paired Build Raven run had a separate macOS runner + failure recorded as FINDING-156. Final release-SHA rerun remains due. +- **Final status:** FIXED; final release-SHA qualification pending. + +### FINDING-150: Ordinary RVN coin selection can consume a protected asset anchor + +- **Severity:** MEDIUM operational availability. Consensus still rejects an + asset spend without a matching PQ input. +- **Frozen initial status:** OPEN during the merged asset-phase review at + `7dc44845309ae36e33a55d3122e62603fcaed9dd`. +- **Affected RIP-25 invariant:** The wallet should preserve or refresh the + native PQ anchor needed to spend a protected asset later. +- **Affected Core 4.8.0 fix:** None; native PQ anchors are part of this asset + phase. +- **Root cause:** Generic native RVN coin selection includes funded PQ anchor + outputs among available non-asset UTXOs, even when the transaction spends + no asset. Asset-specific refresh logic is not entered in that path. +- **Affected file/function/lines:** `src/wallet/wallet.cpp:2971-2975, + 3178-3186,4101-4104,4234`, native coin selection. +- **Concrete scenario:** An ordinary payment may consume the sole anchor + for a protected asset and return change to a different destination. + The asset and keys remain in the wallet, but a later protected spend needs + another RVN payment to the original PQ witness-v2 address. +- **Expected behavior:** Preserve a last required anchor during generic + spending or atomically recreate a funded anchor for the same program. + Avoid silently overriding a user-selected change destination. +- **Regression required:** Fund one protected asset and its sole anchor, + issue an ordinary RVN payment that selects the anchor, verify the later + asset spend fails for lack of a matching funded anchor, then top up that + PQ address and verify spending recovers. Confirm no consensus bypass. +- **Remediation commit:** None; automatic last-anchor reservation remains a + wallet design task. `193d26483` documents manual recovery. +- **Verification:** `3424ddc5e` forces ordinary RVN coin selection to + consume the sole matching anchor. The asset balance remains in the wallet, + a subsequent protected spend fails, and a matching PQ top-up restores it. + The focused functional test passed locally. +- **Final status:** DOCUMENTED LIMITATION for 4.8.1. The anchor-consumption + and matching top-up vector passed in both required security jobs at + `9151946ad`; final release-SHA qualification remains due. + +### FINDING-151: Raw transaction funding does not select asset PQ anchors + +- **Severity:** MEDIUM interoperability and availability. Consensus rejects + the unanchored protected asset transaction. +- **Frozen initial status:** OPEN during the merged asset-phase review at + `7dc44845309ae36e33a55d3122e62603fcaed9dd`. +- **Affected RIP-25 invariant:** Each protected asset input needs a funded + native PQ input for the same program. +- **Affected Core 4.8.0 fix:** None; Core 4.8.0 has no asset PQ anchor rule. +- **Root cause:** `fundrawtransaction` funds native RVN through generic coin + selection and does not derive required PQ programs from raw asset inputs. + Its native change also does not automatically refresh a supplied anchor. +- **Affected file/function/lines:** `src/wallet/wallet.cpp:3869-3918, + 3999-4016,4100-4104,4250-4309`, raw funding and coin selection. +- **Concrete scenario:** A raw protected asset transfer is funded and signed + without a matching anchor. Mempool and block checks reject it. The caller + can supply a funded matching anchor explicitly and return enough RVN to + that same PQ address for future spends. +- **Expected behavior:** Either derive and fund matching PQ anchors with + accurate fee accounting, or clearly document the manual raw transaction + construction sequence. Merely marking anchors as selected is insufficient: + the raw append loop omits selected inputs. +- **Regression required:** Demonstrate rejection of a funded raw protected + transfer lacking the matching anchor, then acceptance after adding the + anchor explicitly and preserving its PQ destination for change. +- **Remediation commit:** None; automatic raw anchor selection remains a + wallet design task. `193d26483` documents the explicit construction path. +- **Verification:** `3424ddc5e` proves that generic raw funding omits the + matching anchor and consensus rejects the signed transaction. Supplying + the anchor and an RVN return output to its PQ address succeeds, and a + later protected spend remains possible. The focused test passed locally. +- **Final status:** DOCUMENTED LIMITATION for 4.8.1. The raw-funding + rejection and explicit-anchor recovery vector passed in both required + security jobs at `9151946ad`; final release-SHA qualification remains due. + +## Findings frozen by the independent merged asset-phase review + +### FINDING-152: Default asset issuance RPCs choose an invalid destination after activation + +- **Severity:** MEDIUM operational availability. The transaction constructor + rejects the classical-only default; consensus is not bypassed. +- **Frozen initial status:** OPEN at merged commit + `7dc44845309ae36e33a55d3122e62603fcaed9dd`. +- **Affected RIP-25 invariant:** New asset and owner-token outputs after bit 13 + activation must carry a canonical PQ destination. +- **Affected Core 4.8.0 fix:** None; the classical defaults predate bit 13. +- **Root cause:** `issue`, `issueunique`, and `issuequalifierasset` fill an + omitted address with a fresh P2PKH address, while active issuance requires + a `classical|PQ` descriptor. +- **Affected file/function/lines:** `src/rpc/assets.cpp:510-526,675-691, + 2483-2499`; `src/assets/assets.cpp:4266-4275`. +- **Concrete scenario:** An ordinary RPC call without an explicit address + fails after bit 13 activation, including an owner-token issuance path. +- **Expected behavior:** Generate and persist a wallet-owned PQ asset + descriptor by default when the rule is active. Preserve classical + defaults before activation. +- **Regression required:** Exercise all three default-address RPCs before + and after activation. Confirm their new asset and owner-token outputs are + tagged, wallet-owned, and spendable with a matching funded anchor. +- **Remediation commit:** `7a2502499` generates and persists a wallet-owned + composite destination for active default issuance calls. +- **Verification:** The new functional assertions failed before the code + change for all three RPCs with the canonical-destination error. After the + change, `feature_pq_asset_wallet_ownership.py` passes locally and checks + preactivation classical behavior and postactivation owned PQ pairs. + Both required security jobs passed this functional test at `9151946ad`. +- **Final status:** FIXED on the qualified candidate SHA; final release-SHA + qualification remains due. + +### FINDING-153: Asset index RPC aggregates distinct PQ programs by classical address + +- **Severity:** MEDIUM interoperability and custody interpretation. No + consensus bypass or loss of asset keys is shown. +- **Frozen initial status:** OPEN at merged commit + `7dc44845309ae36e33a55d3122e62603fcaed9dd`. +- **Affected RIP-25 invariant:** The earlier dual-signature candidate + identified an active asset recipient by a classical address and PQ program + together. The current PQ-only candidate derives a distinct synthetic + identifier from each PQ program. +- **Affected Core 4.8.0 fix:** None; the index predates bit 13. +- **Root cause:** The earlier candidate let `listaddressesbyasset` aggregate + outputs under one classical-address index row even when their PQ programs + differed. That output format has been superseded for new protected assets. +- **Affected file/function/lines:** `src/rpc/assets.cpp:787-791,1138-1157`; + `src/coins.cpp:291-302`. +- **Concrete scenario:** In the earlier candidate, two protected descriptors + shared a classical key and the RPC reported one aggregate row. In the + current PQ-only format their program-derived identifiers are distinct. + Historical classical outputs retain their original index semantics. +- **Expected behavior:** Expose descriptor-aware outpoint information, or + explicitly label this legacy aggregate as non-destination data. +- **Regression required:** Retain the earlier aggregate vector as historical + evidence. Verify that current PQ-only outputs under different programs + have distinct identifiers and that a mismatched identifier/program pair + is rejected. +- **Remediation commit:** `193d26483` documented the earlier aggregate + semantics. `0b6d74bd8` updates the asset design note for the PQ-only + candidate. The format redesign is tracked by FINDING-158. +- **Verification:** `588ef7ec8` exercises the superseded dual-signature + format and its fail-safe recipient check. The current source RPC functional + vector asserts distinct synthetic identifiers for two PQ programs and + rejects an identifier/program mismatch. Both required security jobs + passed the current source RPC test at `9151946ad`. +- **Final status:** SCOPE SUPERSEDED for new PQ-only outputs. Historical + classical index rows remain descriptive data, not proof of PQ custody. + +### FINDING-154: Qt asset control displays and copies a classical-only address + +- **Severity:** MEDIUM usability and destination integrity. Sending to the + copied address fails for active protected assets. +- **Frozen initial status:** OPEN at merged commit + `7dc44845309ae36e33a55d3122e62603fcaed9dd`. +- **Affected RIP-25 invariant:** Wallet UI must distinguish protected + destinations with different PQ programs. +- **Affected Core 4.8.0 fix:** None; asset coin control predates bit 13. +- **Root cause:** `WalletModel::listAssets` and the coin-control table group + by the extracted classical destination; Copy address uses that same field. +- **Affected file/function/lines:** `src/qt/walletmodel.cpp:737-756`; + `src/qt/assetcontroldialog.cpp:282-288,649-705`. +- **Concrete scenario:** In the earlier dual-signature candidate, two tagged + outputs could share a classical key and Copy address yielded a + classical-only string. The PQ-only candidate instead uses distinct + program-derived identifiers; the list, tree and clipboard required + interactive qualification on an unmodified release artifact. +- **Expected behavior:** Show the full protected descriptor per active + output and copy a valid descriptor, without relabeling historical outputs. +- **Regression required:** GUI exercise with two distinct PQ programs and + synthetic identifiers, including list and tree labels and clipboard + content. Build the Qt executable against depends Qt 5.12.11. +- **Remediation commit:** `879a818c6` groups coin-control outputs by their + contextual descriptor and copies the complete protected destination. +- **Verification:** The existing contextual formatter unit test covers + historical and active output heights. At 870a0bee3, the Linux + x86_64-pc-linux-gnu depends prefix reports Qt 5.12.11 in + Qt5Core.pc; make -j4, make check, and raven-qt -version pass with + that prefix. The local GCC 13 build needed -Wno-error=array-bounds + while compiling the pinned libxkbcommon dependency; the Qt recipe + and version were not changed. A later local Xvfb session with that + pinned Qt version showed two PQ-only asset outpoints under distinct + descriptors in list and tree views; Copy address returned each full + `classical|PQ` descriptor. Screenshots and copied strings are retained + with the private audit evidence. The local GCC 13 executable required + a process-local workaround for a Qt 5.12.11 `readlink` fortify crash; + the workaround is not part of the repository or release artifact. + Build Raven `38296258242` produced Linux artifact `11770728793` at + `9151946ad`, alongside a green Final Gate `38296262444`. The archive's + published SHA-256 file and full-SHA provenance verified locally. Its + unmodified `raven-qt` opened a copied regtest wallet under Xvfb without + the workaround. A private fixture issued two units of `PQGUIQA915` to + one PQ-only descriptor, then transferred one unit to a second descriptor. + Asset Control list mode displayed two separate rows; tree mode displayed + two separate groups. Copy address on both rows and on the second tree + group returned the complete corresponding descriptor byte-for-byte. + Screenshots, the fixture, and exact clipboard comparisons are retained + outside the repository with the private audit evidence. +- **Final status:** FIXED on the qualified PQ-only candidate SHA; + final release-SHA artifact verification remains due. + +### FINDING-155: Partial migration of a legacy asset UTXO needs explicit PQ change + +- **Severity:** MEDIUM migration availability. The wallet fails safely; + whole-UTXO migration remains possible. +- **Frozen initial status:** OPEN at merged commit + `7dc44845309ae36e33a55d3122e62603fcaed9dd`. +- **Affected RIP-25 invariant:** New asset change after activation must be + protected even when its input was a historical classical asset UTXO. +- **Affected Core 4.8.0 fix:** None; legacy asset outputs predate bit 13. +- **Root cause:** The wallet can infer protected change from an existing + tagged source, but a historical input has no tag. The transfer RPC accepts + an explicit asset change descriptor; the GUI does not expose that field. +- **Affected file/function/lines:** `src/wallet/wallet.cpp:4334-4387`; + `src/rpc/assets.cpp:1252-1285`; `src/qt/assetsdialog.cpp:897-955`. +- **Concrete scenario:** After activation, sending part of an old asset + holding in the GUI fails when it would create unprotected asset change. +- **Expected behavior:** Migrate an entire legacy UTXO to a protected + descriptor, or supply an explicit protected change descriptor through RPC. + Neither path may silently create classical-only asset change. +- **Regression required:** Show the partial GUI-equivalent path fails + without change, then show whole-UTXO migration and RPC partial migration + succeed with canonical tags. Confirm later spends require a funded anchor. +- **Remediation commits:** `193d26483` documents whole-UTXO migration and the + explicit asset-change RPC path; `12200477b` adds a protected change field + and wallet-owned PQ-only descriptor chooser to the transfer dialog. +- **Verification:** `588ef7ec8` proves that a partial legacy spend without + protected change fails, while whole-UTXO asset and owner-token migration + and explicit PQ change succeed. A later protected spend needs and accepts + a funded matching anchor. The focused source RPC test passed locally and + in both required security jobs at `9151946ad`. +- **Further verification:** The focused pinned Qt 5.12.11 regression failed + before the GUI field existed and passed after it was added. It checks that + empty or invalid entries leave no classical asset-change destination in + coin control. The dialog accepts a canonical descriptor only if it belongs + to the selected wallet. The pinned Qt GUI binary builds and `make check -j4` + passes. The upstream Qt test runner still exits before its test cases; + the focused test used a temporary local runner and host Qt compatibility + shim. A full active-bit-13 GUI transfer and native Windows/macOS manual GUI + checks remain release qualification work under FINDING-067. +- **Final status:** FIXED locally for the GUI input path; final exact-SHA CI + and manual GUI qualification remain due under FINDING-067. + +### FINDING-156: macOS release runner disconnects during the artifact build + +- **Severity:** MEDIUM release availability. The failed job produced no + macOS artifact at the tested SHA; no code or consensus defect is established. +- **Frozen initial status:** OPEN after Build Raven run `37888464222` at + `b8bc089f3e3d2b395279e23ec5fdd8e1a9036633`. +- **Affected RIP-25 invariant:** All release artifacts must be produced and + checked from the same SHA as a green Final Gate. +- **Affected Core 4.8.0 fix:** None known; the cause of runner loss is not yet + classified as product code or CI infrastructure. +- **Observed failure:** Security tests and the Linux, aarch64, arm32v7 and + Windows artifact jobs passed. The macOS job `113688195436` built pinned + dependencies and configured successfully, then its hosted runner lost + communication with GitHub during `make -j2`. GitHub left that step without + a conclusion, skipped packaging and emitted no macOS artifact. Its available + log ends before the build output, so it does not identify a compiler error. +- **Affected file/function/lines:** `.github/workflows/build-raven.yml:216-240`, + dependency and product build steps; no defective source line is established. +- **Expected behavior:** The macOS job must complete its pinned Qt build, + package an artifact and verify the embedded source SHA. A runner loss must + remain a failed qualification, not be treated as a code pass. +- **Regression required:** Rerun the failed macOS job at the exact same SHA. + If runner loss recurs, inspect resource use and reduce parallel build load + or otherwise correct the CI environment, then rerun. Final qualification + must repeat at the eventual release SHA. +- **Remediation commit:** None. The failure was a hosted-runner disconnect, + with no demonstrated product-code defect. +- **Verification:** Build Raven run `37888464222`, attempt 2, succeeded on + the unchanged SHA `b8bc089f3e3d2b395279e23ec5fdd8e1a9036633`. + Its macOS job completed and uploaded the artifact named + `raven-unsigned-osx-b8bc089f3e3d2b395279e23ec5fdd8e1a9036633`. + The other four release targets and required security job also succeeded. + Paired Final Gate run `37888456288` succeeded at the same SHA. A newer + exact-SHA pair, Build Raven `37928136449` and Final Gate `37928133299`, + also succeeded at `870a0bee36f4ec4b4c52f1c0e238df83834025e2`, with + five artifacts. Build Raven `38296258242` also produced all five on + `9151946ad` with paired green Final Gate `38296262444`. These are + candidate builds, not the final release SHA. +- **Final status:** RESOLVED as a transient CI runner loss at the tested + SHAs; final release-SHA artifact qualification remains pending. + +### FINDING-157: Historical asset spends remain classical after bit 13 + +- **Severity:** HIGH for the newly requested legacy-signature sunset. +- **Frozen initial status:** OPEN on 2026-10-09 at e0893a6b6, before any + sunset design or consensus change. +- **Affected RIP-25 invariant:** After a separately defined sunset point, + no historical asset or owner-token UTXO may be spendable with a + classical signature alone. +- **Affected Core 4.8.0 fix:** None. Core 4.8.0 had no PQ asset rule. +- **Root cause:** The bit-13 input rule requires a matching native PQ + anchor only for asset coins created at or after the effective bit-13 + activation height. Historical asset scripts commit to a classical + P2PKH key but contain no PQ program. The current migration path spends + them classically to create a tagged output. +- **Affected file/function/lines:** src/consensus/tx_verify.cpp:626-676, + especially the origin-height gate at 661-667; + src/assets/assets.cpp:3632-3707, canonical program extraction; + src/wallet/wallet.cpp:2998-3007,4257-4267, historical-input selection. +- **Concrete scenario:** A historical asset or ASSET! token remains + spendable by its classical key after bit 13 activates. A later blanket + ban would make every unmigrated holding and its owner authority + unspendable unless a separate authenticated recovery rule exists. + Adding an arbitrary PQ anchor at spend time would not prove ownership + because an attacker could supply an anchor under its own PQ key. +- **Expected behavior:** Choose and specify a sunset deployment and an + explicit fate for unmigrated asset and owner-token UTXOs. If recovery + remains possible after sunset, its PQ authorization must be bound to + the historical owner before the classical-only path closes. +- **Regression required:** Prove ordinary and owner-token migration + before sunset; reject a classical-only historical spend after sunset + in mempool and blocks; cover the activation boundary, reorgs, restart, + assumevalid, and any authorized recovery path. Wallet and GUI balances + must not imply that frozen UTXOs are spendable. +- **Remediation commit:** None. The owner superseded the sunset request; + no sunset consensus change is planned for 4.8.1. +- **Verification:** The existing unit vector at + src/test/assets/asset_tx_tests.cpp:839-872 proves the current + origin-height exemption. The functional migration vector at + test/functional/feature_pq_asset_source_rpc.py:71-93 proves that + asset and owner-token migration currently depend on a classical + historical spend. No sunset regression exists yet. +- **Final status:** SCOPE SUPERSEDED on 2026-10-09. The owner withdrew the + requested sunset and chose voluntary migration, as for native RVN. + Historical classical spending remains a documented property, not a fix. + +### FINDING-158: New protected asset outputs still require a classical signature + +- **Severity:** HIGH release requirement mismatch. The existing dual-key + rule still requires ML-DSA, but it does not provide PQ-only ownership. +- **Frozen initial status:** OPEN on 2026-10-09 at 73dfc7f8a, before any + redesign of asset scripts, consensus, wallet, RPCs, or Qt. +- **Affected RIP-25 invariant:** After bit 13, new asset and owner-token + outputs must be spendable with ML-DSA alone. Historical outputs retain + their classical spending rule until their owners migrate them. +- **Owner-approved authorization model:** A matching native witness-v2 RVN + input may carry the ML-DSA signature for the asset spend. The asset input + itself must require no ECDSA signature. +- **Affected Core 4.8.0 fix:** None. Its asset ownership format was P2PKH. +- **Root cause:** The candidate asset envelope retains a P2PKH ownership + prefix and appends a 32-byte PQ program. Its input rule additionally + requires a matching native witness-v2 anchor, but the asset script itself + still executes ECDSA. The wallet similarly requires both keys. +- **Affected file/function/lines:** src/script/script.cpp:245-281, + src/script/standard.cpp:75-83, src/script/sign.cpp:90-123, + src/assets/assets.cpp:3632-3707, src/consensus/tx_verify.cpp:626-676, + src/wallet/wallet.cpp:1941-1951,2998-3007. +- **Concrete scenario:** A holder of a new tagged asset or ASSET! output + cannot spend it with the ML-DSA key alone. Loss of its classical key + prevents spending even when the PQ key is available. This contradicts + the confirmed 4.8.1 ownership rule; it is not evidence of an ML-DSA + bypass in the present dual-key rule. +- **Expected behavior:** Specify and implement a consensus-safe PQ-only + asset output and spend format for all asset classes and owner authority. + Preserve the authenticated classical spend of historical outputs for + voluntary migration, without a sunset. Prove old-node validity and + activation behavior before selecting a deployment path. +- **Regression required:** Before the fix, demonstrate that a new asset + and owner-token output cannot be spent with ML-DSA alone. After the fix, + prove ML-DSA-only spending and rejection of missing or mismatched PQ + signatures in mempool and blocks. Cover historical migration, partial + change, issuance, reissuance, restricted and qualifier administration, + raw RPCs, wallet backup and recovery, Qt, reorgs, and exact-SHA CI. +- **Remediation commits:** `198b93029` introduced the parser and script + format; `802bf2433` added the PQ-only consensus and signing rules; + `5f34f8cbf` added wallet custody, recovery and contextual ownership; + `b7153328d` switched asset RPC and Qt construction to PQ-only outputs. +- **Verification:** The previous dual-signature consensus check and asset + signing path establish the pre-fix behavior. The + `pq_only_asset_prefix_is_legacy_valid_and_program_bound_test` was red + before `198b93029` because the parser rejected the proposed prefix. It + now passes with an empty scriptSig and a program-bound identifier. + `pq_asset_creation_height_and_anchor_rule_test` covers new-output and + anchored-input rejection. The asset anchor, source RPC, wallet ownership + and authority functional tests pass at `6ed46c91c`; local `make check` + passes. Both required security jobs passed the PQ-only unit and functional + vectors at `9151946ad`. In a two-binary regtest, Core 4.8.0 + `b60f50e04` synchronized the same 932-block chain as the candidate + artifact `9151946ad` while bit 13 was active on the candidate. Blocks + 864 through 932 contained 66 PQ-only asset outputs and 35 later spends. + The old node accepted both the example output in block 864 and its + scriptSig-empty anchored spend in block 865; both nodes reached the + same tip `3ec9956751d3d8e43fb3995bb96df5929a678155f03c0124d216141d8c65cebe`. + The ordinary-sized example spend block weighed 5,602 WU on both nodes. + This establishes old-node acceptance of the asset format, not old-node + enforcement of ML-DSA. Public activation remains blocked by the + incompatible native block rules in FINDING-160 and final release-SHA + qualification. +- **Final status:** FIXED for the PQ-only implementation on the qualified + candidate SHA; public activation and release remain blocked by + FINDING-160 and the remaining deployment qualification. + +### FINDING-159: Proposed PQ-only asset input permits scriptSig malleability + +- **Severity:** MEDIUM transaction integrity and relay consistency. +- **Frozen initial status:** OPEN on 2026-10-09 at 17053c0bc, before an + input scriptSig rule was added to the PQ-only consensus candidate. +- **Affected RIP-25 invariant:** A PQ-only asset spend has one canonical + empty asset scriptSig; its ML-DSA signature resides in a matching native + witness-v2 input. +- **Affected Core 4.8.0 fix:** None. This is a risk introduced by the new + PQ-only prefix under consideration for 4.8.1. +- **Root cause:** The proposed prefix leaves one true stack item when the + asset scriptSig is empty. Consensus block script flags do not include + CLEANSTACK, so a pushed extra true value could leave a nonempty stack + and still pass script evaluation. The input rule currently checks the + matching PQ anchor but not that the asset scriptSig is empty. +- **Affected file/function/lines:** src/validation.cpp:2550-2583, + `GetBlockScriptFlags`; src/script/script.cpp:245-281, asset marker; + src/consensus/tx_verify.cpp:626-676, `CheckTxPQAssets`. +- **Concrete scenario:** A third party can add a push of true to a proposed + PQ-only asset input scriptSig, changing its txid without changing its + authenticated asset movement or the anchor signature. Standard mempool + policy may reject it, but a block rule must reject it independently. +- **Expected behavior:** At active bit 13, every PQ-only asset input must + have an empty scriptSig in mempool and blocks. Historical classical asset + inputs retain their previous scriptSig rules. +- **Regression required:** Show an otherwise valid anchored PQ-only spend + with a nonempty asset scriptSig is accepted before the new check, then + rejected with a specific reason afterward. Empty scriptSig with the + matching anchor and historical classical inputs remain valid. +- **Remediation commit:** `802bf2433` rejects nonempty scriptSig for a + protected asset input after bit 13 while retaining historical input rules. +- **Verification:** The unit vector at + src/test/assets/asset_tx_tests.cpp:995-1003 failed when the new check was + temporarily removed: the malleable spend was accepted and had no reject + reason. Restoring the check made the same vector pass. The full local + `make check` also passed at `6ed46c91c`. Both required security jobs + passed at `9151946ad`. +- **Final status:** FIXED on the qualified candidate SHA; final release-SHA + qualification remains due. + +### FINDING-160: Native RIP-25 block limits can split Core 4.8.0 nodes + +- **Severity:** HIGH consensus compatibility and release blocker. +- **Frozen initial status:** OPEN on 2026-10-09 at `2e07c1a14`, before a + compatibility remedy or a revised deployment plan. +- **Affected RIP-25 invariant:** A deployment described as a soft fork must + not make a block valid under the new rules when Core 4.8.0 rejects it. +- **Affected Core 4.8.0 fix:** None. Core 4.8.0 enforces the RIP-2 8 MWU + block limit; the candidate changes that limit after bit 12. +- **Root cause:** The candidate allows 12 MWU immediately after native + bit-12 activation and 16 MWU after one nominal year. It also applies a + contextual PQ witness discount. Core 4.8.0 retains the 8 MWU rule and + its original weight accounting. The original RIP-25 proposal explicitly + calls for 12 MWU at PQ activation and 16 MWU one year later, but also + describes the deployment as a soft fork. The block expansion and that + compatibility description cannot both hold against Core 4.8.0. +- **Affected file/function/lines:** src/consensus/consensus.h:22-34, + limits; src/validation.cpp:2448-2474, contextual limits; + src/validation.cpp:4494-4507, block checks; doc/RIP-0025-PQ-Signatures.md:19,492, + soft-fork claim. Core 4.8.0 baseline is + `b60f50e04f1fba425b28804e61be2694faaf3469`. +- **Concrete scenario:** After bit 12, an upgraded miner may produce a + block whose old-accounted weight exceeds 8 MWU but whose candidate + accounting is within the new 12 MWU limit. An upgraded node can accept + it while a Core 4.8.0 node rejects it. +- **Owner decision:** Preserve the original 12 MWU limit at bit-12 + activation and 16 MWU one year later. This requires an explicit + incompatible-rule deployment plan and release documentation before + public activation; miner signaling alone does not upgrade old nodes. + On 2026-10-13 the owner approved the documented plan: distribute 4.8.1 + before STARTED, identify Core 4.8.0 incompatibility, require operators + to upgrade before ACTIVE, apply 12 MWU and PQ discount at ACTIVE and + 16 MWU after one nominal year, and retain testnet-history and 8 MWU + boundary proofs as pre-activation gates. This is not approval to tag or + publish the release. +- **Expected behavior:** Prove old-node acceptance of every post-activation + block allowed by the candidate, or explicitly choose and approve a + deployment that accounts for the incompatible block rules. Correct the + protocol and release documentation before public activation. +- **Regression required:** Run old Core 4.8.0 and candidate nodes against + blocks on both sides of the 8 MWU boundary, including PQ witness data + whose discounted and old-accounted weights differ. Verify block and + chain outcomes, not only isolated weight functions. +- **Remediation commits:** `73cbc93fd` documents the public upgrade plan; + `3c3bf8385` removes residual claims that BIP9 signaling alone makes + the combined deployment compatible with Core 4.8.0; `948b1ea20` + records the chosen mainnet window in protocol and operator release + notes. No consensus limit was changed by these documentation commits. +- **Verification:** Static comparison of the baseline 8 MWU constant and + candidate contextual 12/16 MWU checks establishes the incompatible + acceptance ranges. In a local two-binary regtest at height 540 after + candidate bit-12 activation, both Core 4.8.0 `b60f50e04` and the + candidate `90a16eff8` accepted a 7,600,636-WU block. Core 4.8.0 + rejected the next 8,801,320-WU block with `bad-blk-length`, while the + candidate accepted it and advanced one block. The two transactions + in the larger block were each below 8 MWU. The baseline was built + from unchanged source with a compiler-only GCC 13 header workaround. + A second two-binary test spent a valid ML-DSA witness-v2 output in a + block weighing 8,000,697 WU by the Core 4.8.0 calculation and about + 7,998,831 WU with the candidate's 1,866-WU PQ witness discount. Core + 4.8.0 rejected it with `bad-blk-weight`; the candidate accepted it and + advanced one block. This proves the discount is independently + incompatible at the 8 MWU boundary, even without using the 12 MWU + phase-1 allowance. On 2026-10-13 the two tests were repeated with the + versioned 4.8.1 binary from `55be9a0f2`, SHA-256 + `19de029e001adddfa4eb6143cc18c012a6e88d13d5b5dbf234a33be184edd378`. + Both binaries accepted the 7,600,636-WU control block. Core 4.8.0 + rejected the 8,801,320-WU block with `bad-blk-length` and the + 8,000,697-WU old-accounted PQ witness block with `bad-blk-weight`; + 4.8.1 accepted both. The private logs are + `evidence/f160-v481-55be-plain.log` and + `evidence/f160-v481-55be-pq-witness.log`. These are candidate checks; + the final release SHA must repeat them after the remaining evidence + updates. +- **Final status:** ACCEPTED as an incompatible consensus upgrade with an + owner-approved plan. The 4.8.1 release and public bit-12 activation + remain gated by FINDING-067: testnet history, final-SHA qualification, + release instructions and checksums, and operator readiness. The + 2027-07-01 to 2029-01-01 mainnet bit-12 timing was implemented in + `2df4d2d25` after a red-then-green BIP9 parameter regression. + +### FINDING-161: Pre-activation PQ-only lookalike output has no PQ custody + +- **Severity:** HIGH asset custody ambiguity. +- **Frozen initial status:** OPEN on 2026-10-09 at `2e07c1a14`, before any + creation rule, wallet classification, or origin-bound regression. +- **Affected RIP-25 invariant:** An output described as PQ-only must have + enforceable ML-DSA authorization when spent. The historical exception + must not silently label an anyone-can-spend lookalike as PQ protected. +- **Affected Core 4.8.0 fix:** None. The lookalike prefix is introduced by + the 4.8.1 candidate and remains valid to old script evaluation. +- **Root cause:** Before bit 13, `CheckTxPQAssets` does not apply the new + anchor rule. After activation, the origin-height exception still exempts + an output created before activation. The proposed script prefix is + satisfiable with an empty scriptSig under historical script rules. +- **Affected file/function/lines:** src/consensus/tx_verify.cpp:626-676, + activation and origin checks; src/script/script.cpp:245-281, legacy asset + parser; src/script/script.h:579-586, asset-tail evaluation; wallet asset + ownership and address generation paths. +- **Concrete scenario:** A pre-activation transaction creates the new + prefix and asset payload. The output can later be spent without an + ML-DSA anchor because its origin predates bit 13. A wallet that labels + or credits it as a protected PQ-only holding would misstate custody. +- **Expected behavior:** Define and enforce an activation-safe creation + and classification policy. Historical lookalikes must not appear as + PQ-protected wallet balance. Preserve valid historical spends without + trapping funds or silently treating them as authenticated PQ spends. +- **Regression required:** Create a lookalike before activation, spend it + before and after activation without an anchor, and prove the exact + consensus outcome and wallet classification. Cover a post-activation + creation and anchored spend as a separate case. +- **Remediation commits:** `802bf2433` adds a consensus origin-height + vector; `5f34f8cbf` refuses generic PQ-only wallet ownership without + confirmed activation context; `ea104132e` adds premature-output relay + policy and reorg cleanup. +- **Verification:** The functional regression failed before `ea104132e`: + a PQ-only output transaction remained in the mempool after a rollback + to the pre-activation tip. It passes after the policy change. The test + also directly mines a lookalike in the last pre-activation block, + confirms it is absent from PQ wallet balance, spends it without an + anchor after activation, and proves that its new post-activation output + cannot be spent again without an anchor. Unit vectors cover the + pre-activation spend rule. Local `make check` passes. Historical + directly mined lookalikes remain valid and unprotected by design; + operators must not present them as PQ custody. Both required security + jobs passed at `9151946ad`. +- **Final status:** FIXED on the qualified candidate SHA, with the + historical-block limitation documented. + +### FINDING-162: PQ-only owner and message assets miss wallet subscriptions + +- **Severity:** MEDIUM wallet messaging regression. +- **Frozen initial status:** OPEN on 2026-10-10 at `99101c0f7`, before a + contextual wallet ownership fix in the message subscription path. +- **Affected RIP-25 invariant:** Owner tokens and message channel assets + received after bit 13 must retain the existing wallet subscription + behavior when the PQ key controls them. +- **Affected Core 4.8.0 fix:** None. The regression follows the new + context-dependent PQ-only ownership rule. +- **Root cause:** `WalletOwnsAssetForMessageSubscription` calls the generic + `CWallet::IsMine(output)` method. The generic method deliberately returns + `ISMINE_NO` for PQ-only asset scripts because it has no origin height. + The helper returns before using the block height and PQ key it already + has available. +- **Affected file/function/lines:** src/coins.cpp:20-39, + `WalletOwnsAssetForMessageSubscription`; src/coins.cpp:309-333, transfer + and new-asset subscriptions; src/script/ismine.cpp:159-203, generic + ownership classification. +- **Concrete scenario:** A wallet receives a post-activation owner token + or message channel at its PQ-only destination. The asset is spendable + under the contextual wallet rule, but the message subscription helper + reports that it is not owned and skips channel enrollment. +- **Expected behavior:** Evaluate wallet ownership using the output's + confirmed block context. Do not enroll pre-activation PQ-only lookalikes + as PQ-protected subscriptions. +- **Regression required:** With messaging enabled, confirm post-activation + PQ-only owner-token and message-channel outputs and verify the expected + subscription. Confirm a pre-activation PQ-only lookalike is excluded. +- **Remediation commit:** `5f34f8cbf` uses confirmed output context in + the message subscription path. +- **Verification:** The owner-token subscription assertion in + `feature_pq_asset_wallet_ownership.py` failed before the contextual + lookup and passed afterward. The full functional test and local + `make check` pass at `6ed46c91c`. Both required security jobs passed + the wallet ownership functional test at `9151946ad`. +- **Final status:** FIXED on the qualified candidate SHA; final release-SHA + qualification remains due. + +### FINDING-163: Synthetic PQ asset identifier leaks as a native RVN address + +- **Severity:** HIGH potential loss of native RVN funds. +- **Frozen initial status:** OPEN on 2026-10-10 at `767e1b2b8`, before + address-book filtering or storage changes. +- **Affected RIP-25 invariant:** The 160-bit asset identifier derived from + a PQ program must never be presented as a wallet-owned RVN receive + address. No ECDSA key exists for its P2PKH interpretation. +- **Affected Core 4.8.0 fix:** None. The synthetic identifier is new to the + PQ-only 4.8.1 candidate. +- **Root cause:** New PQ asset destination creation adds the synthetic + `CKeyID(Hash160(program))` to the wallet address book with purpose + `pqasset`. `getaddressesbyaccount` enumerates every address-book entry + with the matching account name without checking purpose or spendability. + Qt hides the purpose, but the RPC exposes the identifier as an ordinary + Ravencoin address. +- **Affected file/function/lines:** src/wallet/rpcwallet.cpp:280-290, + PQ asset destination creation; src/wallet/rpcwallet.cpp:503-513, + `getaddressesbyaccount`; src/wallet/rpcwallet.cpp:1572-1610, + `listreceivedbyaddress`; src/wallet/rpcwallet.cpp:791-800 and + src/wallet/wallet.cpp:5290-5375, `listaddressgroupings` inputs; + src/qt/walletmodel.cpp:219-229, analogous Qt creation; + src/qt/addresstablemodel.cpp:54-68, Qt purpose filtering; + src/wallet/walletdb.cpp:1332-1350 and src/wallet/wallet.cpp:5675-5682, + key-only recovery of v2 metadata. +- **Concrete scenario:** An application asks `getaddressesbyaccount` for + RVN receive addresses after `getnewpqassetaddress` used the same account. + It pays RVN to the returned synthetic P2PKH identifier. The wallet has + no matching ECDSA private key, so that RVN is not spendable by the + holder of the PQ program. A key-only recovery also drops the address + purpose, so Qt can reclassify the synthetic identifier as a send address. +- **Expected behavior:** Native RVN address listings must exclude the + synthetic asset identifier, including account, received-address and + grouping APIs. Asset-specific listings must preserve the complete + descriptor and PQ key association. +- **Regression required:** Generate a PQ-only asset destination for an + account; prove the synthetic identifier appears in the native address + listing before correction and is absent afterward, while the witness-v2 + native address remains present and the asset descriptor survives reload. + Cover `listreceivedbyaddress` with empty entries and + `listaddressgroupings` after receiving an asset. Recover keys only and + verify the Qt address-table classification remains hidden. +- **Remediation commit:** `5f34f8cbf` filters the canonical synthetic ID + from native address listings and groupings, and preserves its hidden + address-book purpose after key-only recovery. +- **Verification:** The new `getaddressesbyaccount` assertion failed + before the filter and passed afterward. The source RPC test also checks + `listreceivedbyaddress` and `listaddressgroupings`. The wallet unit + vector checks recovered purpose and canonical ID classification; + both tests pass at `6ed46c91c`. Both required security jobs passed the + source RPC test and wallet unit vectors at `9151946ad`. +- **Final status:** FIXED on the qualified candidate SHA; final release-SHA + qualification remains due. + +### FINDING-164: PQ-only restricted-asset status misses wallet ownership + +- **Severity:** MEDIUM wallet and GUI state regression. +- **Frozen initial status:** OPEN on 2026-10-10 at `767e1b2b8`, before a + contextual PQ ownership correction for restricted-asset events. +- **Affected RIP-25 invariant:** Tag, untag, freeze and unfreeze operations + affecting a wallet's PQ-only asset identifier must update its local + restricted-asset state and GUI after a confirmed block. +- **Affected Core 4.8.0 fix:** None. The synthetic identifier without an + ECDSA key is new to the PQ-only candidate. +- **Root cause:** `ContextualCheckNullAssetTxOut` decides whether to record + an address event for the private wallet database with generic + `IsMine(wallet, DecodeDestination(address))`. That detects classical + key ownership but cannot identify a PQ-only asset identifier derived + from an owned ML-DSA program. Only collected events reach the private + restricted-asset database. +- **Affected file/function/lines:** src/assets/assets.cpp:5704-5710, + wallet ownership test; src/validation.cpp:3097-3111, event storage and + `UpdateMyRestrictedAssets`; restricted-asset RPC and Qt listings. +- **Concrete scenario:** A valid tag or freeze for an owned PQ-only asset + identifier confirms on chain, but the wallet does not record the event. + Its personal restricted-asset views can then disagree with the chain. +- **Expected behavior:** Recognize the synthetic identifier through its + owned canonical PQ-only descriptor and preserve the existing update and + restart behavior. Do not claim unrelated identifiers as wallet-owned. +- **Regression required:** Tag and freeze a wallet-owned PQ-only + descriptor, mine the operations, and check the private DB, relevant + RPCs and Qt view. Repeat after restart and cover an unrelated program. +- **Remediation commits:** `b7153328d` adds tag and freeze regressions; + `6ed46c91c` records owned PQ-only identifiers in the private restricted + database and Qt history. +- **Verification:** `feature_pq_asset_anchor.py` failed before the + private-DB change because the tagged identifier was absent from + `viewmytaggedaddresses`. It passes afterward for tag, untag, freeze, + unfreeze, restart and an unpaired PQ program excluded from private + history. The full local `make check` passes at `6ed46c91c`. Both + required security jobs passed at `9151946ad`. +- **Final status:** FIXED on the qualified candidate SHA; final release-SHA + qualification remains due. + +### FINDING-165: PQ asset functional tests require optional Python RIPEMD160 + +- **Severity:** MEDIUM release qualification failure. +- **Frozen initial status:** OPEN on 2026-10-12, before changing the + functional tests or their hash assertions. +- **Affected RIP-25 invariant:** The required asset custody and source RPC + regressions must run on the pinned CI environment so a green final gate + proves the protected asset paths on the release SHA. +- **Affected Core 4.8.0 fix:** None. The dependency entered with the new + PQ-only functional assertions. +- **Root cause:** Two functional tests call the test framework's Python + `hash160` helper. It requires `hashlib.new('ripemd160', ...)`, which the + Ubuntu 22.04 runner's Python/OpenSSL combination does not provide. +- **Affected file/function/lines:** + test/functional/feature_pq_asset_anchor.py:776, unrelated program ID; + test/functional/feature_pq_asset_source_rpc.py:63, descriptor ID check; + test/functional/test_framework/script.py:33-34, hash helper. +- **Concrete scenario:** Final Gate run `38047870174` on + `3ed32302d37fb343231c20905d178016ab1a07c2` passes `make check` + but fails its behavioral security invariant step. Both functional tests + stop with `ValueError: unsupported hash type ripemd160`. Required + functional tests and the build matrix are skipped. +- **Expected behavior:** Preserve descriptor script and unrelated identifier + assertions without relying on an optional Python hash provider. Keep the + program-to-identifier binding check in the C++ consensus vectors. +- **Regression required:** Reproduce the failure with RIPEMD160 unavailable, + then run both tests successfully under the same restriction and in the + regular local test environment. Keep the C++ consensus binding vectors. +- **Remediation commit:** `3c1d8c091` removes the optional Python hash + dependency from both functional tests. +- **Verification:** Red CI run `38047870174`; both functional tests pass + locally with `hashlib.new('ripemd160', ...)` forced to raise, and local + `make check` passes. Both required security jobs ran the two functional + tests successfully at `9151946ad` on the pinned runner. +- **Final status:** FIXED on the qualified candidate SHA; final release-SHA + qualification remains due. + +### FINDING-166: Build Raven hides the failing unit test diagnosis + +- **Severity:** MEDIUM release qualification failure. +- **Frozen initial status:** OPEN on 2026-10-12, before changing the + workflow's mandatory unit-test step. +- **Affected RIP-25 invariant:** All release artifact builds must be gated + by a diagnosable, successful `make check` on their exact source SHA. +- **Affected Core 4.8.0 fix:** None. The gate is specific to this release + workflow. +- **Root cause:** Build Raven previously ran `make check` without printing + `src/test-suite.log` or `src/test/test_raven.log` on failure. Automake + printed only the aggregate `FAIL: test/test_raven` result. The original + unit assertion cannot be reconstructed from the retained CI log. +- **Affected file/function/lines:** .github/workflows/build-raven.yml:65-68, + mandatory unit-test step. +- **Concrete scenario:** Build Raven run `38047871989` on + `3ed32302d37fb343231c20905d178016ab1a07c2` builds successfully + but fails `make check` with one failing test binary. It skips all release + artifact jobs. Final Gate on the same SHA passes its unit tests, so the + particular Build Raven test failure has no retained diagnostic log. +- **Expected behavior:** Preserve `make check` as a hard gate and print its + bounded failure logs before exiting nonzero. If a failure recurs, use + its log to identify and remediate the underlying test defect. +- **Regression required:** Simulate a failing `make check` and prove the + workflow reports the specific test failure and still fails; then rerun + both workflows on one exact SHA. +- **Remediation commit:** `2c9576001` prints bounded test logs on a + nonzero `make check` result and keeps the artifact gate mandatory. +- **Verification:** Red CI run `38047871989`; a local simulated failure + showed the original step hid a test assertion, while the revised step + prints it and still fails. Local `make check` passes. Build Raven + `38296258242` and Final Gate `38296262444` passed their mandatory + `make check` at `9151946ad`. Build Raven `38335505917` and Final Gate + `38335508527` also passed at `6bdbae2b4`. The original run has no + preserved test log or artifact, so its particular assertion remains + unknown. On 2026-10-13 the owner accepted this documented uncertainty + after reviewing the diagnostic repair and repeated green paired runs. +- **Final status:** DISPOSED as an unclassifiable historical unit failure; + the diagnostic defect is FIXED and qualified on candidate CI. Mandatory + `make check` on the final 4.8.1 SHA remains a release gate. Any new + failure requires its own diagnosis and remedy. + +### FINDING-167: Pinned FreeType source host blocks the ARM64 artifact + +- **Severity:** MEDIUM release availability; the artifact gate fails closed. +- **Frozen initial status:** OPEN on 2026-10-12 at `a939b1629`, before + changing the FreeType recipe. +- **Affected RIP-25 invariant:** All five Build Raven release targets must + produce artifacts from the same SHA as a green Final Gate. +- **Affected Core 4.8.0 fix:** None. The FreeType 2.7.1 version, Savannah + redirecting download URL and SHA-256 pin are inherited from Core 4.8.0. +- **Root cause:** On the ARM64 runner, repeated connections to + `download.savannah.gnu.org` timed out. The generic depends fallback URL + was empty, so the final curl call failed with an invalid URL. +- **Affected file/function/lines:** `depends/packages/freetype.mk:1-5`, + pinned source URL and digest; `depends/funcs.mk:22-35`, fetch fallback. +- **Concrete scenario:** Build Raven run `38234048442` on + `1df43214f8159826cb8a27cda29c23c299be3873` passed its mandatory + security gate, then the `build (aarch64, aarch64-linux-gnu)` job failed + in Build Dependencies with six curl connection timeouts and a final + empty-URL error. The same SHA's Final Gate run `38234051822` passed. +- **Expected behavior:** Retrieve the same pinned FreeType archive from a + reachable official mirror, verify its existing SHA-256 digest, and fail + closed if the bytes differ. Requalify all release targets on one SHA. +- **Regression required:** Preserve the red CI log; fetch the archive from + the replacement source and verify its digest against the recipe; run a + clean ARM64 depends fetch and rerun Build Raven and Final Gate on one SHA. +- **Remediation commit:** `829e895e3` switches to the official Savannah + direct mirror without changing the archive name, version or digest. +- **Verification:** The official Savannah direct mirror and the upstream + FreeType SourceForge release both supplied an archive matching the pinned + digest `3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88` + in independent local fetches. A clean ARM64 depends fetch through the + revised recipe passed and checked the same digest. The red CI run is + `38234048442`. Build Raven `38296258242` built the aarch64 artifact + `11771315730` and the other four release targets at `9151946ad`; + paired Final Gate `38296262444` succeeded at that SHA. After the + recurrence and fallback repair in FINDING-171, Build Raven + `38433449366` completed the ARM64 artifact `11837205453` and the + other four release targets on exact versioned SHA + `bfa2efbb3f47dca3e32df3c73b74625d5c64b5ee`; Final Gate + `38433455013` passed on the same SHA. +- **Final status:** FIXED on the paired versioned candidate SHA after + the fallback repair in FINDING-171. Final release-SHA artifact + qualification remains due. + +### FINDING-168: Wallet rewrite recovery can leave PQ plaintext in the database file + +- **Severity:** HIGH confidentiality and release qualification failure. +- **Frozen initial status:** OPEN on 2026-10-12 at `471deaeea`, before + changing the wallet or its regression. +- **Affected RIP-25 invariant:** After wallet encryption and recovery, a + plaintext ML-DSA secret must not remain in the physical wallet database. +- **Affected Core 4.8.0 fix:** None. Core 4.8.0 uses the same Berkeley DB + optimization; the 2,560-byte ML-DSA secret and recovery test are new to + this phase. +- **Root cause:** Pinned Berkeley DB 4.8.30 sets the new BTREE page clear + length to 32 bytes when its own encryption is off. Its memory pool can + reuse buffers containing a deleted 2,560-byte PQ secret, and the rest of + a newly allocated 4,096-byte page is not cleared before it is written. + Logical record deletion and a successful rewrite therefore do not prove + the active wallet file is free of those bytes. +- **Affected file/function/lines:** `src/wallet/test/pq_wallet_tests.cpp:255-263` + and lines 2969-3133, physical secret scan after restart recovery; + `src/wallet/db.cpp:788-960`, database rewrite and file replacement; + pinned Berkeley DB `db/db.c:545-557`, page clear-length policy, and + `mp/mp_fget.c:804-814`, new-page initialization. +- **Concrete scenario:** Build Raven run `38238492777` on + `471deaeead7533d238d03e675235a07f13e41e1e` passed mandatory + `make check`, then failed `pq_wallet_tests/rewrite_failure_quarantines_until_restart_recovery` + at line 3085 because the physical wallet file still contained the exact + ML-DSA secret after startup rewrite recovery. Final Gate run + `38238496806` passed the same test and SHA, so the condition appears + intermittent. Build Raven skipped every release artifact. +- **Expected behavior:** Recovery keeps the wallet quarantined on rewrite + failure. Once recovery reports success, the physical wallet file and + any exportable backup contain no plaintext PQ secret. +- **Regression required:** Preserve the red CI run. Assert that the + Berkeley DB wallet page clear length equals its page size, retain the + exact physical secret scan after recovery, then demonstrate a red-to-green + result with the patched dependency and rerun both CI workflows at one + exact SHA. Keep `make check` mandatory. +- **Remediation commits:** `77036141c` adds the deterministic red + clear-length regression. `5f21bb18f` patches the pinned Berkeley DB + Btree, Recno and Hash page policy to clear every new page in full. + `d64c34830` makes `CDB::Rewrite` refuse a dependency whose new database + pages are not fully cleared, preserving wallet quarantine on failure. +- **Verification:** Red Build Raven run `38238492777`; green Final Gate + run `38238496806` is conflicting evidence, not closure. The new + `wallet_database_clears_entire_new_page` regression failed against the + original pinned library with `32 != 4096`, then passed after rebuilding + against the patched dependency. Fifteen local full wallet suites and 250 + isolated recovery tests passed on Ubuntu 24.04 before the patch, + confirming that repetition alone is not a reliable release gate. After + the patch, local `make check`, the full RIP-25 behavioral invariant gate + with Python 3.10, and all nine required functional tests passed. These + checks cover the active wallet file and backups, not forensic erasure of + old filesystem blocks or Berkeley DB transaction logs. Build Raven + `38296258242` passed the new full-page-clear unit regression and the + restart-recovery physical-secret scan at `9151946ad`, then produced all + five artifacts. Paired Final Gate `38296262444` passed the same tests + and SHA. +- **Final status:** FIXED on the qualified candidate SHA; final release-SHA + qualification remains due, and FINDING-067 remains OPEN. + +### FINDING-169: Functional cache peer can remain at genesis during CI qualification + +- **Severity:** MEDIUM release qualification failure; the artifact gate fails closed. +- **Frozen initial status:** OPEN on 2026-10-13 at `7c0d43c85`, before a + cache connectivity remedy or diagnostic regression. +- **Affected RIP-25 invariant:** Required functional security tests must run + before release artifacts are accepted from a Build Raven run. +- **Affected Core 4.8.0 fix:** None. The failure occurs in the functional + test framework before an asset or PQ functional assertion runs. +- **Root cause:** The precise cause of the original CI event is not + established. Cache creation waited for RPC readiness but did not require + P2P connectivity before mining. During the failed run, node 7 remained + at genesis while nodes 0 through 6 reached height 25. The runner + retained no node debug logs as CI artifacts, so the original missing + connection or block acceptance cannot be distinguished from that log. +- **Affected file/function/lines:** `test/functional/test_framework/test_framework.py:385-417`, + cache node startup and synchronization; + `test/functional/test_framework/util.py:490-512`, block sync timeout; + `test/functional/test_runner.py:442-455`, cache failure reporting; + `.github/workflows/build-raven.yml:85-86`, required behavioral gate. +- **Concrete scenario:** Build Raven run `38366146893` on + `7c0d43c85550afc310a7a3a2e458fda3a274fc4f` failed in + `create_cache.py`: seven nodes had the same height-25 block hash, while + node 7 reported height 0 for the full 60-second wait. The five artifact + jobs were skipped. Final Gate run `38366157600` passed the behavioral + and required functional suites on the identical SHA. +- **Expected behavior:** Cache creation establishes usable peer connections + before mining, fails with enough node evidence to diagnose any persistent + disconnect, and never reports a green artifact gate if its tests did not run. +- **Regression required:** Reproduce a missing cache peer deterministically, + show the failure before remediation, then show cache creation recovering + or diagnosing it after remediation. Rerun mandatory `make check`, Build + Raven and Final Gate at one exact SHA. +- **Remediation commit:** `e6d033883` requires each cache follower to + establish a versioned peer connection to node 0 before mining, retries + a missing connection, and includes peer state when later block sync + fails. The real eight-node regression is part of both CI behavioral + gates through `check-rip25-v48-invariants.sh`. +- **Verification:** Before the repair, the regression withheld node 7's + startup connection and reproduced the observed height pattern: nodes + 0 through 6 reached 25, node 7 remained at genesis, and sync timed + out. After the repair the same eight-node test passed in 3.1 seconds; + a fresh complete `create_cache.py` run passed in 4.4 seconds. Local + `make -j8 check` passed. The full behavioral gate, including the new + regression and nine functional cases, passed under Python 3.10. The + original CI cause remains uncertain because its node logs were lost. + On exact SHA `06f9d90fd0d213ad33b078c87fd1e9f4fd05edde`, the + required security job in Build Raven `38395943288` and the security + job plus all seven builds in Final Gate `38395942637` passed. Both + security jobs ran the cache regression successfully. Build Raven as a + whole failed only in its ARM64 FreeType fetch, recorded in FINDING-171. + On exact versioned SHA + `bfa2efbb3f47dca3e32df3c73b74625d5c64b5ee`, Build Raven + `38433449366` and Final Gate `38433455013` both completed successfully. + The required security jobs passed the cache regression and mandatory + `make check`; Build Raven completed all five release targets and Final + Gate all seven builds. +- **Final status:** FIXED on the paired versioned candidate SHA. The + final release SHA still requires the same two wholly green workflows + after the later optional-tool repair in FINDING-172. + +### FINDING-170: Standalone cache peer regression cannot locate ravend in CI + +- **Severity:** MEDIUM release qualification failure; the artifact gate fails closed. +- **Frozen initial status:** OPEN on 2026-10-13 at `4078257ee`, before + correcting the new standalone regression's binary path. +- **Affected RIP-25 invariant:** A required behavioral regression must run + against the just-built node binary before artifacts are accepted. +- **Affected Core 4.8.0 fix:** None. This is a test harness integration + defect introduced during FINDING-169 remediation. +- **Root cause:** The standalone unittest called the functional cache + initializer without setting `RAVEND`. The initializer then attempted + to execute the bare name `ravend`; the CI runner does not place the + just-built binary on `PATH`. +- **Affected file/function/lines:** + `test/functional/test_framework/test_cache_peers.py:29-60`, standalone + regression setup; `test/functional/test_framework/test_framework.py:385-395`, + cache node command; `contrib/devtools/check-rip25-v48-invariants.sh:807`, + required invocation. +- **Concrete scenario:** Build Raven run `38394096626` on exact SHA + `4078257ee2862586eb7a0c07ed79b7ee4635f5f3` passed mandatory + `make check`, wallet behavioral cases, fuzz smoke, and gate helper + checks. Its new cache peer unittest then raised `FileNotFoundError` + for `ravend` before starting any node; all five artifact jobs were skipped. +- **Expected behavior:** The standalone test resolves the built `src/ravend` + binary independently of the runner's ambient `PATH`, while still + withholding node 7's startup peer to test the recovery path. +- **Regression required:** Run the test with `RAVEND` unset and a `PATH` + lacking the build tree; show the failure before correction and success + afterward. Rerun mandatory `make check`, Build Raven, and Final Gate + on one exact post-correction SHA. +- **Remediation commit:** `d45aa5b44` resolves the built node binary + from the regression test's source-tree location and supplies it as + `RAVEND` only for that test. +- **Verification:** The isolated local command reproduced the same + `FileNotFoundError` before correction. With the test deriving the + binary path from its source location, it passed in 3.1 seconds under + the same restricted environment. Local `make -j8 check` and the full + behavioral invariant gate with nine focused functional cases passed + under Python 3.10 after the correction. On exact SHA + `06f9d90fd0d213ad33b078c87fd1e9f4fd05edde`, the required security + jobs in Build Raven `38395943288` and Final Gate `38395942637` both + passed the standalone cache regression. Final Gate passed all eight + builds; Build Raven's ARM64 artifact failed for the separate FreeType + source timeout in FINDING-171. + On exact versioned SHA + `bfa2efbb3f47dca3e32df3c73b74625d5c64b5ee`, Build Raven + `38433449366` and Final Gate `38433455013` both completed successfully + with mandatory `make check`, all five release targets, and all eight + Final Gate builds. +- **Final status:** FIXED on the paired versioned candidate SHA. The + final release SHA still requires the same two wholly green workflows + after the later optional-tool repair in FINDING-172. + +### FINDING-171: FreeType mirror timeout again blocks the ARM64 release artifact + +- **Severity:** MEDIUM release availability; artifact qualification fails closed. +- **Frozen initial status:** OPEN on 2026-10-13 at + `06f9d90fd0d213ad33b078c87fd1e9f4fd05edde`, before another source + mirror or fallback change. +- **Affected RIP-25 invariant:** All five Build Raven release targets must + succeed at the same exact SHA as a green Final Gate. +- **Affected Core 4.8.0 fix:** The FreeType 2.7.1 archive and SHA-256 pin + are inherited from Core 4.8.0. FINDING-167 changed its primary host but + retained a single reachable-host dependency. +- **Root cause:** The ARM64 runner could not connect to + `download-mirror.savannah.gnu.org` during six curl attempts. The generic + fallback path was empty, so the final request used an invalid URL. +- **Affected file/function/lines:** `depends/packages/freetype.mk:1-5`, + primary source and pinned digest; `depends/funcs.mk:22-35`, fetch and + generic fallback; `.github/workflows/build-raven.yml`, ARM64 artifact job. +- **Concrete scenario:** Build Raven run `38395943288`, job + `115296217551`, passed checkout and security invariant checks but failed + in Build Dependencies before configuring Ravencoin. The log shows six + connection timeouts followed by `curl: (3)` for the empty fallback. + The required security job had passed; this is an artifact-source + availability failure, not evidence of an asset-consensus failure. +- **Expected behavior:** Fetch the exact pinned FreeType archive from a + second official source if the primary host is unreachable, validate the + unchanged SHA-256 before extraction, and reject any mismatch. +- **Regression required:** Preserve this red CI log; force primary-source + failure in a clean local depends fetch and prove the secondary source + downloads the exact pinned bytes. Then rerun Build Raven and Final Gate + at a common post-remediation SHA with all release targets green. +- **Remediation commit:** `38b84a9c7` adds a FreeType-specific SourceForge + fallback to the depends recipe. Each fetched archive is checked against + the unchanged pinned digest before it reaches the source directory. +- **Verification:** With the primary URL forced to the closed local port + `127.0.0.1:9`, a clean ARM64 depends fetch used the SourceForge fallback + and accepted the archive with SHA-256 + `3a3bb2c4e15ffb433f2032f50a5b5a92558206822e22bfe8cbe339af4aa82f88`. + The same forced primary failure followed by altered fallback bytes + failed its hash check and left no source archive in place. The red CI + evidence remains Build Raven `38395943288`, job `115296217551`. + On exact versioned SHA + `bfa2efbb3f47dca3e32df3c73b74625d5c64b5ee`, Build Raven + `38433449366` passed all five release targets, including ARM64 + artifact `11837205453`, and Final Gate `38433455013` passed all + eight builds and its required security job. Both security jobs passed + mandatory `make check`. +- **Final status:** FIXED on the paired versioned candidate SHA; + final release-SHA artifact qualification remains due. FINDING-067 + remains OPEN. + +### FINDING-172: Optional raven-tx cannot link the asset and PQ helpers + +- **Severity:** LOW release tooling failure; the default release artifact + workflow does not enable `raven-tx`. +- **Frozen initial status:** OPEN on 2026-10-13 at + `bfa2efbb3f47dca3e32df3c73b74625d5c64b5ee`, before changing its + link libraries. +- **Affected RIP-25 invariant:** The 4.8.1 release process regenerates + manpages from current utilities. The optional `--with-tx` utility must + link for the normal Automake build of that target. +- **Affected Core 4.8.0 fix:** Core 4.8.0 already omitted the asset/server + library from `raven_tx_LDADD`. The current PQ-only asset helper adds + another missing symbol to that existing link gap. +- **Root cause:** The target links common transaction objects, including + `coins.cpp` and `script/sign.cpp`, but omits the server archive that + defines the asset helpers they call. Other server dependencies may + also be needed once the archive is included. +- **Affected file/function/lines:** `src/Makefile.am:531-565`, optional + target link list; `src/Makefile.am:247-270`, server asset sources; + `src/assets/assets.cpp:3719`, PQ-only helper. +- **Concrete scenario:** After configuring this checkout with + `--with-tx --with-gui=no` and the pinned local depends prefix, + `make -C src -j8 raven-tx` exited 2. Its linker reported unresolved + `CAssetsCache`, `AreAssetsDeployed`, and `IsPQOnlyAssetScript` symbols. + The default `--without-tx` local `make -j8 check` passed. +- **Expected behavior:** The optional utility links against the full + dependency closure and its current `--help` can regenerate the manpage. +- **Regression required:** Preserve the red link result, then build + `raven-tx` successfully with `--with-tx`, run help and transaction + creation smoke checks, rerun mandatory `make check`, and exercise the + optional target in the required CI security builds. +- **Remediation commits:** `09277a96b` places the mutually dependent + common, server, and wallet objects in one aggregate static archive and + adds the missing external libraries; `af0cc47d4` enables `--with-tx` + in both required Linux security builds. +- **Verification:** Red local link log is private + `evidence/f172-raven-tx-red.log`; CI release artifacts are unaffected + because the Build Raven configure script does not pass `--with-tx`. + Before the fix, libtool removed repeated core archives and moved GNU + linker group flags ahead of them; a direct linker invocation supplied + the current manpage from the same source. After the fix, local + configuration with the pinned depends prefix, Qt 5.12.11, and + `--with-tx` built `libraven_tx_core.a` and linked `raven-tx` through + Automake. Its `--help` and `-create -json` smoke checks passed, as did + `make -j4 check`. The aggregate archive contains 152 core objects. + Both required Linux CI security jobs now exercise this optional target. + Cross-platform execution of the optional tool is outside the release + artifact matrix. +- **Final status:** FIXED. The required CI security jobs in Build Raven + 38475261912 and Final Gate 38475261551 both built the optional + Automake raven-tx target and passed mandatory make check on exact + SHA a16a05bdb925985bc33694455e582cd7d2261958. All five Build + Raven artifacts and seven Final Gate builds also passed. The five + release artifacts do not contain this optional utility. + +### FINDING-173: XCB source timeout blocks Linux release artifacts + +- **Severity:** MEDIUM release availability; exact-SHA artifact qualification fails closed. +- **Frozen initial status:** OPEN on 2026-10-14 at + `975ab4f4a49d6217fa213da87384950b4e21290d`, before changing either + XCB depends recipe. +- **Affected RIP-25 invariant:** Build Raven must produce all five release + targets on the same commit as a green Final Gate. +- **Affected Core 4.8.0 behavior:** The pinned libxcb 1.10 and xcb-proto 1.10 + source URLs are inherited depends configuration. Their expected SHA-256 + digests remain the release integrity checks. +- **Root cause:** `xcb.freedesktop.org` timed out during dependency download + in two release jobs. The generic fallback URL was empty, so the final + curl request failed before configuration or compilation. +- **Affected files:** `depends/packages/libxcb.mk`, + `depends/packages/xcb_proto.mk`, and `depends/funcs.mk` fetch fallback. +- **Concrete scenario:** Build Raven `38546424615` at the exact SHA above + passed its mandatory security job and produced Windows, ARM32, and macOS + artifacts. Linux x86_64 job `115800868704` timed out fetching + `libxcb-1.10.tar.bz2`; ARM64 job `115800868477` timed out fetching + `xcb-proto-1.10.tar.bz2`. Both ended in the `Build Dependencies` step. + Final Gate `38546434041` passed all eight jobs at the same SHA. The + earlier exact-SHA Build Raven `38475261912` fetched both XCB archives + successfully from the same host, so the failure is source availability, + not evidence of a consensus or compiler defect. +- **Expected behavior:** A second pinned source supplies the exact same + archive when the first source is unavailable. The fixed SHA-256 must be + checked before extraction; a digest mismatch must fail closed. +- **Regression required:** Preserve both red CI jobs. Force the first + source to fail in a clean local fetch, prove that the alternate source + supplies both expected bytes, and prove that altered bytes are rejected. + Then rerun Build Raven and Final Gate at one final SHA. +- **Remediation commit:** `fecdcdaee865f9d95eb931a447ff9570562eafde` + adds a package-specific HTTPS fallback for both pinned XCB archives. + Their versions and SHA-256 digests are unchanged. +- **Verification:** The original `38546424615` attempt 1 remains the red + artifact evidence. Its same-SHA attempt 2 passed all five release jobs + once the original XCB host responded. In a clean local fetch with the + primary URL forced to `127.0.0.1:9`, both fallback downloads matched + their pinned digests. One-byte-altered fallback archives were rejected + for both packages, without installing source archives. Private logs: + `evidence/f173-xcb-fallback-green.log`, + `evidence/f173-xcb-fallback-reject.log`, and + `evidence/f173-xcb-proto-fallback-reject.log`. Build Raven + `38592937851` passed its security job and all five release targets on + exact `fecdcdaee`; the restored primary host served the XCB archives in + that run, so the forced local test supplies direct fallback evidence. + Final Gate `38592942408` failed separately at pinned ccache first use, + recorded in FINDING-174. +- **Final status:** FIXED for the XCB availability defect. A wholly green + Build Raven and Final Gate pair at the final release SHA is still due. + +### FINDING-174: Parallel first use of pinned ccache can abort the test gate + +- **Severity:** MEDIUM release availability; the mandatory security job + fails closed before tests or artifact builds run. +- **Frozen initial status:** OPEN on 2026-10-14 at + `fecdcdaee865f9d95eb931a447ff9570562eafde`, before changing the + CI cache initialization. +- **Affected RIP-25 invariant:** Mandatory `make check` and all release + builds must complete at one exact candidate SHA. +- **Affected Core 4.8.0 behavior:** The pinned native ccache 3.3.4 recipe + is inherited; the first-use workflow setup is release CI configuration. +- **Root cause:** Two compiler processes can start ccache concurrently + when its per-runner `ccache.conf` is absent. In ccache 3.3.4, + `initialize` reads that path and then checks it with `stat` after a + failed open. One process can create the file between the other + process's failed open and `stat`, causing the latter to report the + original missing-file error as fatal. Creating only the directory + does not eliminate this race. +- **Affected files:** `.github/workflows/rip25-v48-final-gate.yml` and + `.github/workflows/build-raven.yml`, the cache setup before parallel + compilation; pinned ccache 3.3.4 `ccache.c:2980-3073`. +- **Concrete scenario:** Final Gate `38592942408`, security job + `115952251224`, on the exact SHA above failed in `Build tests` at its + first parallel C++ objects with + `ccache: error: /home/runner/.ccache/ccache.conf: No such file or directory`. + No `make check` result exists for that run. Build Raven `38592937851` + passed its security job and all five artifact jobs at the same SHA; + the independent failure is timing-dependent. +- **Expected behavior:** Initialize the pinned ccache configuration once + serially before any parallel compile in each workflow job. Preserve + existing configuration when present. +- **Regression required:** Retain the red Final Gate log, start the pinned + ccache from an empty cache directory and prove serial initialization + creates its configuration, then run parallel compiler wrappers without + the missing-file error. Both exact-SHA workflows must pass afterward. +- **Remediation commit:** `8efa331e0` runs the pinned ccache + `--print-config` once after creating its directory and before any + parallel compile in both security jobs and both artifact matrices. + Existing cache configuration is preserved. +- **Verification:** The red Final Gate log is private + `evidence/f174-final-gate-38592942408-failed.log`. A clean local + ccache directory gained `ccache.conf` from the serial invocation; + 16 subsequent parallel compiler wrappers all exited successfully + and produced their object files. Its JSON record is + `evidence/f174-ccache-first-use-local.json`. The required functional + list gate and local `make -j4 check` passed after the workflow edit; + log: `evidence/local-make-check-f174-candidate.log`. Build Raven + `38713686026` and Final Gate `38713685824` passed on exact + `0c33cc92c`; both mandatory security jobs ran `make check`, all five + release targets and seven Final Gate matrix jobs passed, and the five + downloaded release packages passed checksum and provenance review. + The private verification record is + `evidence/ci-0c33cc92c-verification.json`. +- **Final status:** FIXED for the ccache first-use race. Release + qualification on the later final source SHA remains FINDING-067. + +### FINDING-175: xproto source timeout blocks the Linux release artifact + +- **Severity:** MEDIUM release availability; the artifact job fails closed + before building the Linux x86_64 package. +- **Frozen initial status:** OPEN on 2026-10-15 at + `ee30148e794ab5fec6c07d621e6bb4a581ef942f`, before changing the + pinned xproto fetch recipe. +- **Affected RIP-25 invariant:** Build Raven must deliver all five release + targets on the same exact SHA as a green Final Gate. +- **Affected Core 4.8.0 behavior:** The xproto 7.0.26 package recipe and + its HTTP primary source are inherited from Core 4.8.0. This is a + dependency availability defect, not a consensus failure. +- **Root cause:** The primary xproto archive host timed out repeatedly. + The generic fetch then attempted an unset fallback download path, + producing a malformed URL and terminating the depends build. +- **Affected file/function:** `depends/packages/xproto.mk`, the pinned + source recipe; `depends/funcs.mk`, generic fetch fallback. +- **Concrete scenario:** Build Raven `38710185861`, Linux x86_64 job + `116347291798`, failed in `Build Dependencies` after repeated + `curl: (28) Connection timeout` for `xproto-7.0.26.tar.bz2` and + `curl: (3) URL using bad/illegal format or missing URL` on the empty + fallback. The mandatory security job at that SHA passed. +- **Expected behavior:** When the primary host is unavailable, an HTTPS + alternate source supplies exactly the pinned archive. A SHA-256 + mismatch must reject the archive before extraction. +- **Regression required:** Retain the red CI log; force the primary + source to fail in a clean local fetch, verify the alternate archive's + existing SHA-256, and prove that altered bytes fail closed. Then rerun + both workflows on one final SHA. +- **Remediation commit:** `8552b2ca94255e1fd02f5411a7b2ae1ba7e6062a` + adds a package-specific HTTPS fallback. The xproto version and pinned + SHA-256 are unchanged. +- **Verification:** The red CI job log is private + `evidence/build-raven-38710185861-linux-job.raw.log`. A clean local + fetch with the primary URL forced to `127.0.0.1:9` reproduced the + empty fallback failure before the fix. With the new recipe, the same + forced-primary test fetched the alternate archive and matched its + existing digest. A one-byte-altered alternate archive was rejected + before installation. Private logs: `evidence/f175-xproto-red.log`, + `evidence/f175-xproto-green.log`, and + `evidence/f175-xproto-reject.log`. Local `make -j4 check` passed after + the fix, including `test/test_raven`; log: + `evidence/local-make-check-f175-candidate.log`. +- **Final status:** FIXED. Build Raven `38753089034` and Final Gate + `38753089260` passed on `35fa04a40`; all five downloaded + packages passed provenance, individual checksums, and common-source + verification in + `evidence/ci-35fa04a40/artifact-verification.json`. + Final release-SHA qualification remains FINDING-067. + +### FINDING-176: libXau source has the same empty fallback failure + +- **Severity:** MEDIUM release availability; the Linux depends build can + fail closed before packaging. +- **Frozen initial status:** OPEN on 2026-10-15 at `6e0a2213e`, before + changing the pinned libXau fetch recipe. +- **Affected RIP-25 invariant:** The release SHA must build all five + targets after its mandatory security gate. +- **Affected Core 4.8.0 behavior:** The libXau 1.0.8 package recipe and + HTTP Xorg primary source are inherited from Core 4.8.0. +- **Root cause:** libXau depends on xproto and uses the same Xorg host + that timed out in FINDING-175. It has no package-specific alternate + source. The generic fallback URL is empty, so a primary failure ends + with an invalid URL instead of trying an available mirror. +- **Affected file/function:** `depends/packages/libXau.mk`, pinned source + recipe; `depends/funcs.mk`, generic fetch fallback. +- **Concrete scenario:** In an isolated local depends fetch with the + primary path forced to `127.0.0.1:9`, the unmodified recipe failed + with `curl: (3) URL rejected: No host part in the URL`. The prior + Linux CI job stopped at xproto before reaching libXau. The same Xorg + archive host timed out during a separate direct availability probe. +- **Expected behavior:** A second HTTPS source supplies the exact pinned + libXau archive when the primary is unavailable. The unchanged SHA-256 + must reject altered bytes before extraction. +- **Regression required:** Preserve the red local fetch, force the + primary source to fail after adding the alternate, verify the pinned + digest, reject a one-byte-altered archive, and rerun `make check` and + exact-SHA CI. +- **Remediation commit:** `f154953a8e554c7e32a2c878b41fb0795b014103` + adds a package-specific HTTPS fallback. The libXau version and pinned + SHA-256 are unchanged. +- **Verification:** `evidence/f176-libxau-red.log` records the missing + fallback before the fix. With the primary source forced to fail, the + new recipe downloaded the alternate archive with the pinned digest; + `evidence/f176-libxau-green.log` records the passing fetch. A + one-byte-altered archive failed its checksum before installation in + `evidence/f176-libxau-reject.log`. Local `make -j4 check` passed after + the fix, including `test/test_raven`; log: + `evidence/local-make-check-f176-candidate.log`. +- **Final status:** FIXED. Build Raven `38753089034` and Final Gate + `38753089260` passed on `35fa04a40`; all five downloaded + packages passed provenance, individual checksums, and common-source + verification in + `evidence/ci-35fa04a40/artifact-verification.json`. + Final release-SHA qualification remains FINDING-067. + +### FINDING-177: Qt GUI crashes when wallet support is disabled at runtime + +- **Severity:** MEDIUM release availability. The wallet-disabled GUI + exits with a segmentation fault before it can serve as a node UI. +- **Frozen initial status:** OPEN on 2026-10-15 at `35fa04a40`, before + changing the GUI or its release workflow. +- **Affected RIP-25 invariant:** The pinned Qt release package must + start in each supported runtime mode without dereferencing an + absent wallet control. +- **Affected Core 4.8.0 behavior:** Official Core 4.8.0 source + `b60f50e04` has the same unconditional currency-control call + and the same wallet-disabled construction path. +- **Observed failure:** Unmodified Linux release packages from + `9151946ad` and `0c33cc92c` both exited 139 when started + under Xvfb with a fresh testnet data directory and `-disablewallet`. + The `0c33cc92c` package opened its main regtest window with an + existing wallet, answered RPC, and shut down cleanly without that + option. Private red logs are + `evidence/gui-smoke-35fa/no-wallet-old-startup.log` and + `evidence/gui-smoke-35fa/no-wallet-startup.log`. +- **Root cause:** `RavenGUI` omits `walletFrame` and + `comboRvnUnit` when the wallet is disabled, but + `setClientModel` still calls `onCurrencyChange`. + That slot dereferences the null currency combo box after a valid + initial index. The affected sites are + `src/qt/ravengui.cpp:132-171,714-720,962-975,1864-1877`. + A local GDB run bypassing the separately documented GCC 13 + pinned-Qt fortify issue for that diagnostic process stopped at + `QComboBox::setCurrentIndex` from `RavenGUI::onCurrencyChange`. + Its private stack is + `evidence/gui-smoke-35fa/local-no-wallet-gdb.log`. +- **Expected behavior:** Wallet-disabled GUI startup reaches an RPC + ready state, retains its non-wallet controls, and stops cleanly. +- **Regression required:** Keep the red package launch, run a + focused Xvfb wallet-disabled startup and RPC smoke against the + pinned Linux GUI after the fix, add that smoke to the Linux + release build, and rerun `make check` and exact-SHA CI. +- **Remediation commits:** `4f6e6305c` adds the focused Xvfb + startup, liveness, RPC, and clean-shutdown regression to the Linux + Build Raven release job. `9861294c4` skips the absent wallet + currency control in the node-only GUI. +- **Verification:** The final regression script failed against the + unmodified `0c33cc92c` package with status 139 before RPC + readiness; private log: + `evidence/f177-red-final.log`. The local pinned-Qt GUI rebuilt + after the guard; with the same diagnostic-only Qt fortify shim + already required on this GCC 13 host, the script reached RPC + readiness, remained alive for two seconds, and shut down cleanly + through RPC. The shim is outside the repository and is not part of + any release package. Private log: + `evidence/f177-green-local-final.log`. Local + `make -j4 check` passed all 511 unit cases after the fix; + `evidence/local-make-check-f177.log` and + `evidence/local-make-check-f177-unit.log` retain the results. +- **Final status:** FIXED on tested source `77d8f4c65`. + Build Raven `38853895481`, Linux job `116826216457`, passed + the unmodified pinned-Qt startup smoke without the diagnostic + shim. Final Gate `38853895080` passed on the same SHA, and all + five Build Raven packages passed the checks recorded in + `evidence/ci-77d8f4c65/artifact-verification.json`. + Final release-SHA qualification remains FINDING-067. + +### FINDING-178: Wallet-disabled Qt GUI can crash when toolbar icons option changes + +- **Severity:** MEDIUM release availability. The node-only GUI can + dereference absent toolbar controls after startup. +- **Frozen initial status:** OPEN on 2026-10-15 at `9061e119b`, before + changing the affected GUI slot or its regression. +- **Affected RIP-25 invariant:** The pinned Qt release package must + remain usable in wallet-disabled mode when available options change. +- **Affected Core 4.8.0 behavior:** The wallet-only toolbar creation + path and unconditional options signal connection are inherited from + Core 4.8.0. +- **Observed failure path:** `RavenGUI::createToolBars` creates + `labelToolbar` and `m_toolbar` only when `walletFrame` exists. + `-disablewallet` omits that frame, while the Options dialog retains + its Window tab and Icons only option. Changing the option emits + `updateIconsOnlyToolbar`; the connected GUI slot dereferences both + absent controls in `src/qt/ravengui.cpp`. +- **Observed failure:** The pinned-Qt source GUI at `9061e119b` + reached RPC readiness with `-disablewallet` under Xvfb. The + focused regression opened Options, selected Window and Icons only, + then submitted the dialog. Qt exited with SIGSEGV, status 139. + GDB stopped in `QLabel::setPixmap` from + `RavenGUI::updateIconsOnlyToolbar` at + `src/qt/ravengui.cpp:918`. The Options mapper also emits the + toolbar signal on a normal OK submission even if the setting did + not change. +- **Expected behavior:** Changing the option in node-only mode must + not crash the GUI. The wallet-enabled toolbar must still update. +- **Regression:** Commit + `068a87ba2067ab32ef2877ee39e3fce394926339` adds a + real Xvfb Options interaction to the Linux Build Raven job. + It verifies RPC readiness, opens the dialog, selects Icons only, + submits, checks the stored setting and process liveness, then + shuts down through RPC. It failed before the GUI fix; private + evidence is `evidence/f178-options-red.log` and the GDB + trace in `evidence/f178-options-gdb.log`. +- **Remediation commit:** + `0441b38d34ab8622c28bd59e216c3386a0fc0ca9` skips the + toolbar update when its wallet-only controls are absent. +- **Verification:** The same Xvfb regression passed after the guard, + including the persisted Icons only setting and clean RPC + shutdown; private log: + `evidence/f178-options-green-local.log`. The local pinned + Qt build required the previously documented GCC 13 diagnostic + shim, which is outside the repository and package. Local + `make -j4 check` passed all 511 unit cases after the fix; + `evidence/f178-local-make-check-unit.log` preserves the result. +- **Final status:** FIXED on tested source `77d8f4c65`. + Build Raven `38853895481`, Linux job `116826216457`, passed + the unmodified pinned-Qt Options smoke without the diagnostic + shim. Final Gate `38853895080` passed on the same SHA, and all + five packages passed provenance and checksum checks in + `evidence/ci-77d8f4c65/artifact-verification.json`. + Final release-SHA qualification remains FINDING-067. + +### FINDING-179: Bit-12 rollout notes omit automatic miner signaling and a go/no-go gate + +- **Severity:** HIGH release safety. A pool can count toward bit-12 lock-in + before its validating ecosystem is ready for the incompatible block rules. +- **Frozen initial status:** OPEN on 2026-10-15 at `22b65545c`, before + changing the release notes or adding a rollout documentation regression. +- **Affected RIP-25 invariant:** Operators must coordinate an upgrade of + miners, pools, validators, and services before signaling can lock in the + 12 MWU and PQ witness rules. +- **Observed gap:** The 4.8.1 release notes require readiness before + signaling but do not say that upgraded mining templates set bit 12 + automatically in STARTED. They do not define a recorded go/no-go decision. + The notes also omit the need to deploy revised parameters to all miners + and validators if a replacement release moves the start window. +- **Code behavior:** `CreateNewBlock` uses `ComputeBlockVersion`; + `pq_hybrid` is a forced getblocktemplate version bit; the + `-blockversion` override applies only on regtest. Mainnet operators + cannot rely on that override to disable the bit in 4.8.1 templates. +- **Expected behavior:** Release instructions must state the automatic + template signaling, identify the evidence and owner decision required + before the STARTED window, and describe a qualified replacement release + with coordinated deployment if readiness is absent. +- **Regression required:** Freeze a focused release-document contract + check that fails against the current instructions and passes after the + rollout instructions are corrected. Review the described behavior + against the actual mining and versionbits code. +- **Remediation commit:** `fe274e28b84ad168245e633c9f5b2cdcc305bee0` + explains automatic mainnet bit-12 signaling, the recorded + pre-STARTED go/no-go decision, and the need to qualify and deploy + a replacement release across affected operators if the schedule + must move. +- **Verification:** The focused source-and-document contract in + `contrib/devtools/test-rip25-release-contract.py` failed four + documentation assertions against the frozen release notes, while + its mining-source check passed. It passed all five checks after + the documentation change. Private logs are + `evidence/f179-f180-release-contract-red.log` and + `evidence/f179-f180-release-contract-green.log`. +- **Final status:** FIXED for the release instructions. The actual + network go/no-go decision and operator adoption remain OPEN + under FINDING-067. + +### FINDING-180: Generic tag-first process conflicts with 4.8.1 release qualification + +- **Severity:** MEDIUM release process. A tag made before final artifact + qualification could point to a candidate that later fails a mandatory gate. +- **Frozen initial status:** OPEN on 2026-10-15 at `32eb10141`, before + changing the release process or adding a process-order regression. +- **Affected RIP-25 invariant:** The release tag must identify exactly + the source SHA whose mandatory tests and five packages passed review, + after separate owner approval to create the tag. +- **Observed gap:** `doc/release-process.md` instructs maintainers to tag + before building binaries. The 4.8.1 remediation register instead + requires final-SHA `make check`, paired CI, artifact checksum and + provenance review, and owner authorization before tagging. +- **Expected behavior:** The release process should state a 4.8.1 + pre-tag qualification and approval sequence, then bind the tag to + that exact SHA. Any publication remains a separate owner decision. +- **Regression required:** A focused release-document contract check + fails before the process clarification and passes after it. +- **Remediation commit:** `fe274e28b84ad168245e633c9f5b2cdcc305bee0` + adds a 4.8.1-specific pre-tag qualification sequence to the + inherited general release process. +- **Verification:** The same focused contract failed on the old + tag-first instructions and passed after the new ordered steps + covered mandatory `make check`, paired exact-SHA CI, all five + artifact packages, provenance, and separate owner approval. + Private red and green logs are + `evidence/f179-f180-release-contract-red.log` and + `evidence/f179-f180-release-contract-green.log`. +- **Final status:** FIXED for the written process. FINDING-067 + retains the actual qualification, owner approval, and + publication gates. + +### FINDING-181: Build Raven loses the wallet RPC during rewrite-failure recovery + +- **Severity:** HIGH release qualification until the process exit is + explained. The mandatory Build Raven security step did not pass. +- **Frozen initial status:** OPEN on 2026-10-16 at `22b65545c` + after Build Raven run `38805825854` failed, before changing + the wallet, test, or workflow. +- **Affected RIP-25 invariant:** A failed wallet rewrite must not + silently lose wallet access or bypass recovery checks; the + release security test must report and verify the actual result. +- **Observed failure:** In the required functional test step, + `wallet_encryption_rewrite.py` raised + `ConnectionRefusedError` instead of the expected RPC error + while inducing a rewrite failure. Its node was unavailable + during cleanup. The other 32 required functional tests passed. + Build Raven skipped all artifact jobs. The paired Final Gate + run `38805825878` passed its security job and all seven builds + on the same `22b65545c` SHA, so the pair is not a release + qualification. +- **Initial classification:** The CI log proves a process/RPC + disappearance in this test, but does not yet establish whether + the wallet exited on the intended failure path, crashed, + or the test harness observed a timing race. +- **Root cause assessment:** On this induced failure path, + `CDB::Rewrite` refuses the injected directory and + `encryptwallet` calls `StartShutdown` before throwing + RPC error -16. HTTP replies are dispatched asynchronously; + the CI trace shows a disconnected reply followed by a + refused retry. A clean shutdown can therefore outrun the + expected RPC error. The failed runner did not publish its + node debug log or exit status, so the exact historical + process exit remains unproven. +- **Evidence:** Private retained CI output is + `evidence/ci-22b65545-build-raven-failed.log`. +- **Regression required:** Identify the mechanism, demonstrate + the failure against the frozen behavior, apply a narrow remedy, + rerun the same test and mandatory `make check`, then obtain + a fully green exact-SHA Build Raven and Final Gate pair with + five checked release packages. +- **Remediation commit:** + `9688f7e03e17dc9789039a1d2b0ea159a350a489` accepts only + the exact RPC -16 error or a narrow set of shutdown transport + errors. It then requires a clean node exit and the path-specific + `CDB::Rewrite: Refusing stale non-regular path` log entry + before the existing restart and key-recovery assertions. +- **Verification:** The pre-fix required Build Raven gate is + the red regression. The revised focused test passed 11 + consecutive runs, the required static gate passed six + checks, and the complete required functional set passed + 33 of 33 tests under Python 3.10, matching CI. Private + green records are `evidence/f181-wallet-encryption-rewrite-focused-run1.log`, + `evidence/f181-wallet-encryption-rewrite-focused-repeat10.log`, + `evidence/f181-required-functional-gate-static.log`, and + `evidence/f181-required-functional-gate-full-python310.log`. + Local `make -j4 check` then passed all 511 unit cases; + `evidence/local-make-check-f181-final.log` and + `evidence/local-make-check-f181-final-unit.log` retain + the result. + A separate host Python 3.12 attempt could not import + inherited `asyncore` in 13 unrelated functional tests; + it is excluded from the green result and retained as + `evidence/f181-required-functional-gate-full.log`. +- **Final status:** FIXED on tested source `77d8f4c65`. + Build Raven `38853895481`, security job `116820958622`, + passed the wallet rewrite recovery test and all 33 required + functional tests. Final Gate `38853895080` passed on the + same SHA; the five Build Raven packages passed the checks in + `evidence/ci-77d8f4c65/artifact-verification.json`. + Final release-SHA qualification remains FINDING-067. + +### FINDING-182: Linux Qt smoke lacks the pinned GUI XKB runtime + +- **Severity:** MEDIUM release availability. The Linux package job + cannot verify GUI startup while a required shared library is absent. +- **Frozen initial status:** OPEN on 2026-10-16 at `e67b23c7b`, + before changing the Build Raven runner dependencies. +- **Affected RIP-25 invariant:** The pinned-Qt Linux release GUI + must start in wallet-disabled mode without local diagnostic shims. +- **Observed failure:** Build Raven run `38805823946`, job + `116672035877`, passed its security gate and built the Linux + GUI, then failed `Test Qt startup without wallet`. The + pull-request run checked out synthetic merge `614dbd5ed` + of branch head `22b65545c` into base `7dc448453`. + The executable exited 127 because the runner lacked + `libxkbcommon-x11.so.0`; the subsequent missing RPC cookie + was a consequence of that startup failure. The package was + not published from this job. +- **Evidence:** Private job log + `evidence/ci-22b65545-build-raven-linux-qt-failed.log`. +- **Expected behavior:** The Linux runner must install the + pinned Qt GUI's host runtime requirements before both + wallet-disabled Xvfb smokes, then fail if the GUI itself + does not reach RPC readiness or clean shutdown. +- **Regression required:** Preserve the red missing-library + log, add the precise runner dependency, rerun both GUI + smokes on an unmodified CI build without a shim, and + qualify all five packages on the exact final SHA. +- **Remediation commit:** `1de647a88e2449afe445da77fdc7667052be505a` + installs the missing `libxkbcommon-x11-0` runner package and adds + an explicit dynamic-library preflight before the Xvfb smokes. +- **Verification:** Workflow YAML, job ordering, mandatory + `make check`, and the five release targets passed local structural + checks. The failed PR package job is the retained red run. + Build Raven `38853895481`, Linux job `116826216457`, installed + `libxkbcommon-x11-0`, passed the dynamic-library preflight, + passed both unmodified GUI smokes, and uploaded the Linux + package. All five packages passed checks in + `evidence/ci-77d8f4c65/artifact-verification.json`. +- **Final status:** FIXED on tested source `77d8f4c65`. + Final Gate `38853895080` passed on the same SHA. Final + release-SHA qualification remains FINDING-067. + +### FINDING-183: dbus source timeout leaves ARM64 depends without a fallback + +- **Severity:** MEDIUM release availability. The ARM64 package + cannot build when its primary dbus source host times out. +- **Frozen initial status:** OPEN on 2026-10-16 at `e67b23c7b`, + before changing the pinned dbus fetch recipe. +- **Affected RIP-25 invariant:** All five release packages must + be reproducible from pinned source archives on the exact SHA. +- **Observed failure:** Build Raven run `38805823946`, job + `116672036056`, timed out repeatedly while fetching + `dbus-1.10.18.tar.gz`. The generic fallback URL was empty, + so curl failed with an invalid URL and Build Dependencies + stopped. This job also checked out synthetic merge + `614dbd5ed`. The run's mandatory security job passed, but + no complete five-target release set existed. +- **Evidence:** Private job log + `evidence/ci-22b65545-build-raven-arm64-dep-failed.log`. +- **Expected behavior:** A second HTTPS source supplies the + identical pinned archive when the primary is unavailable; + the existing SHA-256 check must still reject altered bytes. +- **Regression required:** Force the primary dbus source to + fail against the old recipe, demonstrate the missing + fallback, then verify alternate download, pinned digest, + rejection of altered bytes, `make check`, and exact-SHA + paired CI with five checked packages. +- **Remediation commit:** `21962b1804b1ab5960b6042756cd5d2a2b3c155e` + adds the package-specific HTTPS fallback at + `download.bitcoinabc.org/depends-sources` while preserving the + pinned dbus 1.10.18 SHA-256. +- **Verification:** An isolated ARM64 depends fetch forced the + primary source to fail. The frozen recipe failed with an empty + fallback URL; the remedy fetched a 1,986,589-byte archive from + the alternate host and accepted only SHA-256 + `6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e38cc514a`. + An altered fallback archive failed the checksum and left no + accepted source file. Private logs are + `evidence/f183-red-frozen-forced-primary.log`, + `evidence/f183-green-forced-primary.log`, and + `evidence/f183-corrupt-fallback-rejected.log`. + With both FINDING-182 and FINDING-183 remedies present, local + `make -j4 check` passed all 511 unit cases at `21962b180`. + Private logs are `evidence/local-make-check-f182-f183.log` + and `evidence/local-make-check-f182-f183-unit.log`. + Build Raven `38853895481`, ARM64 job `116826216414`, built + and uploaded its package. The primary dbus source succeeded in + that CI job; the forced-primary-failure and altered-byte tests + above are the fallback-path evidence. All five downloaded + packages passed the provenance and checksum checks in + `evidence/ci-77d8f4c65/artifact-verification.json`. +- **Final status:** FIXED on tested source `77d8f4c65`. + Final Gate `38853895080` passed on the same SHA. Final + release-SHA qualification remains FINDING-067. + +### FINDING-184: Exact-multiple reward recipients create an empty payout batch + +- **Severity:** MEDIUM. A completed payout can be reported as a failed + distribution, complicating safe operator reconciliation. +- **Frozen initial status:** OPEN before remediation. +- **Affected RIP-25 invariant:** Asset reward retries must not manufacture a + transaction with no recipients after committing all intended batches. +- **Affected Core 4.8.0 fix:** Inherited from official Core 4.8.0 tag + `22549129888d02e0e08fcdb9f96f3c699167e774`. +- **Root cause:** `src/assets/rewards.cpp` computes the transaction count as + `paymentDetails.size() / MAX_PAYMENTS_PER_TRANSACTION + 1`. When the + recipient count is an exact multiple of 1,000, the final iteration starts + at the end of the vector and calls `BuildTransaction` with no recipients. +- **Concrete scenario:** A reward has exactly 1,000 eligible addresses. The + first batch can be committed, then an empty second batch fails creation and + leaves a failure status for a distribution whose intended payout already + occurred. A repeated request is rejected as a duplicate. +- **Expected behavior:** Use a checked ceiling count, skip empty batches, + preserve each durable batch intent, and report completion only after all + real batches are resolved. +- **Regression required:** Prove the old exact-multiple failure, then test + recipient counts around the 1,000 boundary, including zero, 999, 1,000, + 1,001, and 2,000. Assert no empty transaction attempt and no duplicate + payout on restart. +- **Remediation commit:** `c90814b1a` uses a ceiling batch count without + manufacturing an empty batch at exact multiples of 1,000. +- **Verification:** `asset_tests/reward_batch_boundaries` failed six checks + on the old calculation at zero, 1,000, and 2,000 recipients. It passed + after the fix for zero, one, 999, 1,000, 1,001, 1,999, 2,000, and 2,001. + `make -j4 check`, `feature_rewards.py`, and the separate + `feature_rewards_restart.py` single-payout retry test passed. The boundary + unit test proves that no empty batch is requested; the restart test checks + that persisted batch intent does not duplicate a payout. +- **Final status:** FIXED locally; exact-SHA CI remains due. + +### FINDING-185: ARM release builds lack a fontconfig source fallback + +- **Severity:** MEDIUM release availability. Both ARM packages are absent from + a Build Raven run that otherwise passed its security gate and other targets. +- **Frozen initial status:** OPEN on 2026-10-19, before changing the pinned + fontconfig depends recipe. +- **Affected RIP-25 invariant:** Every release target must package the same + tested source SHA, with every downloaded dependency checked against its + pinned digest. +- **Observed failure:** Build Raven `39143408033` at `54cd1befe` passed its + required security tests, Windows, macOS, and Linux. ARM32 job + `117725944066` and ARM64 job `117725944097` timed out six times fetching + `fontconfig-2.12.1.tar.bz2` from `mirrors.mit.edu`, then called the generic + fallback with an empty URL (`curl: (3)`). Final Gate `39143408017` passed + at the same SHA, including its ARM builds, but does not replace the missing + Build Raven packages. +- **Root cause:** `depends/packages/fontconfig.mk` has one download host and + no package-specific fallback. The generic fallback in `depends/funcs.mk` + has no configured `FALLBACK_DOWNLOAD_PATH` in these jobs. +- **Expected behavior:** Use an available pinned-source mirror if the primary + times out, reject bytes that do not match the existing SHA-256, and produce + both ARM packages in Build Raven on the final candidate SHA. +- **Regression required:** Force primary failure and prove the fallback fetch + passes the pinned digest; prove altered bytes are rejected; re-run both ARM + release builds and all other mandatory jobs at one exact SHA. +- **Remediation commit:** `c70679b80` adds a package-specific MacPorts + archive fallback while retaining the pinned version and SHA-256. +- **Verification:** A forced primary failure reached the empty fallback URL + on the original recipe. With the fix, the same failure fetched the + 1,608,901-byte MacPorts archive and verified digest + `b449a3e10c47e1d1c7a6ec6e2016cca73d3bd68fbbd4f0ae5cc6b573f7d6c7f3`. + A deliberately wrong expected digest rejected the archive without + installing a source file. Direct primary and fallback fetches passed. +- **Final status:** FIXED locally; Build Raven ARM32 and ARM64 packages on + the final candidate SHA remain due under FINDING-067. + +### FINDING-186: Legacy global message records become unreachable after wallet scoping + +- **Severity:** MEDIUM. An operator upgrading a node can lose access through + RPC to historical messages and subscriptions even though their LevelDB rows + remain on disk. +- **Frozen initial status:** OPEN, found during independent review of the + FINDING-102 wallet-scoping remedy before its commit. +- **Affected RIP-25 invariant:** Wallet isolation must preserve a safe way to + retrieve pre-upgrade notification history without guessing which wallet + owned each global record. +- **Affected Core 4.8.0 fix:** Core 4.8.0 stored messages under global `Z` + keys and subscribed channels under global `C` keys in separate LevelDBs. +- **Root cause:** The wallet-scoped message RPCs read only new `W` and `c` + records. Existing global rows have no wallet identity or reliable manual + subscription provenance, so automatically assigning them to a wallet would + leak data or fabricate user preferences. +- **Expected behavior:** Offer an explicitly labeled, read-only export of + persisted unscoped rows with each message outpoint and lossless payload. + Fail rather than silently omit malformed rows. Never import, delete, or + assign the exported rows to a wallet automatically. +- **Regression required:** Show the old RPC has no safe export, then test + valid legacy rows, an empty legacy database, and malformed legacy values. +- **Remediation commit:** `212610f62` adds the explicit node-level + `exportlegacymessagedata true` RPC. It returns persisted global messages + with outpoint, raw payload bytes, time, expiration, height, and status, + plus global channel names. It rejects wallet endpoints and never attributes + or imports a legacy row. Legacy database loaders fail on malformed rows + instead of returning a silently incomplete export. +- **Verification:** The functional export call failed with method-not-found + before the RPC was added. Unit tests failed on malformed legacy message + and channel rows before strict loading. The same functional and unit tests + pass after the fix, including valid and empty exports, CLI boolean + conversion, explicit acknowledgement, wallet-endpoint rejection, and + malformed-row errors. `make check -j4` and the invariant gate passed. +- **Final status:** FIXED locally; exact-SHA CI qualification pending. diff --git a/doc/man/raven-cli.1 b/doc/man/raven-cli.1 index 14975837c2..b365e98fc4 100644 --- a/doc/man/raven-cli.1 +++ b/doc/man/raven-cli.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH RAVEN-CLI "1" "September 2017" "raven-cli v0.15.99.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. +.TH RAVEN-CLI "1" "October 2026" "raven-cli v4.8.1.0" "User Commands" .SH NAME -raven-cli \- manual page for raven-cli v0.15.99.0 +raven-cli \- manual page for raven-cli v4.8.1.0 .SH DESCRIPTION -Raven Core RPC client version v0.15.99.0 +Raven Core RPC client version v4.8.1.0 .SS "Usage:" .TP raven\-cli [options] [params] @@ -25,6 +25,14 @@ Specify configuration file (default: raven.conf) \fB\-datadir=\fR .IP Specify data directory +.HP +\fB\-getinfo\fR +.IP +Get general information from the remote server. Note that unlike +server\-side RPC calls, the results of \fB\-getinfo\fR is the result of +multiple non\-atomic requests. Some entries in the result may +represent results from different states (e.g. wallet balance may +be as of a different block from the chain state reported) .PP Chain selection options: .HP @@ -85,14 +93,13 @@ input is used for the RPC password. \fB\-rpcwallet=\fR .IP Send RPC for non\-default wallet on RPC server (argument is wallet -filename in ravend directory, required if ravend/\-Qt runs -with multiple wallets) +filename in ravend directory, required if ravend/\-Qt runs with +multiple wallets) .SH COPYRIGHT -Copyright (C) 2009-2017 The Bitcoin Core developers -Copyright (c) 2017-2019 The Raven Core developers +Copyright (C) 2009-2022 The Raven Core developers -Please contribute if you find Raven Core useful. Visit - for further information about the software. +Please contribute if you find Raven Core useful. Visit +for further information about the software. The source code is available from . This is experimental software. diff --git a/doc/man/raven-qt.1 b/doc/man/raven-qt.1 index 035d12efec..0ac69f95e1 100644 --- a/doc/man/raven-qt.1 +++ b/doc/man/raven-qt.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH RAVEN-QT "1" "September 2017" "raven-qt v0.15.99.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. +.TH RAVEN-QT "1" "October 2026" "raven-qt v4.8.1.0" "User Commands" .SH NAME -raven-qt \- manual page for raven-qt v0.15.99.0 +raven-qt \- manual page for raven-qt v4.8.1.0 .SH DESCRIPTION -Raven Core version v0.15.99.0 (64\-bit) +Raven Core version v4.8.1.0 (64\-bit) Usage: .IP raven\-qt [command\-line options] @@ -32,9 +32,9 @@ block hash) If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: -0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a, +0000000000018d2fdcf4ac8eaac8db059584bd2840be5629562bb8599d39998c, testnet: -0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) +000000006272208605c4df3b54d4d5515759105e7ffcb258e8cd8077924ffef1) .HP \fB\-conf=\fR .IP @@ -48,10 +48,29 @@ Specify data directory .IP Set database cache size in megabytes (4 to 16384, default: 450) .HP +\fB\-disablemessaging\fR +.IP +Turn off the databasing the messages sent with assets (default: 0) +.HP \fB\-loadblock=\fR .IP Imports blocks from external blk000??.dat file on startup .HP +\fB\-maxreorg=\fR +.IP +Set the Maximum reorg depth (default: 60) +.HP +\fB\-minreorgpeers=\fR +.IP +Set the Minimum amount of peers required to disallow reorg of chains of +depth >= maxreorg. Peers must be greater than. (default: 4) +.HP +\fB\-minreorgage=\fR +.IP +Set the Minimum tip age (in seconds) required to allow reorg of a chain +of depth >= maxreorg on a node with more than minreorgpeers +peers. (default: 43200) +.HP \fB\-maxorphantx=\fR .IP Keep at most unconnectable transactions in memory (default: 100) @@ -76,9 +95,19 @@ Extra transactions to keep in memory for compact block reconstructions .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-2\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP +\fB\-autofixmempool\fR +.IP +When set, if the CreateNewBlock fails because of a transaction. The +mempool will be cleared. (default: 0) +.HP +\fB\-bypassdownload\fR +.IP +When set, if the chain is in initialblockdownload the getblocktemplate +rpc call will still return block data (default: 0) +.HP \fB\-pid=\fR .IP Specify pid file (default: ravend.pid) @@ -112,12 +141,33 @@ Create new files with system default permissions, instead of umask 077 .IP Maintain a full transaction index, used by the getrawtransaction rpc call (default: 0) +.HP +\fB\-assetindex\fR +.IP +Keep an index of assets, used by the requestsnapshot rpc call. Requires +a \fB\-reindex\fR. +.HP +\fB\-addressindex\fR +.IP +Maintain a full address index, used to query for the balance, txids and +unspent outputs for addresses (default: 0) +.HP +\fB\-timestampindex\fR +.IP +Maintain a timestamp index for block hashes, used to query blocks hashes +by a range of timestamps (default: 0) +.HP +\fB\-spentindex\fR +.IP +Maintain a full spent index, used to query the spending txid and input +index for an outpoint (default: 0) .PP Connection options: .HP \fB\-addnode=\fR .IP -Add a node to connect to and attempt to keep the connection open +Add a node to connect to and attempt to keep the connection open (see +the `addnode` RPC command help for more info) .HP \fB\-banscore=\fR .IP @@ -136,7 +186,8 @@ for IPv6 \fB\-connect=\fR .IP Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic -connections +connections (the rules for this peer are the same as for +\fB\-addnode\fR) .HP \fB\-discover\fR .IP @@ -206,7 +257,7 @@ Support filtering of blocks and transaction with bloom filters (default: .HP \fB\-port=\fR .IP -Listen for connections on (default: 8767 or testnet: 18767) +Listen for connections on (default: 8767 or testnet: 18770) .HP \fB\-proxy=\fR .IP @@ -258,31 +309,53 @@ Tries to keep outbound traffic under the given target (in MiB per 24h), .PP Wallet options: .HP -\fB\-disablewallet\fR +\fB\-bip44=\fR .IP -Do not load the wallet and disable wallet RPC calls +Sets the wallet to use/not use bip44 12\-words, non\-bip44=0 or bip44=1 +(default: 1). Note: By default 12\-words will automatically be +generated for you (random word selection). See \fB\-mnemonic\fR and +\fB\-mnemonicpassphrase\fR below to create a wallet using a specific +word list (use an existing bip\-44 wallet word\-list), or use the +RPC/CLI getmywords or dumpwallet to retrieve the auto\-generated +word\-list. This flag is ignored if there is already an existing +non\-bip44 wallet. .HP -\fB\-keypool=\fR +\fB\-discardfee=\fR .IP -Set key pool size to (default: 1000) +The fee rate (in RVN/kB) that indicates your tolerance for discarding +change by adding it to the fee (default: 0.00025). Note: An +output is discarded if it is dust at this rate, but we will +always discard up to the dust relay fee and a discard fee above +that is limited by the fee estimate for the longest target +.HP +\fB\-disablewallet\fR +.IP +Do not load the wallet and disable wallet RPC calls .HP \fB\-fallbackfee=\fR .IP A fee rate (in RVN/kB) that will be used when fee estimation has -insufficient data (default: 0.0002) +insufficient data (default: 0.01025) .HP -\fB\-discardfee=\fR +\fB\-keypool=\fR .IP -The fee rate (in RVN/kB) that indicates your tolerance for discarding -change by adding it to the fee (default: 0.0001). Note: An output -is discarded if it is dust at this rate, but we will always -discard up to the dust relay fee and a discard fee above that is -limited by the fee estimate for the longest target +Set key pool size to (default: 1000) .HP \fB\-mintxfee=\fR .IP Fees (in RVN/kB) smaller than this are considered zero fee for -transaction creation (default: 0.00001) +transaction creation (default: 0.01) +.HP +\fB\-mnemonic=\fR +.IP +A space separated list of 12 words used to import a BIP44 wallet. +Command\-line arguments can be visible to other local users and +process tools. +.HP +\fB\-mnemonicpassphrase=\fR +.IP +Passphrase securing your 12\-word mnemonic word list. Command\-line +arguments and configuration files can retain plaintext secrets. .HP \fB\-paytxfee=\fR .IP @@ -305,14 +378,14 @@ Spend unconfirmed change when sending transactions (default: 1) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: 6) .HP -\fB\-walletrbf\fR -.IP -Send transactions with full\-RBF opt\-in enabled (default: 0) -.HP \fB\-upgradewallet\fR .IP Upgrade wallet to latest format on startup .HP +\fB\-walletrbf\fR +.IP +Send transactions with full\-RBF opt\-in enabled (default: 0) +.HP \fB\-wallet=\fR .IP Specify wallet file (within data directory) (default: wallet.dat) @@ -332,6 +405,16 @@ Delete all wallet transactions and only recover those parts of the blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) +.HP +\fB\-miningaddress=\fR
+.IP +When getblocktemplate is called. It will create the coinbase transaction +using this address(default: empty string) +.HP +\fB\-minrewardheight=\fR +.IP +The default height that is required before rewards are allowed to be +sent out (default: 60) .PP ZeroMQ notification options: .HP @@ -350,6 +433,10 @@ Enable publish raw block in
\fB\-zmqpubrawtx=\fR
.IP Enable publish raw transaction in
+.HP +\fB\-zmqpubrawmessage=\fR
+.IP +Enable publish raw asset messages in
.PP Debugging/Testing options: .HP @@ -364,7 +451,7 @@ optional). If is not supplied or if = 1, output all debugging information. can be: net, tor, mempool, http, bench, zmq, db, rpc, estimatefee, addrman, selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej, -libevent, coindb, qt, leveldb. +libevent, coindb, qt, leveldb, rewards. .HP \fB\-debugexclude=\fR .IP @@ -388,7 +475,7 @@ Prepend debug output with timestamp (default: 1) .IP Maximum total fees (in RVN) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions -(default: 0.10) +(default: 1000.00) .HP \fB\-printtoconsole\fR .IP @@ -422,12 +509,12 @@ Maximum size of data in data carrier transactions we relay and mine .HP \fB\-mempoolreplacement\fR .IP -Enable transaction replacement in the memory pool (default: 1) +Enable transaction replacement in the memory pool (default: 0) .HP \fB\-minrelaytxfee=\fR .IP Fees (in RVN/kB) smaller than this are considered zero fee for relaying, -mining and transaction creation (default: 0.00001) +mining and transaction creation (default: 0.01) .HP \fB\-whitelistrelay\fR .IP @@ -542,11 +629,10 @@ Show splash screen on startup (default: 1) .IP Reset all settings changed in the GUI .SH COPYRIGHT -Copyright (C) 2009-2017 The Bitcoin Core developers -Copyright (c) 2017-2019 The Raven Core developers +Copyright (C) 2009-2022 The Raven Core developers -Please contribute if you find Raven Core useful. Visit - for further information about the software. +Please contribute if you find Raven Core useful. Visit +for further information about the software. The source code is available from . This is experimental software. diff --git a/doc/man/raven-tx.1 b/doc/man/raven-tx.1 index fedd023110..22196be205 100644 --- a/doc/man/raven-tx.1 +++ b/doc/man/raven-tx.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH RAVEN-TX "1" "September 2017" "raven-tx v0.15.99.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. +.TH RAVEN-TX "1" "October 2026" "raven-tx v4.8.1.0" "User Commands" .SH NAME -raven-tx \- manual page for raven-tx v0.15.99.0 +raven-tx \- manual page for raven-tx v4.8.1.0 .SH DESCRIPTION -Raven Core raven\-tx utility version v0.15.99.0 +Raven Core raven\-tx utility version v4.8.1.0 .SS "Usage:" .TP raven\-tx [options] [commands] @@ -112,11 +112,10 @@ set=NAME:JSON\-STRING .IP Set register NAME to given JSON\-STRING .SH COPYRIGHT -Copyright (C) 2009-2017 The Bitcoin Core developers -Copyright (c) 2017-2019 The Raven Core developers +Copyright (C) 2009-2022 The Raven Core developers -Please contribute if you find Raven Core useful. Visit - for further information about the software. +Please contribute if you find Raven Core useful. Visit +for further information about the software. The source code is available from . This is experimental software. diff --git a/doc/man/ravend.1 b/doc/man/ravend.1 index a0507a9147..773ee77a5e 100644 --- a/doc/man/ravend.1 +++ b/doc/man/ravend.1 @@ -1,9 +1,9 @@ -.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.47.4. -.TH RAVEND "1" "September 2017" "ravend v0.15.99.0" "User Commands" +.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.49.3. +.TH RAVEND "1" "October 2026" "ravend v4.8.1.0" "User Commands" .SH NAME -ravend \- manual page for ravend v0.15.99.0 +ravend \- manual page for ravend v4.8.1.0 .SH DESCRIPTION -Raven Core Daemon version v0.15.99.0 +Raven Core Daemon version v4.8.1.0 .SS "Usage:" .TP ravend [options] @@ -33,9 +33,9 @@ block hash) If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: -0000000000000000003b9ce759c2a087d52abc4266f8f4ebd6d768b89defa50a, +0000000000018d2fdcf4ac8eaac8db059584bd2840be5629562bb8599d39998c, testnet: -0000000002e9e7b00e1f6dc5123a04aad68dd0f0968d8c7aa45f6640795c37b1) +000000006272208605c4df3b54d4d5515759105e7ffcb258e8cd8077924ffef1) .HP \fB\-conf=\fR .IP @@ -53,10 +53,29 @@ Specify data directory .IP Set database cache size in megabytes (4 to 16384, default: 450) .HP +\fB\-disablemessaging\fR +.IP +Turn off the databasing the messages sent with assets (default: 0) +.HP \fB\-loadblock=\fR .IP Imports blocks from external blk000??.dat file on startup .HP +\fB\-maxreorg=\fR +.IP +Set the Maximum reorg depth (default: 60) +.HP +\fB\-minreorgpeers=\fR +.IP +Set the Minimum amount of peers required to disallow reorg of chains of +depth >= maxreorg. Peers must be greater than. (default: 4) +.HP +\fB\-minreorgage=\fR +.IP +Set the Minimum tip age (in seconds) required to allow reorg of a chain +of depth >= maxreorg on a node with more than minreorgpeers +peers. (default: 43200) +.HP \fB\-maxorphantx=\fR .IP Keep at most unconnectable transactions in memory (default: 100) @@ -81,9 +100,19 @@ Extra transactions to keep in memory for compact block reconstructions .HP \fB\-par=\fR .IP -Set the number of script verification threads (\fB\-2\fR to 16, 0 = auto, <0 = +Set the number of script verification threads (\fB\-8\fR to 16, 0 = auto, <0 = leave that many cores free, default: 0) .HP +\fB\-autofixmempool\fR +.IP +When set, if the CreateNewBlock fails because of a transaction. The +mempool will be cleared. (default: 0) +.HP +\fB\-bypassdownload\fR +.IP +When set, if the chain is in initialblockdownload the getblocktemplate +rpc call will still return block data (default: 0) +.HP \fB\-pid=\fR .IP Specify pid file (default: ravend.pid) @@ -117,12 +146,33 @@ Create new files with system default permissions, instead of umask 077 .IP Maintain a full transaction index, used by the getrawtransaction rpc call (default: 0) +.HP +\fB\-assetindex\fR +.IP +Keep an index of assets, used by the requestsnapshot rpc call. Requires +a \fB\-reindex\fR. +.HP +\fB\-addressindex\fR +.IP +Maintain a full address index, used to query for the balance, txids and +unspent outputs for addresses (default: 0) +.HP +\fB\-timestampindex\fR +.IP +Maintain a timestamp index for block hashes, used to query blocks hashes +by a range of timestamps (default: 0) +.HP +\fB\-spentindex\fR +.IP +Maintain a full spent index, used to query the spending txid and input +index for an outpoint (default: 0) .PP Connection options: .HP \fB\-addnode=\fR .IP -Add a node to connect to and attempt to keep the connection open +Add a node to connect to and attempt to keep the connection open (see +the `addnode` RPC command help for more info) .HP \fB\-banscore=\fR .IP @@ -141,7 +191,8 @@ for IPv6 \fB\-connect=\fR .IP Connect only to the specified node(s); \fB\-connect\fR=\fI\,0\/\fR disables automatic -connections +connections (the rules for this peer are the same as for +\fB\-addnode\fR) .HP \fB\-discover\fR .IP @@ -211,7 +262,7 @@ Support filtering of blocks and transaction with bloom filters (default: .HP \fB\-port=\fR .IP -Listen for connections on (default: 8767 or testnet: 18767) +Listen for connections on (default: 8767 or testnet: 18770) .HP \fB\-proxy=\fR .IP @@ -263,31 +314,53 @@ Tries to keep outbound traffic under the given target (in MiB per 24h), .PP Wallet options: .HP -\fB\-disablewallet\fR +\fB\-bip44=\fR .IP -Do not load the wallet and disable wallet RPC calls +Sets the wallet to use/not use bip44 12\-words, non\-bip44=0 or bip44=1 +(default: 1). Note: By default 12\-words will automatically be +generated for you (random word selection). See \fB\-mnemonic\fR and +\fB\-mnemonicpassphrase\fR below to create a wallet using a specific +word list (use an existing bip\-44 wallet word\-list), or use the +RPC/CLI getmywords or dumpwallet to retrieve the auto\-generated +word\-list. This flag is ignored if there is already an existing +non\-bip44 wallet. .HP -\fB\-keypool=\fR +\fB\-discardfee=\fR .IP -Set key pool size to (default: 1000) +The fee rate (in RVN/kB) that indicates your tolerance for discarding +change by adding it to the fee (default: 0.00025). Note: An +output is discarded if it is dust at this rate, but we will +always discard up to the dust relay fee and a discard fee above +that is limited by the fee estimate for the longest target +.HP +\fB\-disablewallet\fR +.IP +Do not load the wallet and disable wallet RPC calls .HP \fB\-fallbackfee=\fR .IP A fee rate (in RVN/kB) that will be used when fee estimation has -insufficient data (default: 0.0002) +insufficient data (default: 0.01025) .HP -\fB\-discardfee=\fR +\fB\-keypool=\fR .IP -The fee rate (in RVN/kB) that indicates your tolerance for discarding -change by adding it to the fee (default: 0.0001). Note: An output -is discarded if it is dust at this rate, but we will always -discard up to the dust relay fee and a discard fee above that is -limited by the fee estimate for the longest target +Set key pool size to (default: 1000) .HP \fB\-mintxfee=\fR .IP Fees (in RVN/kB) smaller than this are considered zero fee for -transaction creation (default: 0.00001) +transaction creation (default: 0.01) +.HP +\fB\-mnemonic=\fR +.IP +A space separated list of 12 words used to import a BIP44 wallet. +Command\-line arguments can be visible to other local users and +process tools. +.HP +\fB\-mnemonicpassphrase=\fR +.IP +Passphrase securing your 12\-word mnemonic word list. Command\-line +arguments and configuration files can retain plaintext secrets. .HP \fB\-paytxfee=\fR .IP @@ -310,14 +383,14 @@ Spend unconfirmed change when sending transactions (default: 1) If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: 6) .HP -\fB\-walletrbf\fR -.IP -Send transactions with full\-RBF opt\-in enabled (default: 0) -.HP \fB\-upgradewallet\fR .IP Upgrade wallet to latest format on startup .HP +\fB\-walletrbf\fR +.IP +Send transactions with full\-RBF opt\-in enabled (default: 0) +.HP \fB\-wallet=\fR .IP Specify wallet file (within data directory) (default: wallet.dat) @@ -337,6 +410,16 @@ Delete all wallet transactions and only recover those parts of the blockchain through \fB\-rescan\fR on startup (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data) +.HP +\fB\-miningaddress=\fR
+.IP +When getblocktemplate is called. It will create the coinbase transaction +using this address(default: empty string) +.HP +\fB\-minrewardheight=\fR +.IP +The default height that is required before rewards are allowed to be +sent out (default: 60) .PP ZeroMQ notification options: .HP @@ -355,6 +438,10 @@ Enable publish raw block in
\fB\-zmqpubrawtx=\fR
.IP Enable publish raw transaction in
+.HP +\fB\-zmqpubrawmessage=\fR
+.IP +Enable publish raw asset messages in
.PP Debugging/Testing options: .HP @@ -369,7 +456,7 @@ optional). If is not supplied or if = 1, output all debugging information. can be: net, tor, mempool, http, bench, zmq, db, rpc, estimatefee, addrman, selectcoins, reindex, cmpctblock, rand, prune, proxy, mempoolrej, -libevent, coindb, qt, leveldb. +libevent, coindb, qt, leveldb, rewards. .HP \fB\-debugexclude=\fR .IP @@ -393,7 +480,7 @@ Prepend debug output with timestamp (default: 1) .IP Maximum total fees (in RVN) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions -(default: 0.10) +(default: 1000.00) .HP \fB\-printtoconsole\fR .IP @@ -427,12 +514,12 @@ Maximum size of data in data carrier transactions we relay and mine .HP \fB\-mempoolreplacement\fR .IP -Enable transaction replacement in the memory pool (default: 1) +Enable transaction replacement in the memory pool (default: 0) .HP \fB\-minrelaytxfee=\fR .IP Fees (in RVN/kB) smaller than this are considered zero fee for relaying, -mining and transaction creation (default: 0.00001) +mining and transaction creation (default: 0.01) .HP \fB\-whitelistrelay\fR .IP @@ -521,11 +608,10 @@ non\-verbose mode, non\-segwit(0) or segwit(1) (default: 1) .IP Set the number of threads to service RPC calls (default: 4) .SH COPYRIGHT -Copyright (C) 2009-2017 The Bitcoin Core developers -Copyright (c) 2017-2019 The Raven Core developers +Copyright (C) 2009-2022 The Raven Core developers -Please contribute if you find Raven Core useful. Visit - for further information about the software. +Please contribute if you find Raven Core useful. Visit +for further information about the software. The source code is available from . This is experimental software. diff --git a/doc/release-notes/release-notes-4.8.1.md b/doc/release-notes/release-notes-4.8.1.md new file mode 100644 index 0000000000..ffbef91644 --- /dev/null +++ b/doc/release-notes/release-notes-4.8.1.md @@ -0,0 +1,169 @@ +# Raven Core 4.8.1 release notes (draft) + +Status: release qualification in progress. These notes are for review and +must be updated with the final source SHA, artifact checksums, and release +date before publication. + +## Upgrade before bit 12 activates + +RIP-25 adds ML-DSA-44 signatures for native RVN witness-v2 outputs. The +same activation changes the block limit from 8 to 12 million weight units +(MWU) and applies a contextual discount to PQ witness data. Core 4.8.0 +can reject blocks accepted by upgraded nodes after activation. This is an +incompatible consensus upgrade. Miners, pools, full nodes, exchanges, +custodians, explorers, and other validating services must install a +qualified 4.8.1 release before bit 12 becomes ACTIVE. A miner's signal +does not upgrade any other validator. + +Back up wallets, stop the old node cleanly, install the release artifact +for the platform, verify its published checksum, and allow the upgraded +node to synchronize. Before relying on it for validation, confirm the +running version and inspect `getblockchaininfo.bip9_softforks.pq_hybrid`. +Do not downgrade a validator to Core 4.8.0 after bit 12 is ACTIVE: it may +follow a different chain once a newly allowed block appears. + +## Mainnet signaling and block limits + +| Rule | Value | +|------|-------| +| Bit 12 STARTED threshold | Median time past reaches 2027-07-01 00:00 UTC (1814400000) at a window boundary | +| Timeout | 2029-01-01 00:00 UTC (1861920000), 18 months after start | +| Signaling threshold | At least 1714 of 2016 blocks in one STARTED window | +| Lock-in | One complete LOCKED_IN window follows a successful signaling window | +| Activation | ACTIVE begins at the following window boundary, at height `H12` | +| Block limit at `H12` | 12 MWU, with the contextual PQ witness discount | +| PQ sigop cost at `H12` | 2 units per active native or P2SH-wrapped witness-v2 input; 80,000-unit block cap unchanged | +| Later block limit | 16 MWU at `H12 + 525600` blocks, one nominal year at the 60-second target spacing | + +The start timestamp is not the date when the new rules take effect. BIP9 +uses chain median time past and full 2016-block windows. If the timeout +is reached before lock-in, bit 12 fails and these rules do not activate +through this deployment. The 16 MWU phase is height-based, not tied to +a calendar date. There is no automatic return to Core 4.8.0 validation +rules after activation. + +The PQ sigop cost of two is a consensus change from the initial RIP-25 +value of one. It is fixed across platforms and begins with bit-12 ACTIVE. +Both miner templates and block validation count native and P2SH-wrapped +witness-v2 inputs at this cost. The change follows portable-backend +verification measurements and exact-limit regression tests. + +On mainnet, upgraded 4.8.1 `getblocktemplate` mining templates +automatically set bit 12 and signal during STARTED and LOCKED_IN. +`CreateNewBlock` uses `ComputeBlockVersion`, and `pq_hybrid` is a forced +getblocktemplate version bit. A pool can therefore signal as soon as its +upgraded node reaches STARTED, even if other validators and services are +still running Core 4.8.0. The `-blockversion` override is regtest-only; +it cannot disable bit 12 in mainnet 4.8.1 templates. Pools should check +the returned template version and coordinate their upgrade with the +validators and services that depend on their blocks. + +Testnet differs from this mainnet schedule. Its native RIP-25 rule is +force-enabled from genesis by `nPQHybridEnabled=true`. At the reviewed +2026-10-15 testnet tip, `getblocktemplate` therefore advertises the later +16 MWU limit, even while the raw bit-12 BIP9 state reads STARTED. This +does not imply that mainnet has activated bit 12. The testnet override +cannot demonstrate the public 8-to-12-MWU BIP9 transition; controlled +regtest boundary tests cover that transition. Testnet miners and node +operators should account for the already active PQ and block-weight +rules when comparing with Core 4.8.0. + +Before the original bit-12 STARTED window, the release owner must record +an evidence-based go/no-go decision. The evidence includes a green +`make check` on the final 4.8.1 SHA, paired green Build Raven and Final +Gate runs on that SHA, checksum and provenance review of all five +packages, and confirmed readiness of miners, pools, validators, and +dependent services. The clean candidate has already replayed the +available public testnet history from genesis, and both old and new +binaries have been tested on both sides of the 8 MWU boundary. That +history does not replace the final qualification or operator readiness +decision. Operators must have time to upgrade before ACTIVE. + +If the decision is no-go or readiness is absent, a replacement release +must revise both the start and timeout dates and pass the full release +qualification again. The replacement release with revised parameters +must be installed across miners, pools, validators, and dependent +services before the original STARTED window begins. Merely preparing +revised parameters, or relying on `-blockversion` on mainnet, does not +prevent an already deployed 4.8.1 miner from signaling on the original +schedule. + +## Migration to PQ custody + +Native RVN in historical classical outputs remains spendable with the +existing signature. Owners migrate voluntarily by generating a witness-v2 +PQ address with `getnewpqaddress` and sending RVN to it. Its later spend +uses ML-DSA-44 without an ECDSA signature. Existing classical outputs +are not converted by activation. + +The separate bit-13 mainnet deployment is scheduled to start no earlier +than 2029-01-01 00:00 UTC by median time past, with a 1714/2016 threshold +and 2030-07-01 timeout. It cannot enforce its PQ-only asset rule until +bit 12 and the transfer-script deployment on bit 8 are ACTIVE. Once all +three deployments are ACTIVE, newly created asset and owner-token outputs +use the PQ asset format: spending requires a matching native RVN +witness-v2 input signed with ML-DSA-44 and no classical asset signature. +Historical asset and owner-token outputs retain their classical spending +condition until their owners move them to new protected outputs. Activation +does not retroactively protect them. If bit 12 fails or activates too late, +the bit-13 window must be reviewed before it starts. + +There is no automatic network-wide point at which only PQ keys can spend +all existing holdings. Each owner reaches PQ-only custody after moving every +historical RVN, asset, and owner-token UTXO they hold. Unmigrated historical +outputs remain spendable with their original classical keys. + +For the asset format, wallet recovery routes, and current limitations in +automatic anchor refresh and raw transaction funding, see +[RIP-25 PQ Asset Extension Design Note](../RIP-0025-PQ-Assets-Followup.md). + +## Known asset limitations + +An asset PQ anchor is native RVN at the protected output's matching PQ +address. Some asset spends and ordinary RVN payments consume the last +anchor without creating a replacement. Exact-fee, dust, multiple-program, +and explicit native-change cases need the owner to fund the matching PQ +address again before the next protected asset spend. The asset output +remains unspent; consensus rejects a spend without its anchor. + +`fundrawtransaction` does not discover or select anchors for protected +asset inputs. Raw transaction callers must supply matching funded PQ +inputs and check the final transaction before broadcast. + +## Node-only GUI + +The Qt GUI now starts with `-disablewallet` without accessing the +wallet-only currency selector. Its Options dialog can also submit +the Icons only setting without accessing absent wallet toolbar controls. +Both crashes were inherited from Core 4.8.0. The Linux release build +runs wallet-disabled GUI startup, Options interaction, and RPC +shutdown checks. + +## Release qualification still required + +The available testnet active chain was replayed from genesis by a clean +4.8.1 candidate with `-assumevalid=0`, then synchronized with public +peers through height 2,554,504. The exact snapshots and limits are in the +remediation register and private audit evidence. +The scanned chain contained no native witness-v2 outputs or spends, so +the replay checks historical compatibility rather than an on-chain PQ +signature. It does not test unknown side branches or future bit-13 +activation. +The scoped activation and side-branch defects FINDING-104 through +FINDING-107 are fixed after H-2 through H+2 regressions, connected +alternate branches, rollback and restart checks, and a green exact-SHA +Build Raven and Final Gate pair on `0c33cc92c`. All five packages from +that run passed downloaded checksum and provenance review. The +pinned xproto and libXau source-availability fixes passed both +workflows on `35fa04a40`; all five downloaded packages passed +provenance and checksum review. The owner accepted a documented +cause-not-reconstructible disposition for two historical intermittent +wallet recovery test failures (FINDING-112); a renewed failure blocks +release, and `make check` remains mandatory on the final SHA. +The wallet-disabled Qt corrections (FINDING-177 and FINDING-178) +require passing unmodified Linux package smokes on the final SHA. +Operator readiness, manual GUI checks on release operating systems, +final checksums, and signatures +still require qualification. +The release will be tagged and published only after those checks and +the owner's separate release approval. diff --git a/doc/release-process.md b/doc/release-process.md index ed99c67a4a..6f2708d3f8 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -25,6 +25,32 @@ Before every major release: * Update [`BLOCK_CHAIN_SIZE`](/src/qt/intro.cpp) to the current size plus some overhead. * Update `src/chainparams.cpp` chainTxData with statistics about the transaction count and rate. +### Core 4.8.1 pre-tag qualification + +For the RIP-25 4.8.1 release, the tag instruction is step 5 below. +Complete this sequence before signing or publishing. Keep the evidence +tied to one full final source SHA. Any source change creates a new +candidate and restarts these gates. + +1. Freeze the final source SHA after all code and documentation edits. + Run `python3 contrib/devtools/test-rip25-release-contract.py` and the + mandatory `make check` on that final SHA; record green results. +2. Run Build Raven and RIP-25 v4.8 Final Gate on the same final SHA. + Both workflows must be green, and their reported checkout SHAs must + match the source SHA from step 1. +3. Download and review artifacts from all five Build Raven targets: + windows, osx, linux, arm32v7, and aarch64. Check each package against + its SHA256 checksum and review its provenance, including the recorded + source SHA. Record the five target results and artifact names before + proceeding. +4. Present the recorded qualification and operator readiness evidence to + the owner. Obtain a separate, explicit owner approval to create the + 4.8.1 tag. The rollout go/no-go decision and permission to publish are + separate decisions. +5. Create the 4.8.1 tag on the exact qualified final SHA. Verify the tag + resolves to that SHA before signing or publishing release material. + Publication requires its own owner decision. + ### First time / New builders @@ -40,7 +66,7 @@ Generate list of authors: git log --format='%aN' "$*" | sort -ui | sed -e 's/^/- /' -Tag version (or release candidate) in git +For releases other than 4.8.1, tag the version (or release candidate) in git: git tag -s v(new version, e.g. 0.8.0) diff --git a/src/Makefile.am b/src/Makefile.am index bd9bdd9c7a..0957c63197 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -529,6 +529,17 @@ raven_cli_LDADD += $(BOOST_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS) $(LIBO # # raven-tx binary # +# Keep the interdependent transaction, asset, and wallet objects in one archive. +EXTRA_LIBRARIES += libraven_tx_core.a +libraven_tx_core_a_SOURCES = +libraven_tx_core_a_LIBADD = \ + $(libraven_common_a_OBJECTS) \ + $(libraven_server_a_OBJECTS) + +if ENABLE_WALLET +libraven_tx_core_a_LIBADD += $(libraven_wallet_a_OBJECTS) +endif + raven_tx_SOURCES = raven-tx.cpp raven_tx_CPPFLAGS = $(AM_CPPFLAGS) $(RAVEN_INCLUDES) raven_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -539,14 +550,18 @@ raven_tx_SOURCES += raven-tx-res.rc endif raven_tx_LDADD = \ + libraven_tx_core.a \ $(LIBUNIVALUE) \ - $(LIBRAVEN_COMMON) \ $(LIBRAVEN_UTIL) \ + $(LIBRAVEN_ZMQ) \ $(LIBRAVEN_CONSENSUS) \ $(LIBRAVEN_CRYPTO) \ + $(LIBLEVELDB) \ + $(LIBLEVELDB_SSE42) \ + $(LIBMEMENV) \ $(LIBSECP256K1) -raven_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(LIBOQS_LIBS) +raven_tx_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(SSL_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS) $(LIBOQS_LIBS) # # ravenconsensus library # diff --git a/src/assets/assets.cpp b/src/assets/assets.cpp index d953306159..5feadf3426 100644 --- a/src/assets/assets.cpp +++ b/src/assets/assets.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include "assets.h" @@ -3631,14 +3632,17 @@ bool GetAssetData(const CScript& script, CAssetOutputEntry& data, const TxAssetD bool GetPQAssetProgram(const CScript& script, uint256& program) { - // Canonical parser for the dependent PQ asset rule. Keep the classical - // envelope intact so legacy nodes see the same asset. - if (script.size() < 26 + 1 + 4 + 32 || - script[0] != OP_DUP || script[1] != OP_HASH160 || script[2] != 20 || - script[23] != OP_EQUALVERIFY || script[24] != OP_CHECKSIG || - script[25] != OP_RVN_ASSET) { + if (script.size() < 26 + 1 + 4 + 32 || script[25] != OP_RVN_ASSET) + return false; + + const bool classicalPrefix = + script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && + script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG; + const bool pqOnlyPrefix = + script[0] == OP_1 && script[1] == OP_1 && script[2] == 20 && + script[23] == OP_DROP && script[24] == OP_DROP; + if (!classicalPrefix && !pqOnlyPrefix) return false; - } size_t payloadStart = 27; size_t payloadSize = script[26]; @@ -3704,9 +3708,20 @@ bool GetPQAssetProgram(const CScript& script, uint256& program) return false; std::copy(script.end() - 32, script.end(), program.begin()); + if (pqOnlyPrefix) { + const uint160 addressHash = Hash160(program.begin(), program.end()); + if (!std::equal(addressHash.begin(), addressHash.end(), script.begin() + 3)) + return false; + } return true; } +bool IsPQOnlyAssetScript(const CScript& script, uint256& program) +{ + return script.size() >= 26 && script[0] == OP_1 && + GetPQAssetProgram(script, program); +} + std::string EncodeContextualAssetDestination(const CScript& script, int originHeight, int pqAssetActivationHeight) { @@ -3824,7 +3839,47 @@ bool BuildPQAssetTaggedScript(const CScript& legacyScript, const uint256& progra return true; } +bool BuildPQOnlyAssetScript(const CScript& legacyScript, const uint256& program, CScript& pqOnlyScript) +{ + CScript tagged; + if (!BuildPQAssetTaggedScript(legacyScript, program, tagged)) + return false; + + const uint160 addressHash = Hash160(program.begin(), program.end()); + CScript candidate = CScript() << OP_1 << OP_1 + << ToByteVector(addressHash) << OP_DROP << OP_DROP; + if (candidate.size() != 25) + return false; + candidate.insert(candidate.end(), tagged.begin() + 25, tagged.end()); + + uint256 parsedProgram; + if (!GetPQAssetProgram(candidate, parsedProgram) || parsedProgram != program) + return false; + pqOnlyScript = std::move(candidate); + return true; +} + #ifdef ENABLE_WALLET +isminetype GetRestrictedAddressOwnership(const CWallet& wallet, const std::string& address) +{ + const CTxDestination destination = DecodeDestination(address); + const isminetype mine = IsMine(wallet, destination); + if (mine == ISMINE_SPENDABLE) + return mine; + + const CKeyID* assetID = boost::get(&destination); + if (!assetID) + return mine; + for (const std::string& descriptor : wallet.GetOwnedPQAssetDestinations()) { + CKeyID ownedID; + uint256 program; + if (DecodePQOnlyAssetDestination(descriptor, ownedID, program) && + ownedID == *assetID) + return ISMINE_SPENDABLE; + } + return mine; +} + void GetAllAdministrativeAssets(CWallet *pwallet, std::vector &names, int nMinConf) { if(!pwallet) @@ -4266,9 +4321,9 @@ bool CreateAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, const s if (pqAssetsActive) { CKeyID classicalKey; uint256 program; - if (!DecodePQAssetDestination(address, classicalKey, program)) { + if (!DecodePQOnlyAssetDestination(address, classicalKey, program)) { error = std::make_pair(RPC_INVALID_ADDRESS_OR_KEY, - "Active PQ asset issuance requires a canonical classical|PQ asset destination"); + "Active PQ asset issuance requires a canonical PQ-only asset destination"); return false; } assetDestination = classicalKey; @@ -4378,14 +4433,14 @@ bool CreateAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, const s CAssetTransfer assetTransfer(parentName + OWNER_TAG, OWNER_ASSET_AMOUNT); assetTransfer.ConstructTransaction(scriptTransferOwnerAsset); if (protectedAuthorityReturn) { - CScript tagged; - if (!BuildPQAssetTaggedScript(scriptTransferOwnerAsset, - protectedParentProgram, tagged)) { + CScript pqOnly; + if (!BuildPQOnlyAssetScript(scriptTransferOwnerAsset, + protectedParentProgram, pqOnly)) { error = std::make_pair(RPC_WALLET_ERROR, "Could not construct protected parent owner-token return"); return false; } - scriptTransferOwnerAsset = std::move(tagged); + scriptTransferOwnerAsset = std::move(pqOnly); } CRecipient rec = {scriptTransferOwnerAsset, 0, fSubtractFeeFromAmount}; vecSend.push_back(rec); @@ -4399,14 +4454,14 @@ bool CreateAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, const s CAssetTransfer assetTransfer(parentName, OWNER_ASSET_AMOUNT); assetTransfer.ConstructTransaction(scriptTransferQualifierAsset); if (protectedAuthorityReturn) { - CScript tagged; - if (!BuildPQAssetTaggedScript(scriptTransferQualifierAsset, - protectedParentProgram, tagged)) { + CScript pqOnly; + if (!BuildPQOnlyAssetScript(scriptTransferQualifierAsset, + protectedParentProgram, pqOnly)) { error = std::make_pair(RPC_WALLET_ERROR, "Could not construct protected parent qualifier return"); return false; } - scriptTransferQualifierAsset = std::move(tagged); + scriptTransferQualifierAsset = std::move(pqOnly); } CRecipient rec = {scriptTransferQualifierAsset, 0, fSubtractFeeFromAmount}; vecSend.push_back(rec); @@ -4447,14 +4502,14 @@ bool CreateAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, const s CAssetTransfer assetTransfer(strStripped + OWNER_TAG, OWNER_ASSET_AMOUNT); assetTransfer.ConstructTransaction(scriptTransferOwnerAsset); if (protectedAuthorityReturn) { - CScript tagged; - if (!BuildPQAssetTaggedScript(scriptTransferOwnerAsset, - protectedParentProgram, tagged)) { + CScript pqOnly; + if (!BuildPQOnlyAssetScript(scriptTransferOwnerAsset, + protectedParentProgram, pqOnly)) { error = std::make_pair(RPC_WALLET_ERROR, "Could not construct protected restricted owner-token return"); return false; } - scriptTransferOwnerAsset = std::move(tagged); + scriptTransferOwnerAsset = std::move(pqOnly); } CRecipient ownerRec = {scriptTransferOwnerAsset, 0, fSubtractFeeFromAmount}; @@ -4511,9 +4566,9 @@ bool CreateReissueAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, if (pqAssetsActive) { CKeyID classicalKey; uint256 program; - if (!DecodePQAssetDestination(address, classicalKey, program)) { + if (!DecodePQOnlyAssetDestination(address, classicalKey, program)) { error = std::make_pair(RPC_INVALID_ADDRESS_OR_KEY, - "Active PQ reissue requires a canonical classical|PQ asset destination"); + "Active PQ reissue requires a canonical PQ-only asset destination"); return false; } assetDestination = classicalKey; @@ -4629,14 +4684,14 @@ bool CreateReissueAssetTransaction(CWallet* pwallet, CCoinControl& coinControl, CAssetTransfer ownerTransfer(ownerName, OWNER_ASSET_AMOUNT); ownerTransfer.ConstructTransaction(scriptTransferOwnerAsset); if (pqAssetsActive) { - CScript tagged; - if (!BuildPQAssetTaggedScript(scriptTransferOwnerAsset, - ownerReturnProgram, tagged)) { + CScript pqOnly; + if (!BuildPQOnlyAssetScript(scriptTransferOwnerAsset, + ownerReturnProgram, pqOnly)) { error = std::make_pair(RPC_WALLET_ERROR, "Could not construct protected owner-token return"); return false; } - scriptTransferOwnerAsset = std::move(tagged); + scriptTransferOwnerAsset = std::move(pqOnly); } if (asset_type == AssetType::RESTRICTED) { @@ -4757,9 +4812,9 @@ bool CreateTransferAssetTransaction(CWallet* pwallet, const CCoinControl& coinCo uint256 pqProgram; if (pqAssetsActive) { CKeyID classicalKey; - if (!DecodePQAssetDestination(address, classicalKey, pqProgram)) { + if (!DecodePQOnlyAssetDestination(address, classicalKey, pqProgram)) { error = std::make_pair(RPC_INVALID_ADDRESS_OR_KEY, - "Active PQ asset transfer requires a canonical classical|PQ asset destination"); + "Active PQ asset transfer requires a canonical PQ-only asset destination"); return false; } assetDestination = classicalKey; @@ -4823,13 +4878,13 @@ bool CreateTransferAssetTransaction(CWallet* pwallet, const CCoinControl& coinCo CAssetTransfer assetTransfer(asset_name, nAmount, message, expireTime); assetTransfer.ConstructTransaction(scriptPubKey); if (pqAssetsActive) { - CScript tagged; - if (!BuildPQAssetTaggedScript(scriptPubKey, pqProgram, tagged)) { + CScript pqOnly; + if (!BuildPQOnlyAssetScript(scriptPubKey, pqProgram, pqOnly)) { error = std::make_pair(RPC_TRANSACTION_ERROR, "Could not construct a canonical PQ asset transfer output"); return false; } - scriptPubKey = std::move(tagged); + scriptPubKey = std::move(pqOnly); } CRecipient recipient = {scriptPubKey, 0, fSubtractFeeFromAmount}; @@ -5667,13 +5722,8 @@ bool ContextualCheckNullAssetTxOut(const CTxOut& txout, CAssetsCache* assetCache } } -#ifdef ENABLE_WALLET - if (myNullAssetData && vpwallets.size()) { - if (IsMine(*vpwallets[0], DecodeDestination(address)) & ISMINE_ALL) { - myNullAssetData->emplace_back(std::make_pair(address, data)); - } - } -#endif + if (myNullAssetData) + myNullAssetData->emplace_back(address, data); return true; } diff --git a/src/assets/assets.h b/src/assets/assets.h index f76ae3c323..40658d55a2 100644 --- a/src/assets/assets.h +++ b/src/assets/assets.h @@ -7,6 +7,7 @@ #define RAVENCOIN_ASSET_PROTOCOL_H #include "amount.h" +#include "script/ismine.h" #include "tinyformat.h" #include "assettypes.h" @@ -535,9 +536,9 @@ bool GetAssetInfoFromScript(const CScript& scriptPubKey, std::string& strName, C bool GetAssetData(const CScript& script, CAssetOutputEntry& data, const TxAssetDeploymentContext* pAssetContext = nullptr); -// Experimental PQ asset extension: recognize a canonical 32-byte program -// tail without changing the legacy asset parser or script evaluator. +// Recognize a canonical 32-byte program tail under either asset prefix. bool GetPQAssetProgram(const CScript& script, uint256& program); +bool IsPQOnlyAssetScript(const CScript& script, uint256& program); /** Encode an asset destination according to the output's protection at its origin height. * Pass -1 for originHeight when the output is not confirmed on the active chain. * An empty result means the output lacks the required active PQ tag. @@ -546,6 +547,8 @@ std::string EncodeContextualAssetDestination(const CScript& script, int originHeight, int pqAssetActivationHeight); /** Tag a canonical legacy P2PKH asset output for the dependent PQ asset rule. */ bool BuildPQAssetTaggedScript(const CScript& legacyScript, const uint256& program, CScript& taggedScript); +/** Replace legacy asset ownership with a PQ-only, anchor-protected prefix. */ +bool BuildPQOnlyAssetScript(const CScript& legacyScript, const uint256& program, CScript& pqOnlyScript); bool GetBestAssetAddressAmount(CAssetsCache& cache, const std::string& assetName, const std::string& address); @@ -558,6 +561,12 @@ std::string EncodeIPFS(std::string decoded); #ifdef ENABLE_WALLET +/** Ask wallet restricted asset views to reload after index recovery. */ +static constexpr int RESTRICTED_ASSET_VIEW_RELOAD = -2; + +/** Include wallet-owned PQ-only asset identifiers in tag and freeze history. */ +isminetype GetRestrictedAddressOwnership(const CWallet& wallet, const std::string& address); + /** Find a wallet-owned protected authority output for an active PQ asset return. */ bool GetWalletProtectedAssetReturnDescriptor(CWallet* pwallet, const std::string& authorityName, std::string& descriptor, std::pair& error); diff --git a/src/assets/messages.cpp b/src/assets/messages.cpp index 180e2ed401..3f93bd9f2d 100644 --- a/src/assets/messages.cpp +++ b/src/assets/messages.cpp @@ -5,26 +5,118 @@ #include #include #include +#include #include