Qanum: Test Suite Inventory

Test method and inventory of the reference implementation.

This page reproduces docs/TESTSUITE.md from qanum-core v0.9.0 (release tag). Links to files that are not published on this site are shown as plain text until the source repository is public.

Contents

Test inventory of qanum-core: what is tested, how it is counted and how to run it. The counts are checked against the source by tests/release_docs_stamps.rs, so this page cannot drift from the code.

Summary

Counting method. A test function is a #[test], #[tokio::test] or #[tokio::test(...)] attribute at the start of a line, immediately before the function. Commented attributes (// #[test]) are not counted. The same method gives the per-file numbers in the tables below.

The count is static, so a run can differ from it in two known ways: contract_rejects_overlapping_delta_in_debug (tests/utxo_root_delta_equivalence.rs) is compiled only with debug assertions, so a --release run executes one integration test fewer; and the unit tests of src/bin/qanum_conf/ and src/bin/common/ are compiled into both qanumd and qanum-cli, so they run twice.

Special suites

How to run

The dev-only dependencies (tempfile, proptest and the test-fixtures self-dependency) are active in Cargo.toml; they never enter the node binaries. crypto-oqs is a default feature; the crate refuses to build without it.

# Full suite (unit + integration + multi-node E2E)
cargo test --release --locked

# Everything except the multi-node E2E scenarios
cargo test --release --locked -- --skip e2e_

# Multi-node E2E scenarios (real nodes on loopback; sequential)
cargo test --release --locked --test net_v2_multinode_e2e -- --test-threads=1

# Library unit tests only
cargo test --release --locked --lib

# One integration file
cargo test --release --locked --test sync_scheduler_robustness -- --test-threads=1

# Manual scale benchmarks (need RAM; print their measurements)
cargo test --release --locked --test chainstate_scale_benchmark -- --ignored --nocapture --test-threads=1
cargo test --release --locked --test utxo_commitment_scale_benchmark -- --ignored --nocapture --test-threads=1

# Embedded node crate (workspace member, outside the main count)
cargo test --release --locked -p qanum-embed -- --test-threads=1

Suites that start daemons, bind sockets or share the process-wide caches (block store, signature cache) are run with --test-threads=1, as in CI.


Unit tests (src/: 90 source files, 812 tests)

Exhaustive per-file list, counted with the method in the Summary. Paths are relative to src/.

Module Tests What they cover
address_index.rs 2 Coinbase indexing, event dedup and torn-tail recovery
assume_valid.rs 2 -assumevalid argument parsing (0 disables, garbage rejected); a multibyte hash input does not panic
bin/common/parse.rs 3 Property-fuzzed amount parser (QAN/jomas, <- Core parse_numbers.cpp): no panic on arbitrary strings, exact j/jomas suffix, canonical values; found the trim bug (parse_amount_jomas(" 2.0 "))
bin/qanum_cli/cli.rs 36 Amount parsing (QAN/jomas convention, overflow rejected not clamped), -rpcconnect/-rpcport composition, split_host_port IPv6, parse_verbose_flag, parse_u64_arg/parse_u32_arg, CLI->RPC bridge of the policy commands, stdin secret flags (-stdinrpcpass/-stdinwalletpassphrase/-stdin) at Core’s exact boundaries; the TOFU commands and rotatep2pidentity [force] map to RPCs (no offline peer_ids.json editor)
bin/qanum_cli/custody.rs 1 An HTLC destination key of this wallet keeps its suite (ML-DSA-87 stays ML-DSA-87, also with the wallet encrypted); a foreign key id stays ML-DSA-44; a public key fixes the suite by its length; an invalid one is refused
bin/qanum_cli/helpers.rs 3 Local-signing errors explain --datadir/--wallet
bin/qanum_conf/mod.rs 27 -conf: pre-scan of the Core forms, explicit conf absolute or relative to the datadir, <datadir>/qanum.conf before the legacy ~/.qanum/qanum.conf, a missing explicit conf is an error, mining_workers: integers ≥ 1 only (0, negative, non-numeric, > u32 rejected), inline # comments, rpcbind under both spellings, dnsseedrefresh parsing, keys parsed (maxuploadtarget/maxmempool/maxmempoolentries/mempoolexpiry/maxorphantx), invalid values rejected, decimals accepted, maxtxfee=0 disables, blockstore_cache=0\|1, blockstore_index/checkblockindex, chainstate_flush, netv2 master switch, conf-echo rpcpassword redaction, a permissive conf is hardened to 0600 before loading; an owner-only conf is left intact
bin/qanumd/cli.rs 1 --mining-workers: integers ≥ 1 only; above the hardware accepted (the loop clamps)
bin/qanumd_daemon/mod.rs 25 generatetoaddress template errors as the node error’s text (Core “Couldn’t create new block”); prune options checked at startup (minimum 288 blocks for -prune-depth with -prune and for a non-zero -auth-prune-depth; chainstate_flush below the prune depth); prune plan (auth-prune never whole-prunes, whole prune below the retention depth, both passes with their own depths, the prune loop follows the plan, the compact filter index’s prune lock keeps the blocks it still needs); honest startup: accept-all warning off mainnet only, strict flags in the banner, explicit warning when net_v2 is disabled, version line first and before the datadir lock, conf echo order, chainstate tip and timing, RPC bind confirmed after bind, full IBD stated, total init time, net_v2 ready; a node error that stops the start prints its human text (hash in hex, qanumd reindex advice), not its debug form; a node started from a snapshot is not an archive node until its body backfill completes
bin/qanumd_daemon/signals.rs 2 : signal names for the shutdown log; no signal received means no shutdown
block_delta.rs 8 Disconnect delta from the undo data (block_disconnect_delta): inverts the connect delta on seeded random blocks, skips in-block spends held by the undo, refuses an undo missing a spent entry; single net UTXO delta of a block (post = prev − removals ∪ appends, disjoint): coinbase-only block, plain spend, intra-block chain is net, a same-block coinbase spend is unreachable (maturity) and stays net, 300 seeded random blocks equal the sequential uncached application leaf by leaf
block_index.rs 2 Basic queries and sorted heights of the in-memory block index
block_index_file.rs 6 Snapshot roundtrip/CRC, torn journal dropped, CRC mismatch stops replay, INSERT/REMOVE replay, bitflip -> CorruptCrc, compact truncates journal
block_store_cache.rs 10 CRUD, side-block does not steal canonical height, stats atomics, process cache avoids a second blocks/ scan, reload under write lock does not deadlock, clean restart 0 listings, external file rebuild, corrupt snapshot rebuild, same-count different set, -checkblockindex
block_tree.rs 7 Fork choice: best tip is the most work (not the last inserted), equal work keeps the first seen, invalidate/reconsider move the tip, best-chain hashes genesis -> tip, empty tree has no tip; remove_subtree (the in-memory counterpart of removing an index entry with its descendants) equals a tree built without the branch, re-elects the best tip only when it was removed, and leaves a parent without children a leaf again
blockfilter.rs 5 GCS encode/decode/match, dedup, empty filters, BIP158 vectors, block+undo elements
chain_manager.rs 4 Dynamic anti-DoS work threshold: exact 1152-block buffer boundary (-> 0), one block past (-> exactly one block of work), static minimum as floor of the max (both directions), genesis tip -> 0
chainstate_flush.rs 2 No-install stats, forget does not touch other keys
codec.rs 3 decode_compact_size: round-trip at every width, non-minimal encodings rejected, truncated prefixes -> None
crypto.rs 6 Startup self-test: the checked-in known-answer vectors pass on liboqs for every suite; an accept-all backend (corrupted signature verifies), a reject-all backend and one that verifies only ML-DSA-44 are caught; the verifier network guard; #[ignore] vector regeneration. The same once-per-process self-test also runs the ML-KEM-1024 check of crypto_kem.rs. The linked liboqs reports the pinned version (LIBOQS_PINNED_VERSION = 0.16.0), whatever the oqs-sys label in Cargo.lock says (-rc1)
crypto_kem.rs 5 ML-KEM-1024 on liboqs for the P2P transport: the known-answer self-test passes; wrong lengths are refused before liboqs; fresh keys and ciphertexts every time, encapsulation round-trips; a real clatter hybridXX handshake on the liboqs KEM (four messages, protocol name, message 1 exactly 1600 B, both static keys learned, equal handshake hashes, working transport); a different prologue breaks the handshake
conf_echo.rs 2 The startup conf echo is off by default and buffers nothing without it
constants.rs 2 parse_minimum_chain_work (hex/empty/invalid) and default_minimum_chain_work per network: 0 on mainnet and regtest, the compiled non-zero value on testnet with its reference height 9 074 (CVE-2019-25220)
economics.rs 8 Subsidy (consensus): epoch = integer division, ×0.9 decay per epoch, floor at TAIL_SUBSIDY, monotonic non-increasing, extreme heights without overflow; epochs 31-35 pay the exact spec values, tail from epoch 36 including the overflow region, low epochs unchanged (the integration literals live in tests/economics_exact_values.rs)
fee_estimate.rs 11 History cap/persist, median filtered by conf_target with floor, None with insufficient history, record_mempool_confirmed (samples, no-op, coinbase ignored), exact conf_target boundary, pruning of the 50k cap, floor with min_fee_rate=0, even-count median, corrupt JSON
filter_index.rs 5 Save/load, corruption handling, rebuild, lazy indexing
fsutil.rs 6 Orphaned *.tmp cleanup; fsync policy per datadir (a path outside every registered datadir keeps fsync; the innermost registered datadir decides; registering again replaces the entry), atomic write roundtrip in a relaxed datadir; ensure_private_dir creates 0700 recursively and repairs a legacy 0755 (idempotent)
hash.rs 3 h256 known vector, length and determinism; hex32 lowercase 64-char formatting
header_chain.rs 28 DAG tip selection, side branches with less/more work, equal work keeps the first seen, locators on any branch, ancestor-chain bounds, broken chains rejected, MTP (11/short/single), header version and timestamp-equal-to-MTP rejection, bounded stored-header cap and DAG insertion guard (CVE-2019-25220); ensure_contains_chain: keeps a side branch and adds the missing active suffix, equal work keeps the first-seen best, refuses a chain that does not connect; validity marks (Core BLOCK_FAILED_VALID/FAILED_CHILD): marking flags the header and its descendants only, recalculate_best from the active tip picks the most-work valid header (ties: active tip, then first seen), a header on a failed parent is failed and never best, reconsidering clears descendants and ancestors, first-seen order survives loading; ASERT from the parent: the genesis’ child carries the anchor bits, the bits do not depend on the child’s timestamp, the rule is the anchor formula with the parent’s time and height
header_index.rs 9 headers.idx v4: a batch is one write and one fsync with no read or rewrite; a duplicate hash loads once (last write wins); a v3 index is rebuilt as v4 and its journal removed; a torn tail (cut or bad-CRC last record, zero fill) is dropped and cut at the next append without a second read; mid-file damage is kept in headers.idx.corrupt before the rebuild; an append to a missing index must start at the genesis; removing an entry drops its descendants, keeps the rest and needs no block files
header_store.rs 3 blocks/headers.dat (full headers of the blocks whose bodies the node lacks): write, read and append round-trip; a gap or a different record at a held height is refused; a damaged record is an error and never served, a torn tail is cut before the next append
headers_presync.rs 13 (Core HeadersSyncState, #25717): honest chain goes presync -> redownload -> final and releases identical headers; a low-work chain stores nothing; one bit per commitment period; a different or substituted chain in redownload aborts and is never released unverified; non-continuous batch, wrong bits/MTP, PoW (when enabled) and the max-commitments bound abort; clock behind chain start is an error; a short redownload message releases only the verified prefix; locator continues from the last header; bit-queue round-trip
mempool.rs 9 Default limits match the documented profile; children index and size-cache invariants; descendant collection equals a full scan on random DAGs; tie-break returns a tied minimum, varies across runs, ignores priority deltas, prefers the older entry, None on empty; deltas accumulate to a negative total
mempool_reorg.rs 3 Detached-block replay order (ancestor before child), coinbases skipped, blocks with only coinbases or missing on disk skipped
mempool_storage.rs 4 Snapshot v5 roundtrip keeps the byte cap and priorities; v1 migration (time -> 0); truncated snapshot rejected without panic; a truncated snapshot claiming 1M entries fails with UnexpectedEof
merkle.rs 2 Merkle proof reconstructs the root for every leaf; odd counts duplicate the last leaf
miner_v2.rs 16 mine_block_with_max_nonce finds a valid nonce, max_nonce = 0 is exhausted, an empty nonce range returns None; midstate: equals pow_hash_header over 32,064 header/nonce pairs (incl. the ends of the nonce space), and the scan returns golden first nonces computed independently (Python hashlib); parallel nonce scan (scan_nonce_space_parallel, not yet wired): the chunk bounds tile [0, max_nonce] exactly (incl. u64::MAX), the parallel scan returns the same block as the sequential scan for 1/2/4/7 workers and several chunk sizes (golden first nonce), it stops soon after a hit, exhaustion reports every nonce exactly once, abort is honoured, a worker panic is contained as Failed, and a chunk error fails closed; the scan workers run at the lowest priority (nice 19, Linux/Android) while the caller keeps its own, with the golden first nonce unchanged; update_template_time (Core UpdateTime) equals a full rebuild in the same second, bits included and moving under a hard ASERT anchor; it never moves the time back and refuses a template on another tip or a tip whose MTP + 1 is beyond the future limit; the TemplateStamp refresh policy (new tip at once, mempool change only after 5 s, never for the time alone)
mining_loop.rs 11 Single mining loop: sliced sleep returns promptly on stop and sleeps the full interval otherwise, hash-hex parsing, the loop exits on stop without a chain manager; workers: the requested value is clamped to [1, hardware] (default 1), and the loop with 2 workers mines a regtest block with real PoW through the parallel scan, imports it with PoW validation, counts it only once it is on the active chain and stops promptly while a parallel scan runs (checked by condition: at most one 50,000-nonce chunk per worker started after stop, with a 60 s hang guard); the loop pauses only after a mined block or a failure and refreshes only once the interval elapsed and the clock passed the template time; with real PoW an exhausted template is rebuilt without the interval pause but never rescanned within the same second (1 and 2 workers, 10 ms and 60 s intervals), and the loop thread keeps its priority; only a periodic refresh or an exhausted nonce space keep the template for a time-only refresh; a find, a failure, a tip change, a stop or setgenerate false rebuild it; a height peers declare (1 000 000) does not stop the real loop, and the loop waits only on IBD or demonstrated headers ahead, mining on the active tip once the tip has not advanced for 225 s (decision table, injected clock)
net_v2/addrman/store.rs 16 is_connectable_addr cases and non-routable addresses rejected on discovery; routability boundaries; mainnet rejects non-routable gossip and filters legacy buckets, other networks stay permissive; testnet policy rejects LAN and foreign families; persisted garbage purged at load; operator seednode exempt from routability but not from the family filter; snapshot service bits recorded and filtered, snapshot candidates prefer tried; eviction of never-connected and failing tried addresses; regossip refreshes last_seen only with a credible timestamp; distinct usable IPs
net_v2/chain_actor.rs 7 QUSN export cache: hit with the same tip and intact file; miss on height change, same-height reorg, missing file or different size; a poisoned reply write answers with an explicit error; damaged own undo data (UndoCorrupt) and OS errors abort the node, an invalid block or a stray undo file do not (fatal_import_message)
net_v2/chain_service.rs 1 Import errors are classified by variant (no substring matching)
net_v2/compact_filters.rs 3 best_chain_range returns ascending contiguous heights and bounds its range; resolve_* refuses a stop_hash on a side branch (BIP157 anti-DoS); range queries follow the active chain, not the best header
net_v2/config.rs 4 Net-v2 config parsing/validation; a bind without a port expands to the one P2P port
net_v2/connman/dos.rs 9 DoS/misbehavior scoring and ban pruning: expired bans removed, record caps, score decay; persist only on ban, dirty scores flushed, expired-ban removal flushed, UTF-8-safe 128-char reasons; ban_until persists and a legacy banlist loads
net_v2/connman/eviction.rs 10 Bitcoin Core SelectNodeToEvict, one rule at a time: keyed-netgroup, minimum-ping, novel-tx, block-relay-only and novel-block protections, half by uptime with the localhost share, the victim rule (youngest of the most-populated group, ties to the youngest group), small sets evict nobody, a Sybil group is drained before any honest peer
net_v2/connman/manager.rs 54 No echo to known hashes (block + tx), announced-hash marking, BIP157 gated response dispatch, disconnectnode not-connected, duplicate-connection tie-break, pong timeout, oversized Inv misbehavior, bounded known-inv cache, outbound ban skip, tx-request prefers outbound announcer; inbound eviction candidates (outbound never, the crowded address first, for any netgroup key), keepalive keeps the minimum ping and an accepted tx stamps its peer, BIP157 per-peer and Mempool request budgets, upload-window limit and rotation, bridge saturation accounting, one-shot GetAddr and sequential GetData queue; per-target exponential backoff, dead addnode does not block a live alternative, failed or unconnected addresses do not reserve their netgroup, offline node keeps recently good peers; addnode re-resolution moves the dial target, dedup, discovery-pool warning/reminder/recovery (silent under -connect/regtest); manual peers (addnode, connect, DDNS) never punished, automatic peers still punished, setban add/remove/list/errors, unban and clearbanned forget scores; a second peer’s inv of the same block reaches the scheduler and marks that peer as knowing it (no global block-inv dedup); one connection per identity: a crossed pair keeps the socket dialed by the lower peer id, both ends agree on it, the same direction keeps the first, the node’s own peer id is refused
net_v2/dnsseed.rs 13 Default seeds by network, dedup/limit, cycle aggregation and partial/total failure, periodic re-resolution backoff and hostname seednode resolution, host:port splitting, stop conditions; per-seed reason for empty answers and aliases (no false alias warnings), addnode entry classification, usable-address filter/sort/cap, resolution steps
net_v2/protocol/codec.rs 4 Per-type frame budget: oversized non-block message rejected, block up to frame accepted
net_v2/protocol/handshake.rs 6 Conditional NODE_COMPACT_FILTERS service-bit advertisement; nonce is nonzero and stable (getrandom), oversized user_agent rejected, protocol version enforced (too-old rejected, at/above accepted); a protocol-2 (TLS-era) peer is refused
net_v2/qusn_merkle.rs 6 Chunk Merkle tree: deterministic root with odd duplication, proofs verify for every index, out-of-range proof is None, tampered inputs rejected, file leaves match in-memory leaves, leaf/node domain separation
net_v2/runtime.rs 5 Readable runtime errors (config cause, I/O); per-IP in-flight handshake cap and global admission cap, loopback exempt from the per-IP handshake cap; per-IP cap holds and frees for NAT neighbours
net_v2/snapshot_sync.rs 38 Chunk counting and lengths, genesis-only detection, snapshot-address filter and cap, resume (none, mismatch, v2 gaps, legacy prefix, uncovered chunks dropped), disjoint parallel partition, chunked writer v2 (in order, out of order with gap resume, bad proof/tampered data, different artifact, legacy resume), install size bound consistent with the wire, global bootstrap budget; the minimum-work gate refuses cheap artifacts, policy defaults follow the network floor, tip-reply classification, tally needs independent network groups, a non-server disagreement is decisive, regtest groups by socket address, candidate order (configured first, no servers, no duplicates); the pause between corroboration passes doubles up to 60 s, only “behind” and transport failures are retried, a corroborator that refuses every connection is asked again until the bootstrap budget; each attempted bootstrap outcome maps to its own record (none when nothing was attempted), the record round-trips, is replaced by the next attempt and reads as absent when damaged; an unconfirmed artifact is kept with its servers (never resumed as a download), an incomplete or corrupt kept artifact is removed. Five of the tests are multi-thread #[tokio::test(flavor = "multi_thread")]
net_v2/transport/noise.rs 16 Noise hybridXX handshake and encrypted socket: both identities verified once each and the peer_ids learned, 200 KB each way in order; fixed message sizes 1600 / 12 038 / 10 454 / 1600 B; message 1 ≠ 1600 B refused from its length prefix with no key made and nothing sent, EOF inside it; empty or oversized frames; reflected role, signature over substituted static keys (§3.3) and another node’s key refused by the dialer, which then sends nothing; reflected dialer identity refused by the listener; another network fails; rekey by volume and time in both directions; unknown flag, flipped byte, short frame, EOF inside a frame; 10 s timeout; protocol name and prologue; a rotated identity sends its succession and the peer learns the new peer_id; a succession signed for another network is refused
net_v2/transport/p2p_identity.rs 11 ML-DSA-87 node identity: lengths match the suite registry; the payload round-trips to the peer_id; the signature binds role, transcript and both static keys of the signer; a flipped key or signature byte is refused; length, version and suite are refused before any signature work; peer_id stable and domain-separated; p2p_identity.json persists with mode 0600; an invalid file (garbage, another suite, mismatched keys) is refused and left untouched; a rotated identity carries its succession for 60 days, a forged succession is refused, the identity file keeps the succession
net_v2/transport/node.rs 6 One transport per node: the outbound dial is bounded, its timeout is Core’s default, loading keeps the identity, leaves any old TLS files alone and allows one transport per datadir; three multi-thread tests (dial and accept over TCP pin only on the dialer; a pinned address showing another identity is refused; a rotation moves the pins of its peers)
net_v2/transport/tofu.rs 17 peer_id pins per ip:port (peer_ids.json): first contact pins, another port is independent, a mismatch is rejected (never rotated), only outbound pins, owner-only file, LRU cap, TTL boundaries, a corrupt store fails loudly and is copied aside, a second store on the same datadir is refused, clones share pins, the TLS-era file is never touched, removal by IP clears every port; a valid succession moves the pin, one from another identity is refused; listtofupeers sorted JSON, removetofuentry validates and reports a missing pin, resettofu clears memory and disk
node/datadir.rs 6 Datadir network marker; atomic-write boundaries of save_invalid_blocks (a truncated state is impossible)
node/fatal.rs 2 OS errors in chain storage are fatal (node stops); logical and consensus errors are not
node/header_chain_io.rs 8 Header-chain file path, save/load roundtrip, missing file reported as a missing snapshot, idempotent second save; the unindexed best-chain suffix found walking back from the tip (also on a side branch), the save with a current index mirror appends only that suffix without reading the index (and inserts it into the mirror, left equal to a reload), the O(1) headerchain.bin pointer byte-identical to the old v4 snapshot; headers appended without a forced checkpoint survive losing the index appends (journal replay)
node/import/helpers.rs 13 Step-by-step reorg log lines with update-tip semantics; rollback removes only the rolled-back header-index entry; in-place import core: equal to a cold reload after each block, untouched by a rejected block, back on the parent after an undo or journal write failure, on the confirmed tip after a later failure; disconnect_tip_in_place; entry on the chain manager’s own state: reloaded only after an outside write, generation forgotten after a failure, a panic midway (child process) leaves the generation unknown and nothing written
node/mempool_io.rs 4 Mempool save/load roundtrip, missing datadir created, corrupt file errors cleanly, readable error format
node/reorg_apply.rs 2 Detaching below local history (snapshot/pruned datadir) is classified as local, not invalid; the generic missing-best-chain case stays invalid
node/sparse_reorg_walk.rs 8 The sparse cache through a reorg (Core DisconnectTip/ConnectTip): 1..N block reorgs with spends and intra-block chains equal compute_utxo_root; a cold cache rehydrates once and stays exact; a stale warm cache is caught and the reorg goes on cold; an unverifiable ancestor goes cold; a corrupt undo never leaves a wrong root; failures while connecting and while disconnecting unwind to the original tip; the invalidateblock disconnection moves cache and marker
node_api.rs 25 Mempool/fees/block stats/filter index, wallet lifecycle (create/load/unload/list, names, backup/dump/import, encrypted-file commands), custody builders use the selected wallet and branch key, estimate_smart_fee with history/floor, getmempoolinfo with the persisted byte cap, accumulated/negative priority deltas, getchaintxstats (window, errors), decode_address_checked always applies the network check even with a corrupt network.txt, input hardening, verifymessage boolean contract (corrupt signature -> false, infrastructure failures stay errors)
node_warnings.rs 4 Node warning registry: set reports only a new warning and unset clears it; the RPC list follows the warning order; sanitizing keeps only Core’s safe characters; alert_command replaces every %s with the quoted, sanitized message
p2p.rs 1 The six published default ports (P2P 8445 / 18445 / 28445, RPC 8338 / 18338 / 28338) come from Network
parser.rs 4 bounded_prealloc boundaries + crafted truncated block (100k txs) and tx (1024 inputs); readable ParseError Display
pow.rs 1 Readable PowError Display (docs/RPC.md §5)
qlog.rs 10 Rate-limiter by category: allows within budget, suppresses beyond it, independent per category, window resets (CVE-2025-54604/54605); -debug: off by default, enable by name, 1/all everything, 0/none wins, unknown category is an error, unregistered category fails open, -debugexclude has priority; net.tls is no longer a category (-debug=net.tls is an error)
release_sig.rs 12 Post-quantum release signatures: a signed release verifies; no trusted key fails closed; tampered manifest, corrupted ML-DSA-87 or SLH-DSA signature, signature without the qanum-release-v1: domain and unknown keys are refused; one signature alone (or two of one suite) is not enough; tampered/missing files are all reported; malformed .qsig (format, manifest, unknown field) refused; manifest names cannot leave the directory; encrypted key file round-trip and wrong passphrase; embedded key list well-formed
reorg.rs 2 Reorg plan detects the fork and builds detach/attach; null when there is no fork
rpc.rs 75 Request format detection (a body naming "jsonrpc" is JSON-RPC and never retried as native; otherwise the v2 envelope, then a bare request), native RPC serde round-trip and snake_case tags, block identifier detection, exact amount strings (#8317), timing-safe basic/cookie auth, 16 MiB body limit, setgenerate/getblocktemplate/submitblock errors and param parsers, declared arity covers every parameter read, param_opt_u64 array/object/null and never panics, getmininginfo/getnetworkhashps fields and guards (incl. workers: 1 without a miner handle, the requested value clamped to the hardware; the test holds the PoW toggle lock so it can no longer flip PoW off under a concurrent miner test), getchaintxstats nblocks<1 -> -8, /wallet/<name> routing, get_index_info/get_net_totals, live connection counts and peer info, results that are null are not silence (gettxout, reorg-plan), proptest params/RpcRequest; hardening (mainnet verifier guard, fail-closed auth, capped HTTP lines, poisoned locks still serve, cookie 0600 from birth, host_allowed matrix, method echo truncation), first-line classification, disk totals, error codes of generatetoaddress/abandontransaction/getnewaddress, getsnapshotinfo (defaults, pruned/auth-pruned, staged list, provenance, live serving, fallback, both dispatchers); setban arguments and listbanned JSON shape; getsnapshotinfo reports the last snapshot bootstrap attempt and null for a damaged record; a retired reward suite is -8 in generatetoaddress; sendtoaddress / sendmany failures map to Core codes (-18, -13, -5, -3, -6, else -4); getnetworkinfo.localpeerid comes from the live connman; verbose getblock / getblockheader carry Core versionHex, chainwork, mediantime, nTx and nextblockhash (omitted at the tip), and a stale block reports confirmations: -1 with the work and median time of its own branch; below a UTXO snapshot getblockheader answers from blocks/headers.dat with the source’s fields (by height and hash, verbose and raw; nTx 0 as Core for a header without block data)
sighash.rs 3 The last valid input index enters the preimage intact; the preimage is independent of birth height and flags and binds the UTXO value fields
signature_cache.rs 2 Insert/contains semantics
siphash.rs 2 SipHash-2-4 vectors and key derivation
sparse_tree_cache.rs 8 Reversible root delta (apply_block_root_delta_reversible / revert_block_root_delta): seeded random LIFO apply/revert walk from a cold cache, cold apply reverted to a warm pre-state, overwritten and doubly removed leaves restored exactly, exact revert across prune_if_oversized, stale undo refused with a fail-safe clear, empty delta a no-op, preview = apply + revert, and (release builds only) an exact revert of a delta that breaks the disjoint contract; each check compares root, leaf_map and every cached node of a non-empty subtree with a fresh canonical rehydration
sparse_tree_session.rs 3 Session starts empty and exposes its cache; a leaf update changes the root and is leaf-sensitive
suites.rs 10 Signature-suite registry: suite ids are unique; every key slot, P2RKPQ pair and the coinbase suite use registered suites; an unknown suite (0x0000, 0x0102, 0xffff) is refused everywhere; the level-5 suites follow the slot and pair rules; the activation schedule gates only level-5 suites below the height; the schedule is chosen by the genesis hash (testnet: level 5 from 0 and level 1/2 retired at 0; any other genesis: every suite active); -testactivationheight=suites5@<h> parsing, regtest only; the schedule retires only the level-1/2 suites from retire12; a retirement height may not precede suites5; softforks entries are buried deployments active for the next block
tx_index.rs 3 Block offsets, append/load round-trip, snapshot dedup
undo_storage.rs 3 Undo file naming (height-padded + hash), encode/decode roundtrip, directory join
utxo_commitment.rs 1 recompute_and_cache does not return a primed stale root
utxo_journal.rs 16 QNUJ journal: payload roundtrips, non-minimal compact sizes rejected, net diff skips create-and-spend, idempotent erase, header-only decode, bad magic/version rejected, committed batches replay, torn tail and uncommitted ops dropped, CRC bitflip stops at the prior commit, op-count mismatch invalid, unknown opcode stops, file append/load, replay skips through the snapshot hash, compaction to header-only, init does not clobber an existing file
utxo_snapshot.rs 6 Bootstrap provenance roundtrip/absent/corrupt; ASERT precondition at height 1; a forged easy chain is rejected with PoW on and passes structural checks with PoW off; the export self-verifies the UTXO root against the tip commitment
wallet.rs 12 Threshold (P2MULTI) assembly: seats sorted and validated, missing seats listed, empty group below threshold, duplicate/out-of-range key index, suite/key-id mismatch, bad key lengths, sighash cross-check (foreign seat caught, matching passes), value/input-range checks, verifier verdicts honoured
wallet_crypto.rs 13 Argon2id/AES-GCM, randomized ciphertext, key count and clear ownership metadata, per-write HKDF-SHA256 subkeys (v2 blobs), AAD binds version/kdf/params/salt/cipher, real legacy v1 vector, tampering battery, RFC 4231/5869 vectors
wallet_flows.rs 5 sendfrom excludes UTXOs spent in the mempool (exact boundary) and sees both without a mempool entry; insufficient-funds message mentions unconfirmed change only when there is some, outputs spent within the mempool are not announced
wallet_manager.rs 5 Strict branch ownership for six custody builders; locked encrypted metadata checks, outpoint hex without decimal leakage, root cause “in mempool, unconfirmed”, vault delay pre-checked before keys
wallet_policy.rs 8 Wallet key types: names and aliases; -addresstype / -changetype refuse the SLH-DSA (recovery) types; change follows Core’s rule (-changetype, else a level-5 recipient, else the default) and falls back to ML-DSA-44 before the activation; activation errors name the height; public-key lengths are unambiguous per role; key specs (key id or public key); exact signed sizes per suite; the default type follows the height (ML-DSA-87 once level 5 is active) and new keys and change avoid retired suites
wallet_store.rs 10 Encrypted-store lifecycle, metadata migration, master-key writes, locked reads, name validator inside the wallet_dir_for choke point: traversal, absolute paths and reserved names fail closed
wallet_unlock.rs 4 Per-wallet unlock/lock, wrong passphrase, TTL expiry; the same wallet name in two datadirs unlocks independently (state keyed by datadir and wallet)

Integration tests (tests/: 272 files, 1461 tests)

The table below describes the key suites; the complete per-file list follows it.

Key suites

File Tests What it covers
sparse_tree_cache.rs 63 Sparse tree cache (commitments); the largest suite after sync_scheduler_robustness
consensus_invariants.rs 15 Consensus invariants across reorgs/mempool/state
consensus_limits.rs 10 Every consensus size limit tested at exactly the limit (its error must not appear) and one unit over it (it must): tx inputs, outputs, base bytes and auth bytes, auth per input (validator and parser), parser input/output counts, block base bytes and block auth bytes. Sizes measured from the serialization; each test fails if its check is moved by one (a mutation run made all 9 fail); every suite’s Verify-Unit constant covers its worst measured cost
block_validation.rs 17 Block validation: merkle/auth/utxo roots, coinbase rules, templates, vault/p2rkpq/p2multi. block_invalid_p2multi_output derives its key count from P2MULTI_MAX_KEYS. block_over_verify_units_budget_rejected_before_crypto: a block exceeding MAX_BLOCK_VERIFY_UNITS (168 P2RKPQ-recovery spends = 201 600 VU, under the byte limits) is rejected before the crypto phase
verifier_mandatory.rs 6 Live qanumd: accept-all / reject-all refused within 20 s on regtest, testnet and mainnet (command line), in qanum.conf and in qanumd reindex, each with “not available”; the default banner says verifier=Oqs; the RPC verifier parameter of sendrawtransaction / testmempoolaccept only accepts oqs; the library default selection is OQS
node_warnings.rs 4 Core CheckForkWarningConditions on a real ChainManager: an invalid chain more than six blocks of work over the tip raises LARGE_WORK_INVALID_CHAIN until the tip catches up; within six blocks it does not; nothing is evaluated during IBD; a deep invalidateblock warns and reconsiderblock clears it. One registry per process, so the tests run one at a time
alertnotify_e2e.rs 1 Two live qanumd: a 101-block reorg submitted with submitblock puts the deep-reorg warning in getblockchaininfo, getnetworkinfo and getmininginfo (all [] before) and runs -alertnotify exactly once with the sanitized message; the other node has no warning
asert_parent_rule.rs 2 ChainManager with real PoW: a sibling that arrives later stamped earlier, with the parent’s bits, is stored and leaves the first-seen tip alone, and one more block on it moves the tip; bits from the block’s own timestamp are refused with BitsMismatch and do not displace the tip
getblocktemplate_mintime_e2e.rs 1 Live qanumd regtest: mintime = MTP + 1 (Core GetMinimumTime, BIP 23) against the last 11 block timestamps and mediantime; mutable = ["time", "transactions", "prevblock"]; the template block re-stamped at mintime is accepted with the template’s bits, at the MTP refused
p2p_noise_only.rs 4 Live qanumd: -plaintextport on the command line and in qanum.conf refuses to start; the one P2P port speaks only Noise and protocol 3; on a running node the TOFU RPCs list, remove (-8 for a bad or missing pin) and reset the pin the node made for the test, getpeerinfo/getnetworkinfo show peer_id, transport and localpeerid, and after rotatep2pidentity the test’s next handshake moves its pin to the new identity
genesis.rs 14 Deterministic dev and testnet genesis blocks and their chainstates; the testnet genesis meets its own proof of work (as Bitcoin’s does; its hash compared directly against its target); the datadir network persists and a mismatch is refused; the testnet parameters (level 5 from block 0, magic qan4, ML-DSA-87 coinbase output, v2 message, no minimum chain work or assumevalid from v1); --reward-key-id refused where ML-DSA-44 is retired from the genesis; Core LoadChainstate: a datadir with another genesis, or blocks without this network’s genesis, refused at startup and untouched; the node’s own and an empty datadir pass; genesis pin (Core chainparams): the built testnet and regtest genesis match the block hash and merkle root pinned in the source, the testnet pin is the mined hash, and a genesis that differs from its pin is refused with both hashes in the error
header_index_v4.rs 4 A torn tail then an append keeps the index aligned; a zero-filled tail gives no phantom entry; mid-file damage is an error and is never truncated; a 2 000-header batch on a 10 000-entry index reads and rewrites nothing (v3: 31 full reads)
header_store_snapshot.rs 4 A node started from a UTXO snapshot, restarted (empty header cache), serves every header of its active chain from genesis, byte-equal to the source; getblockhash answers below the snapshot (Bitcoin Core keeps full headers in its block index); the node exports a snapshot byte-equal to the source’s; a lost headers.idx is rebuilt equal (before, the rebuild silently kept only genesis)
offline_mine_reward.rs 8 Live qanumd, checked on the stored block’s coinbase: the offline mine-block / mine-blocks take the reward from the daemon’s one setting: --opt=value and --opt value, before or after the subcommand, qanum.conf; two destinations from different sources refused before mining; testnet refuses --reward-key-id in every form; with no reward the anonymous coinbase
vectors.rs 10 Serialization vectors / round-trips. tv007_p2multi_keycount_too_large_invalid derives its key count from P2MULTI_MAX_KEYS for the same reason
golden_vectors.rs 5 Locks the committed golden consensus vectors (tests/data/golden_vectors.json, emitted by examples/golden_vectors.rs): serialization, txid/wtxid, sighash digests, merkle/auth roots, sparse-Merkle UTXO root, subsidy schedule: the no-crypto surface a second implementation must reproduce byte-for-byte
sync_scheduler_robustness.rs 65 Header-sync scheduler against Core MaybeSendGetHeaders, HandleUnconnectingHeaders, UpdateBlockAvailability and ProcessBlockAvailability: IBD entry and exit, announcement dedup, pacing and the fork alert; idle catch-up, including during IBD; header-sync watchdogs; the unified stale-tip threshold; queue stash and resume; presync steps; one getheaders outstanding per peer whatever the trigger; an unconnecting batch re-asks the same peer and only invalid headers quarantine; a local failure never blames the peer; every announcer is asked, a quarantined one does not block the others, and a block whose source left is fetched from another announcer; floods of unknown hashes stay bounded; a height a peer only declares raises no fork alarm; a block a peer answers NotFound for is asked at once from another source; archive peers are preferred over pruned ones (which are asked only when no archive peer can serve the block), and a pruned peer’s NotFound is not retried
headers_presync_chain.rs 7 (ChainManager, Core TryLowWorkHeadersSync): an honest low-work chain is learned through presync and nothing is stored before redownload; a low-work chain is never stored; a batch crossing the threshold mid-batch is stored whole; a substituted chain in redownload is rejected; state dropped on empty message and disconnect; states bounded; a low-work fork below the dynamic threshold is not stored
header_dag_active_chain.rs 5 (ChainManager, Core block index): a side-branch header survives local mining; the active tip is always in the header DAG; getheaders serves the active chain, not a headers-only branch; the reply to a side-branch locator connects; a restart restores the active chain when the best header is a side branch
header_dag_invalid.rs 13 : a block that fails validation after its header arrived must not stay the best header; headerchain.bin must stay loadable; headers on top of an invalid block and the invalid header sent again must be rejected (Core bad-prevblk / duplicate-invalid); invalidateblock moves the best header back and reconsiderblock restores it. Live guard: a mutated body never marks its header. A failing cascade descendant is marked; an invalid block relayed without its header is remembered (its header and a child refused); an invalid header after a valid prefix is refused; is_block_mutated against the header’s commitments. After invalidateblock and a restart the invalidated header, a header on it and the block are refused; reconsiderblock of an ancestor keeps a still-invalidated descendant out of the best header; reconsiderblock after a restart restores a headers-only suffix as the best header
headers_outcome_classification.rs 5 (ChainManager, Core ProcessHeadersMessage): an unconnecting batch is typed Unconnecting and stores nothing; a header failing validation, malformed bytes, a foreign genesis or a chain rooted in another network’s genesis are Invalid; the peer-less import_headers_with_pow keeps its historical error contract
assume_valid.rs 9 (Core assumevalid semantics): compiled hash per network, two-week burial constant and exact boundary, exact ancestry of the assumevalid block, the block itself and its descendants are verified, side-branch blocks are never covered, off-best-chain or best header below the minimum chain work disables skipping, unset/0 never covers
assumevalid_core_parity.rs 3 Signatures skipped only for buried ancestors of the assumevalid block (on regtest, --release); an unsolicited low-work side block is not persisted unvalidated; unrequested-block policy matches Core AcceptBlock
net_v2_runtime_stop.rs 3 Stoppable net-v2 runtime for library hosts: prompt stop releases everything and the datadir reboots; stop during IBD loses nothing and sync resumes; dropping the stop sender stops the node
economics_exact_values.rs 4 Exact subsidy literals at epochs 31-35 (the underpaid epochs of the saturating_mul bug), tail arrival EXACTLY at epoch 36 (pure value 18 022 719 < 20 000 000), cross-check against the direct formula floor(S·9ᵉ/10ᵉ) through epoch 31 in u128, monotone paid subsidy incl. u64::MAX -> tail (the near-infinite-loop sentinel); values independently reproduced with exact integer arithmetic
net_v2_ibd_stress_and_resilience.rs 8 IBD stress: stall recovery, orphan storms, header floods, reorgs, unserviceable retry
wallet_scan.rs / wallet_flows.rs / wallet_store.rs / wallet_runtime.rs 4 / 5 / 2 / 1 Wallet scanning; output/address building (vault, P2PQK, P2RKPQ, network mismatch rejected, immature-coinbase context); key store; runtime. The wallet flow tests are unit tests in src/wallet_flows.rs
wallet_custody_multiwallet_oqs.rs 3 Six strict P2RKPQ/Vault branches with real OQS signatures; native TCP RPC named-wallet build/sign and wrong-wallet rejection
wallet_p2multi.rs 14 P2MULTI on the wallet path: payload/address round-trips, threshold and ordering rules, and the key-cap invariants. the_key_cap_is_the_same_in_every_layer pins no value; it asserts wallet, address and consensus agree, so it survived the 4->16 change untouched. key_cap_is_consistent_with_the_auth_budget fails if the cap is raised past what fits one input’s auth entry. a_spend_at_the_key_cap_fits_the_per_input_auth_budget puts a full n-of-n auth entry through both enforcement points (parser.rs and validator.rs)
crypto_oqs_real.rs 8 Real OQS signatures end to end (crypto-oqs): ML-DSA-44 / SLH-DSA-128s round-trips, mutated-signature rejection, P2PQK and P2RKPQ recovery paths, and validate_tx_with_verifier_accepts_a_real_spend_at_the_p2multi_key_cap: 16 real keypairs sign one input, consensus accepts it, then a flipped bit must yield SignatureVerify. The only test that shows a 16-of-16 output can actually be spent
crypto_fips_kat_oqs.rs 11 crypto-oqs (+ NIST ACVP FIPS 203 ML-KEM-1024 subsets: keyGen tg3 tcId 51-53 from the seed, decapsulation tg6 with valid and modified ciphertexts, encapsulation- and decapsulation-key checks tg11/tg12 with valid and invalid keys; + ML-DSA-87 tg5 and SLH-DSA-SHAKE-256s tg29 sigVer subsets, pure/external, valid and invalid cases including too-long and too-short signatures; the length check covers the four suites): NIST ACVP sigVer vectors through OqsVerifier: SLH-DSA-SHAKE-128s (FIPS 205, external/pure, 7 cases: valid, bad R, bad FORS, bad hypertree, modified message, signature one byte too long and one too short) and ML-DSA-44 (FIPS 204, 6 cases; the valid ones have a context, checked through verify_with_ctx_str); the pk / sk / sig lengths of 0x0101 and 0x0201 equal liboqs’s; ML-DSA-44 signatures made by liboqs 0.12 (the previous dependency) still verify; a SPHINCS+-SHAKE-128s-simple signature made by liboqs 0.12 is refused as 0x0201; a short signature is refused before liboqs. Data with provenance in tests/data/acvp/ and tests/data/legacy_liboqs_0_12_*
suite_registry.rs 6 The suite table gives the same answer as a frozen copy of the literal rules it replaced, for all 65 536 suite ids: slot and P2RKPQ pair rules, lengths / Verify-Units cost / name, the signed message (ML-DSA or SLH-DSA family) and the verify_suite dispatch; consensus, the address builder and the wallet builder accept and refuse the same outputs, with probe controls proving each probe reaches the output rules. A refactor, so no fail-first; the frozen rules gain the level-5 suites, written by hand
crypto_level5_oqs.rs 7 crypto-oqs: real ML-DSA-87 (0x0104) and SLH-DSA-SHAKE-256s (0x0203) signatures through the node verifier (forgeries refused) and through consensus in every key role: P2PQK; the level-5 P2RKPQ pair, main path and recovery path after the delay; P2MULTI mixing ML-DSA-44 and ML-DSA-87; a 16-of-16 ML-DSA-87 P2MULTI (115 553 B) in one input; HTLC claim and refund; VAULT_PENDING hot and cold. Written fail-first, before the level-5 suites existed
suite_activation.rs 6 The suites5 activation height (3 in the tests). A block below it cannot create a level-5 output (three connect paths); the schedule survives ChainState::from_parts; the mempool gates at tip + 1 across five templates; the node submit path, the ChainManager pool and the pool rebuilt after a reorg below the height follow the chain’s schedule (mutation proof: removing the node’s schedule settings fails exactly these 3)
wallet_level5.rs 10 crypto-oqs: level-5 keys in the wallet: getnewaddress mldsa87 before (-4) and from the activation, import before it, keypoolrefill default, change to the spending key’s suite and to a level-5 recipient’s, exact automatic fee (accepted by the mempool at the minimum relay rate), a payment to a level-5 address before the activation fails in the wallet, createmultisig with a foreign ML-DSA-87 public key, nist_level
wallet_migrate_level5.rs 14 crypto-oqs: migratesuites: parsing and -8, dry run equal to execution (fees, sizes, txids), split within the mempool’s 101 000-byte limit, -4 before the activation (also getnewaddress), -13 locked, -maxtxfee, options, a P2RKPQ pair with the recovery key elsewhere left alone; level-5 reward in templates, generatetoaddress and the integrated miner
wallet_addresstype.rs 4 crypto-oqs, own binary (process-wide wallet types): -addresstype for getnewaddress and keypoolrefill from the activation, -changetype / fresh change before and after it, payment-address types only
suite_retirement.rs 16 The retire12 height (suites5 at 2 and retire12 at 4 in the tests). From retire12 a block cannot create an ML-DSA-44 / SLH-DSA-128s output; softforks; the schedule survives ChainState::from_parts; the mempool gates at tip + 1, the node submit path follows it, and the saved pool, a pool loaded from disk and a reorg to retire12 drop retired outputs; the miner does not stall and the template skips them; the anonymous coinbase pays ML-DSA-87 and an ML-DSA-44 reward is refused with the reason; VAULT exemption: the hot path of an old vault still works (twice in one block, accepted, kept and mined by the pool) and covers only the hot-path covenant
wallet_retirement.rs 7 crypto-oqs: the wallet across suites5 and retire12: getnewaddress without a type, keypoolrefill and fresh change follow the height; payments to a retired suite are refused naming the height; change of an old key goes to a fresh ML-DSA-87 key; getnewaddress of a retired type is -4; sendtoaddress / sendmany errors are plain text with Core codes
wallet_history_custody.rs 3 P2RKPQ/Vault/VaultPending/HTLC history through mempool, address index, and no-index canonical fallback
wallet_htlc.rs 7 Canonical HTLC address/payload, claim/refund auth, branch ownership, preimage/height policy, named-wallet Node API
wallet_live_balance_custody.rs 2 Branch-aware mempool balances for P2RKPQ/Vault/VaultPending/HTLC, chained-spend exclusion, confirmed HTLC classification
net_v2_compact_filters.rs 6 BIP157 filter ranges, filter hashes/previous header, checkpoints, service signaling, limits/no-response, net_v2 codec. Refactored to the resolve-under-lock / load-without-lock split exercised by the chain-actor
crypto_validation.rs 15 Post-quantum signature validation (accept-all/reject-all, key-id mismatch, HTLC/vault paths); no_crypto delay/timeout/unlock; cached wtxid still fails HeightCondition
vault_state_machine_hardening.rs 18 Full negative battery of the VAULT/VAULT_PENDING machine in the no-crypto variant (shape/height logic no assumevalid or signature cache can skip): delay bounds at the exact edges (144 / 5 256 000 valid; 0, 143, 5 256 001, u32::MAX rejected), hot≠cold in both templates, unlock height zero, per-output suites, hot-init shape (single input, vout[0] = PENDING, byte-exact pending payload with shrunk AND grown windows rejected as VaultInitPendingMismatch, change with identical payload or VaultInitChangeMismatch, wrong change template), per-path suites and unknown paths on all three transitions, and cold cancel seeded positive at ANY height
consensus_branch_keys.rs 4 Identical P2RKPQ/Vault/HTLC branch keys, HTLC timeout_height == 0, ChainManager::new_with_verifier
consensus_vault_pending_payload.rs 2 VAULT_PENDING unlock_height == 0, MAX_OUTPUT_PAYLOAD_BYTES in the validator
persistence_atomicity.rs 3 Distinct .qnlk/.qnlp temps, undo rebuild on ChainManager::new, header-index append rollback
pruned_reindex.rs 6 -prune deletes the block and undo files below the window and keeps the genesis body, after storing each deleted block’s full header and tx count in headers.dat (getblockhash answers from it; a second pass deletes nothing, a later window extends the store); base-only .qnlp files left by older versions are deleted the same way; a missing body of the active chain stops the pass below it; reindex of a pruned datadir is refused (with a matching or a leftover mismatched snapshot) and leaves the snapshot untouched, non-minimal snapshot rebuild (loading resets to genesis, the node start’s validated activation reconnects the stored block)
pruned_rpc.rs 6 RPC answers about blocks whose bodies -prune deleted, as Bitcoin Core: getblock (by height or hash), getblockstats and gettxoutproof return Block not available (pruned data) (-1), a block never stored on a node that never pruned is Block not available (not fully downloaded), a stored or unknown block is unchanged; getchaintxstats gives the same figures before and after pruning (tx counts from headers.dat), also for a window ending on a pruned block; the getblockstats median time at the window edge equals getblockheader’s; the wallet history without -addressindex lists a spend inside the window of an output received in a pruned block (from the undo data) and not the pruned receive; listsinceblock accepts the hash of a pruned block and still rejects an unknown one
pruned_filter_index.rs 4 The compact filter index on a pruned node, as Bitcoin Core: the prune lock keeps ten blocks below the index’s best block; enabling the index after its blocks were pruned refuses to start (best block of the index goes beyond pruned data); an index that fell behind resumes from its best block instead of genesis; the index’s best block never claims a block it could not index
filter_index_rebuild_pruned.rs 2 Filter-index rebuild with pruned undo (coinbase-only OK, multi-tx skipped + stale)
chainstate_flush.rs 6 Legacy write every extension, defer until N, crash recovery ≤N by UTXO-journal replay from the lagging snapshot (not genesis), save clears dirty, flush_dirty, commit without install
chainstate_flush_lock.rs 7 Every chainstate writer holds the chainstate lock like every import (Core: cs_main): the hazard with the flush steps interleaved by hand (older state published again, import reported as flushed, journal delta lost); flush_dirty and flush_all_dirty wait while another thread holds the lock; a flush thread racing 40 imports loses none; invalidateblock does not disconnect on disk and the snapshot install does not write while the lock is held; the flush before a snapshot export waits for it
chainstate_live_publication.rs 4 The live chainstate is an immutable Arc: flushing it copies nothing (2 copies before, chainstate::full_clone_count), a reader gets one copy that later publications do not move; every chainstate write, install and forget bump a per-datadir generation, reads and no-op flushes do not; a ChainManager import over the live chainstate copies the state once per block (2 for the first after creating the manager)
chain_manager_owned_state.rs 3 : the ChainManager owns its working chainstate: after every import its state equals the published state and a cold load from disk (linear blocks, a flush every 2 blocks, a restart, a reorg onto a branch mined by another manager); an outside chainstate write is seen by the next import (the stored parent is reconnected first); readers of the published state during imports only see whole states
chainstate_catchup_validation.rs 8 (Core ActivateBestChain): a stored body left by an interrupted import (false root, excess coinbase, invalid end of a heavier branch) is never connected at restart and is discarded; a valid one is connected with validation; invalidateblock/reconsiderblock (one-shot and through the chain actor) validate what they reconnect; a lost journal tail is reconnected with validation
sparse_cache_reorg_walk.rs 3 Production path, real PoW: valid reorgs of depth 2 and 3 move the warm cache with zero cold rehydrations; invalidateblock + reconsiderblock keep it warm; a reorg that fails halfway returns the cache exactly to the original tip
template_incremental_refresh.rs 5 Production path ChainManager: a parent, its mempool-only child and an independent spend give exactly the reference template (utxo_root from a cloned, fully recomputed UTXO set), previewing leaves a warm cache untouched and the block connects; Core getblocktemplate refresh policy with explicit clocks (time-only reuse equal to a fresh build, mempool change kept 5 s then rebuilt, other cap / reward key / new tip rebuilt at once); a reused template connects; a replaced mempool never serves the kept template; the miner’s read-lock time-only refresh
generate_core_path.rs 1 Production path ChainManager: regtest generate builds with the one template builder and connects through the normal import: the warm sparse cache follows each generated tip (no rehydration at the next template) and the chain matches a fresh recompute; fails on the old coinbase-only path
daemon_signal_shutdown.rs 8 Real qanumd: SIGTERM, SIGINT and SIGHUP stop in order like the stop RPC (the control): exit 0, block_index.journal empty, no UTXO journal batch, and the restart keeps the tip with 0 listings and no replay; the log names the signal; two SIGTERMs; SIGTERM during startup; SIGHUP/SIGINT inherited as ignored (nohup) stay ignored while SIGTERM still stops; 6 of 8 fail on the old daemon
startup_sparse_cache_warm.rs 2 : a freshly opened manager pays the cold sparse-cache rehydration in its first template (the problem); after warm_sparse_cache (called at startup by qanumd and qanum-embed) the first template does not rehydrate, the root is canonical and a second warm-up is free
generate_relay_e2e.rs 1 Two qanumd: after A and B connect and agree on one generated block, five more generated on A reach B within the bound; fails on the old daemon (B stays behind)
own_block_header_index.rs 3 Production path ChainManager with the daemon’s live chainstate: own blocks read headers.idx 0 times (2 per block before) and keep the cached tree equal to a reload from disk; own-block headers indexed once and headerchain.bin at the tip across a cut without flush; own blocks below a taller headers-only branch: neither the header burst nor the blocks read headers.idx
block_tree_index_mirror.rs 1 Production path ChainManager with the daemon’s live chainstate: the import updates the cached block tree in place and keeps it the mirror of headers.idx in every arm: after an own block, a duplicate, a rejected block, an orphan, a cascade whose descendant fails validation (discarded with its header), a headers-only branch and own blocks below it, a current tree equals a reload and the index holds each header once.
relay_block_import_cost.rs 1 Production path ChainManager with the daemon’s live chainstate and process-wide block-store cache: relayed blocks (header, then body) read headers.idx 0 times, build the stored-body index 0 times and keep the cached tree current (3 reads, 1 build and a stale tree per block before) and own blocks build no body index; bodies stored out of order wait for the missing one and connect as a cascade; tree equal to a reload, headers indexed once, headerchain.bin at the tip
apply_block_undo_oracle.rs 3 : the undo returned by apply_block_to_utxo_set (as Core’s UpdateCoins fills CTxUndo) equals byte for byte the one connect_block_and_create_undo builds on a copy (500 random blocks with several inputs per tx, in-block chained spends, burn outputs, coinbase-only blocks) with the same resulting set and a disconnect back to the pre-state; a chained spend records the in-block coin in vin order; a missing input panics (fail-closed) instead of being skipped
block_store_cache_integration.rs 4 Cache lifecycle KPIs, process cache without a second scan, sync_canonical ignores a side block, clean restart hydrates from block_index.bin with 0 listings
auth_prune.rs 4 Selective auth-data pruning: keeps undo data, txindex lookups survive, idempotent; a pruned (.qnlp) block is never served to peers (load_servable_block_bytes -> NotFound) while full blocks are served byte for byte
mempool_policy_rules.rs 8 RBF feerate, fat-cheap rejected, CPFP eviction, rebuild min_fee_rate, expiry, revalidate, QNMP v3; plus the reorg fee-floor bypass for txs returning from a detached block
asert.rs / pow.rs / header_chain.rs 15 / 11 / 10 from_anchor past hardens, work at POW_LIMIT, MTP < ts < parent accepted; the PoW rule follows the network of the chain checked: a regtest datadir prepared in the same process leaves a testnet header with a broken nonce refused on testnet and mainnet; MAX_ASHIFT = 256 clamp: i64::MAX/i64::MIN saturation + ±256/±257 behavior-preservation boundaries
net_v2_addrman.rs 5 Bootstrap/tried/anti-poisoning, 1-per-netgroup outbound, last_seen clamp, addrman.json 0600
node_import*.rs, node_reorg*.rs, node_mempool*.rs ~20 Node import strict/regular, reorgs, mempool reorg interplay
net_v2_* (addrman, dos, antispam, trickle, netgroups, poisson, header_sync) ~25 Net_v2 peer management, DoS, netgroup quotas, trickle, header sync
adversarial_* (mempool churn, dag, reorg churn) 3 Attack scenarios
utxo_commitment.rs, sparse_tree_* ~90 UTXO commitments and sparse tree correctness
rpc_contract.rs 10 RPC contract over a live JSON-RPC endpoint: getblockchaininfo heights/progress, mediantime of getblockchaininfo/getblockstats is Core’s median time past, negative and surplus parameters, Display error messages, -32700 parse envelope, txid-string submissions, cross-network addresses, hash validation; softforks lists suites5 (and retire12 when scheduled) as Core buried deployments
rpc_contract_params.rs 7 JSON-RPC contract: extra params, 401 Content-Length, getblockstats -8 + named numeric, estimatesmartfee, generatetoaddress, importwallet -8
same_height_reorg_indexes.rs 3 Same-height reorg RAM + indexes, filter-index hash compare, attached_hashes
wallet_custody_rpc.rs 6 signrequest envelope, build-payment outpoint message (library + RPC Display), hex OutPoint, delay Display, getnewaddress slhdsa generates a real SLH-DSA-128s key
release_verify_cli.rs 5 qanum-cli verifyrelease <dir> offline: requires a directory, reports a missing SHA512SUMS or SHA512SUMS.qsig, never accepts signatures by untrusted keys; the release keys built into the binary match the key ids published in docs/SECURITY.md
pq_bench_report.rs 6 How examples/pq_bench reduces and writes its measurements, on fixed samples: lower median and nearest-rank p95, empty and single series, the Verify-Unit estimate ceil(100 × t / t_ML-DSA-44) (gives back known reference values), round drift in basis points, one-line JSON records with kind and schema. The example itself is not run by the suite
security_facts.rs 10 The security facts of a release (security_facts, schema qanum-security-facts/1): complete, deterministic output that survives a JSON round trip, a missing field or another schema refused; suites equal to the registry field by field (a changed cost is caught and named); known limitations equal to docs/AUDIT-PACKAGE.md §6, with entries without a bold title, numbering gaps and a missing section refused; fuzz targets equal to fuzz/fuzz_targets/; pinned toolchain, liboqs (linked and pinned version, self-test) and oqs revision; networks (magic, port, genesis), release key ids and transport from the code; the release inputs: tag v<version>, 40-hex commit, YYYY-MM-DD date, SBOM list (bare .cdx.json names, no duplicates, qanum-core-<version> present), the cargo audit --json summary (a vulnerability stops the release and is named; malformed output refused), SHA-512 of published files (FIPS 180-4 vector), fuzzing status not run unless a report is given, fixed statements (no certification, no external audit, no mainnet), deterministic rendering
release_sha512sums_script.rs 3 contrib/release/sha512sums.sh run for real on a release directory (tarball with COMMIT_INFO, both SBOMs, facts from security_facts::release_facts): SHA512SUMS lists the four files with their SHA-512, sorted; a missing facts file, an extra file or an existing SHA512SUMS.qsig is refused; facts of another commit than COMMIT_INFO, another schema, version, tag or date, with a vulnerability, with a missing SBOM entry, that disagree with an SBOM in the directory, or that are not JSON are refused with the reason and no SHA512SUMS
release_source_tarball_script.rs 3 The secret scan of contrib/release/source-tarball.sh, with the pattern read from the script: it does not match the script itself (the release tree holds it), it still catches each kind of token and private key, and it finds nothing in the source tree
backupwallet_checks.rs 2 backupwallet: a wallet with no keys yet gets a clear error and no file is written; with a key, the backup is a byte-exact copy of the key file
importwallet_key_checks.rs 5 Wallet import: suite lengths, 16 MiB dump cap, 4096-key cap, secret<->pubkey check (real keys accepted, correctly sized fakes refused)
wallet_rpc_contract.rs 6 Wallet RPC contract: total-received vs balance, include_empty, repeated inputs from one address in the index, abandontransaction ownership, keypoolrefill 0, unlock timeout semantics
Other wallet_*_oqs.rs (9 files) 15 Wallet signing with real liboqs: flows, store, runtime, builders, P2RKPQ and vault
fuzz_decode.rs 6 Property-based wire fuzzing (proptest): NetMessage/parse_tx/parse_block_header/parse_block never panic on arbitrary bytes, valid-variant corrupt payloads, u64::MAX counts
fuzz_utxo.rs 8 UTXO commitment fuzzing (<- Core coinscache_sim.cpp/utxo_snapshot.cpp): root stable under insertion order, changes with the value, reflects removals (pruned root == direct root), apply_tx_outputs_to_utxo + compute_utxo_root no-panic with outputs present, empty-set root == empty_utxo_root, deterministic utxo_key/utxo_leaf_hash derivation
fuzz_crypto_oqs.rs 2 Sign->verify roundtrip of ML-DSA-44 and SLH-DSA-128s over random 32-byte messages; a mutated signature or message is rejected (<- Core key.cpp/secp256k1_ecdsa_signature*)
fuzz_indexes.rs 9 Fuzzing of the on-disk index formats (<- Core flatfile.cpp/dbwrapper.cpp/block_index.cpp): txindex.dat (no-panic on arbitrary bytes, valid-record roundtrip, corrupt CRC ignored, invalid header/version) and filter_index (encode/decode roundtrip, no-panic, mutation)
fuzz_crypto.rs 13 Non-OQS crypto: h256 (deterministic, avalanche, extremes), key_id_for_pubkey (per suite, sensitive), wallet crypto (Argon2id+AES-GCM encrypt/decrypt roundtrip with few cases, no-panic, wrong passphrase rejected), sighash msg_alg (deterministic, input/UTXO sensitive)
fuzz_parser.rs 8 Adversarial parser: no-panic on arbitrary bytes, truncation at every prefix, trailing garbage, empty input, parse_block_header_prefix == parse_block_header, extreme tx counter, parse(serialize(parse(x))) stability
fuzz_core_alignment.rs 15 Fuzzing ported from Bitcoin Core targets: addrman.cpp->buckets (determinism/range/no-panic), merkle.cpp->proof roundtrip, pow.cpp->bits/target roundtrip + no-panic, golomb_rice.cpp/blockfilter.cpp->gcs_decode no-panic + gcs_match, message.cpp/protocol.cpp->NetMessage wire roundtrip
fuzz_property.rs 14 Property-based fuzzing of the persistence formats and pure decoders: mempool snapshot (QNMP v5) roundtrip + arbitrary-bytes no-panic, undo roundtrip + no-panic, header-index no-panic, bech32m addresses (arbitrary/binary strings no-panic, P2PQK roundtrip, all-template decode->re-encode canonicality), compact_size full-range roundtrip, tx serialize->parse roundtrip; an arbitrary payload after a v1 undo header (the bytes that reach the parser) never panics
fuzz_seed_corpus.rs 4 The committed libFuzzer seeds still match the current formats: one generated frame per NetMessage variant (exhaustive match, so a new variant needs a seed) decoding to the variant its file names, the version seed inside the handshake frame limit, native / envelope / JSON-RPC RPC seeds parsing in their branch, and the captured transactions, blocks, headers and addresses still parsing
blockfilter_rpc_e2e.rs 2 BIP157 over a real node; hardened against slow CI runners (--rpc-timeout=600, deterministic filter_index_up_to_date wait, daemon log dump); protocolversion checked against PROTOCOL_VERSION
net_v2_antieclipse.rs 3 Several connections from one IP admitted (routable and loopback); inbound full with every peer protected rejects the newcomer; a Sybil filling the slots from one address evicts itself, not honest peers; real connman, deterministic
mempool_orphans.rs 7 CVE-2024-52914: orphan relay cache: indexed capture/resolution on parent arrival, multi-parent kept until all parents arrive, permanently-invalid dropped, cap, TTL, snapshot v4 round-trip, submit-path capture
mempool_congestion_vault_cancel.rs 5 Vault cancel under congestion (VAULT-ADVERSARIAL.md §3.4): congestion star case: descendant-package eviction under both caps (400 entries / 200 KB), urgent cold vault cancel crossing a saturated pool + RBF (incremental fee + strictly-higher feerate; strict anti-pinning rule 2 documented), confirmed-cancel beats attacker finalize race (MissingInput + HeightCondition at tip+1 semantics), honest MempoolFull under entry and byte caps, MAX_REPLACEMENT_CANDIDATES bound
mempool_rbf_boundaries.rs 3 Exact edges of the RBF predicate: feerate tie rejected (strict > required) even when the absolute fee covers the total, absolute fee below replaced_sum + min_fee_rate×size rejected even with a higher feerate, same fee never replaces (real conflict, not DuplicateTxid); sizes measured at runtime and fees derived from them (no constant bytes), each scenario with a passing control
vault_cancel_vs_finalize_congestion.rs 4 Cancel-vs-finalize race: the in-pool cancel-vs-finalize race over the same VAULT_PENDING under a saturated pool: the pre-unlock window pinned (HeightCondition stops the attacker finalize at ANY fee while the cold cancel enters at any height), anti-pinning (cold cancel RBF-displaces a pinned finalize), the fee war pinned as symmetric-by-policy (same-fee steal -> ReplacementFeeTooLow, overpay DOES replace, the cold re-bump wins the slot back), and window closure by spend not by time (MissingInput after a confirmed finalize at any height ≥ unlock)
mainnet_verifier_guard.rs 4 Contract of validate_verifier_selection: accept-all / reject-all refused with “not available” on mainnet AND testnet, allowed on regtest (the simulation build’s only network), Oqs allowed everywhere
max_tx_fee.rs 4 Core -maxtxfee: default cap 0.10 QAN, error format, send_many_rpc rejects over-cap, maxtxfee=0 disables
settxfee_and_mempool_ancestry.rs 3 settxfee roundtrip, getmempoolancestors/getmempooldescendants (list + verbose + not-in-mempool)
wallet_fee_policy_oqs.rs 4 Anti-fee-sniping sets lock_height to tip (and honours explicit), fee=0 uses settxfee rate, zero-fee without rate is a clear error
fee_estimation_history.rs 2 Confirmation-history fee estimation: a block confirming mempool txs feeds the history and estimate_smart_fee returns the median; falls back to the mempool without history
mempool_priority_and_limits.rs 4 prioritisetransaction delta persists in snapshot v5, RPC applies it (and errors on missing tx), eviction tie-break keeps the mempool working, max_orphans override bounds the orphan cache
miner_v2_priorities.rs 4 at template level: higher real fee first (control), the delta orders candidates of equal fee, the coinbase uses the REAL fee (not inflated by the delta), a negative delta demotes, effective fee clamped at 0
chainstate_scale_benchmark.rs 2 oversize_snapshot_rejected pins the 512 MiB snapshot ceiling; scale benchmark is manual #[ignore]
utxo_commitment_scale_benchmark.rs 1 Manual #[ignore] scale bench of the sparse commitment engine: chainstate B/entry, incremental warm-up growth, leaf-map RAM per entry, steady-state per-block delta (800 in/800 out), reconstructive compute_utxo_root reference + bit-exact equivalence, and a cold-cache journal probe demonstrating the unbounded rehydrate journal
storage.rs 10 v4 tip-pointer roundtrip, v3/v2 legacy, headerchain O(1) checkpoint, idempotent idx
utxo_journal.rs 20 QNUJ format, CRC/torn, import deltas, crash replay, CRC -> load stops at the last intact batch, 10× compact, write-amp KPI
utxo_journal_reorg.rs 5 A reorg writes nothing to the UTXO journal: after a failed reorg and a crash the node reloads the tip it had (not the valid prefix of the failed branch), a later block replays without revalidation, a valid reorg leaves only the snapshot, a leftover journal from an older version is emptied after its failed replay
regtest_pow.rs 3 -regtestpow=1 keeps the real proof of work on regtest: end to end with the binary, three blocks mined with the switch all meet their target and three without it do not (nonce search skipped); refused on testnet with a clear message before the datadir is touched; library level: regtest only, and it alone decides the PoW rule of regtest chains (preparing a datadir changes nothing); testnet and mainnet always require PoW
reindex_validation.rs 7 Every rebuild of the chainstate from the stored blocks validates like the import (Core -reindex-chainstate): a stored block whose only fault is a forged signature is not connected by the startup fallback (missing chainstate.bin), by reindex or by the unplannable-chainstate recovery, and the victim coin stays unspent; with an invalid best block reindex keeps the valid chain and the node still starts; a valid stored body above the tip is connected; fixture check: the adversarial block is invalid only by its signature (release, crypto-oqs)
undo_integrity.rs 21 Damaged undo data of the node’s own active chain (Core ReadBlockUndo checksum; FatalError("Failed to disconnect block.")): a truncated or altered undo makes a reorg fail as a local failure, never Invalid, and the heavier valid branch stays stored; invalidateblock over it returns an error and saves nothing; getblockstats reports no fee from it; startup rebuilds a truncated or altered undo; v2 records carry a checksum over block hash + record (every single-bit change refused, a record under another block’s name refused) and v1 records are still read; a v1 undo altered so it still decodes is caught by the UTXO root at the common ancestor (warm and cold sparse cache, reorg and invalidateblock) while an intact v1 still reorgs; after a failed reorg a restart rebuilds the undo of the active chain and the node moves to the heavier branch (release); a node pruned to 288 blocks that loses the undo data of a recent real spend refuses to start with a clear error (the rebuild needs the deleted bodies) and writes no undo data, and reindex of a pruned node is refused and changes nothing, also once the pruned files are gone (persistent pruned marker) and on a node started from a UTXO snapshot
invalidateblock_mempool.rs 3 (Core InvalidateBlock -> MaybeUpdateMempoolForReorg): the transactions of the disconnected blocks return through the node’s verifier: a spend with a broken signature in a block connected without checks does not return, a valid one does (offline path); the operator’s fee floor, byte cap and orphan cap survive on the running node, in memory and on disk, and a returned spend is exempt from the fee floor; with 11 blocks disconnected the spend of the 1st returns and the spend of the 11th does not. Real ML-DSA-44 key on a regtest chain of COINBASE_MATURITY + 2 blocks
persistence_recovery.rs 6 Reindex rebuilds, truncated undo rejected, corrupt block diagnosed with actionable error, clean reindex passes
rpc_auth.rs 3 Basic/cookie auth over a real socket, oversized request body rejected with 413
utxo_snapshot.rs 13 AIP-0001: QUSN v2 self-verifying snapshot core: manifest + UTXO set encode/decode round-trips, embedded header-chain extraction, utxo_root recompute == committed root, install + provenance (snapshot_bootstrap.json), tampered/garbage artifact rejection, a v2 snapshot with an unknown network magic refused
utxo_snapshot_export_best_chain.rs 1 The export walks back from the chainstate tip through prev_hash links, so stale fork blocks left on disk can never leak into the artifact
chain_actor_admin_invalidate.rs 1 invalidateblock/reconsiderblock routed through the chain actor survive flushes and reloads on a running daemon (falsified by disconnecting the reload)
utxo_snapshot_cache_cleanup.rs 3 Single-slot artifact cache: P2P export sweep, startup sweep, manual-export-into-cache sweep; operator-named files untouched (falsified by disabling the export sweep)
snapshot_over_prefix.rs 10 (Bitcoin Core loadtxoutset during IBD, ActivateSnapshot checks): a kept snapshot replaces a local chain that is a strict prefix of it, and the history below it is removed (only the genesis body, no undo; the next block connects); refused, chain untouched: a forked partial chain, a chain at or above the snapshot height, a known forked header chain with more work (also on a fresh datadir; --force unchanged; more work on the same chain accepted), a block of the snapshot chain marked invalid; the prefix check runs under the chainstate lock (a fork committed while the install waits is not overwritten). Start-up without network: a leftover partial download on a datadir with a chain is removed, an obsolete kept artifact is removed without an attempt, a kept artifact over a fork is discarded (Failed), a kept artifact with nobody to confirm it stays kept (Unconfirmed)
snapshot_contextual_asert.rs 9 The contextual ASERT gate of the snapshot pipeline from the public API; ACCEPT path first: an honest chain (real testnet genesis + genuinely mined, ASERT-exact headers; header 1 stamped as late as the clock allows, so header 2 sits at the clamp plateau once the genesis is old enough and at an eased difficulty before; expected bits computed, never assumed) passes end-to-end (commitment_ok, headers_checked=3); exact rejection causes: forged-at-PoW-limit chain dies only at the gate with BitsMismatch naming expected vs declared bits, timestamp == MTP (TimestampMtp, strict >), beyond now+2h (TimestampFuture); genesis identity: foreign genesis rejected by identity, mainnet magic fails CLOSED, unknown magic rejected before contextual work; install anchors ASERT on the NETWORK genesis, never on the artifact (--test-threads=1: touches the process-global PoW switch)
body_backfill_core.rs 7 (background IBD): resumable progress roundtrip (.body-backfill/resume.json), body<->header anchoring (tampered committed hash / tampered prev / wrong-height body / garbage all rejected), full-range verification over a mined chain; full block history (and so NODE_ARCHIVE) only once the backfill of the installed snapshot completes

All integration files

Every file in tests/ with its number of test functions, grouped by name prefix (272 files, 1461 tests).

Prefix Files
adversarial_* adversarial_mempool_churn 1, adversarial_mempool_dag 1, adversarial_reorg_churn 1
block_* block_index 2, block_index_api 1, block_index_edge 2, block_parallel_validation 4, block_store 10, block_store_cache_integration 4, block_store_duplicate_height 1, block_store_query 4, block_tree 4, block_tree_index_mirror 1, block_validation 17, block_validation_with_verifier 4
cached_* cached_block_tree_benchmark 1, cached_block_tree_equivalence 1, cached_import_equivalence 2
chain_* chain_actor 3, chain_actor_admin_invalidate 1, chain_headers_memory 1, chain_manager_owned_state 3
chainstate_* chainstate 6, chainstate_catchup_validation 8, chainstate_flush 6, chainstate_flush_lock 7, chainstate_live_publication 4, chainstate_scale_benchmark 2
consensus_* consensus_branch_keys 4, consensus_invariants 15, consensus_limits 10, consensus_vault_pending_payload 2
crypto_* crypto_backend_selection 3, crypto_fips_kat_oqs 11, crypto_level5_oqs 7, crypto_oqs_real 8, crypto_validation 15
fuzz_* fuzz_core_alignment 15, fuzz_crypto 13, fuzz_crypto_oqs 2, fuzz_decode 6, fuzz_indexes 9, fuzz_parser 8, fuzz_properties 7, fuzz_property 14, fuzz_seed_corpus 4, fuzz_utxo 8
header_* header_chain 10, header_dag_active_chain 5, header_dag_invalid 13, header_future_time 2, header_index 1, header_index_recovery 3, header_index_v4 4, header_store_snapshot 4
import_* import_best_chain_with_bodies 8, import_session_benchmark 1, import_session_stress 1
mempool_* mempool 15, mempool_acceptance_budget 1, mempool_congestion_vault_cancel 5, mempool_deps 3, mempool_orphans 7, mempool_package_limits 2, mempool_policy_rules 8, mempool_priority_and_limits 4, mempool_rbf_boundaries 3, mempool_rebuild_amplification 1, mempool_reorg_invariants 1, mempool_storage 1
miner_* miner 5, miner_deps 1, miner_v2_limits 1, miner_v2_package_scoring 1, miner_v2_package_selection 1, miner_v2_priorities 4, miner_v2_template_valid 1
net_* net_v2_addrman 5, net_v2_addrman_edge 5, net_v2_antieclipse 3, net_v2_antispam 2, net_v2_compact_filters 6, net_v2_dos 2, net_v2_header_sync 8, net_v2_ibd_hardening 6, net_v2_ibd_stress_and_resilience 8, net_v2_multinode_e2e 3, net_v2_netgroups 2, net_v2_poisson 1, net_v2_runtime_stop 3, net_v2_scheduler 6, net_v2_sync_robustness 8, net_v2_trickle 1
node_* node_api 4, node_import 5, node_import_strict 7, node_mempool 1, node_mempool_reorg 4, node_reorg 2, node_reorg_incremental 1, node_session_benchmark 1, node_undo_integration 1, node_warnings 4
persistence_* persistence_atomicity 3, persistence_optimization 2, persistence_recovery 6
release_* release_docs_stamps 2, release_sha512sums_script 3, release_source_tarball_script 3, release_verify_cli 5
reorg_* reorg 4, reorg_api 1, reorg_apply 1, reorg_plan_equivalence 2, reorg_unit 2
rpc_* rpc 11, rpc_auth 3, rpc_contract 10, rpc_contract_params 7, rpc_http_limits 9, rpc_stop 6
sparse_* sparse_cache_multi_attach 3, sparse_cache_rejected_block 4, sparse_cache_reorg_walk 3, sparse_cache_sync_equivalence 3, sparse_tree_block_simulation 2, sparse_tree_cache 63, sparse_tree_cache_unit 6, sparse_tree_equivalence_chain 2, sparse_tree_hybrid 4, sparse_tree_incremental_benchmark 1, sparse_tree_partial_reuse_benchmark 1, sparse_tree_reorg_simulation 3, sparse_tree_reuse_benchmark 1, sparse_tree_reuse_measurement 1, sparse_tree_root_consensus 11, sparse_tree_runtime 1, sparse_tree_session 2, sparse_tree_spend_only 3, sparse_tree_stability_benchmark 1, sparse_tree_stats_benchmark 2
suite_* suite_activation 6, suite_registry 6, suite_retirement 16
undo_* undo 6, undo_edge 4, undo_integrity 21, undo_storage 2
utxo_* utxo 8, utxo_commitment 30, utxo_commitment_scale_benchmark 1, utxo_journal 20, utxo_journal_block_delta 3, utxo_journal_reorg 5, utxo_root_delta_equivalence 12, utxo_snapshot 12, utxo_snapshot_cache_cleanup 3, utxo_snapshot_export_best_chain 1
wallet_* wallet 3, wallet_addresstype 4, wallet_builder 2, wallet_builder_oqs 1, wallet_custody_multiwallet_oqs 3, wallet_custody_rpc 6, wallet_fee_policy_oqs 4, wallet_flows 5, wallet_flows_oqs 2, wallet_history_custody 3, wallet_htlc 7, wallet_keyids 5, wallet_level5 10, wallet_live_balance_custody 2, wallet_migrate_level5 14, wallet_oqs 1, wallet_p2multi 14, wallet_p2rkpq 3, wallet_p2rkpq_oqs 2, wallet_retirement 7, wallet_rpc_contract 6, wallet_runtime 1, wallet_runtime_oqs 1, wallet_runtime_p2rkpq_oqs 2, wallet_runtime_vault_oqs 2, wallet_scan 4, wallet_store 2, wallet_store_oqs 2, wallet_store_race 2, wallet_vault 4, wallet_vault_oqs 2
other address 10, address_bech32 4, addressindex_integration 1, alertnotify_e2e 1, apply_block_undo_oracle 3, asert 15, asert_parent_rule 2, assume_valid 9, assumevalid_core_parity 3, auth_prune 4, backupwallet_checks 2, blockfilter_bip158 2, blockfilter_rpc_e2e 2, body_backfill_core 6, ci_test_coverage 3, cli_network_datadir 2, cli_version_flag 2, codec 6, coinbase_height_rule 9, constants 11, daemon_signal_shutdown 8, datadir_lock 6, dos 4, economics 6, economics_exact_values 4, ensure_undos_fast_replay 2, experimental_commitment_benchmark 1, fee_estimation_history 2, filter_index_rebuild_pruned 2, fsutil 8, generate_core_path 1, generate_relay_e2e 1, genesis 14, getaddressinfo_pubkey 5, getblocktemplate_mintime_e2e 1, gettxoutproof_txindex 5, golden_vectors 5, hash 7, headers_outcome_classification 5, headers_presync_chain 7, htlc_creation_window 6, importwallet_key_checks 5, incremental_block_tree 1, invalidateblock_mempool 3, mainnet_verifier_guard 4, max_tx_fee 4, merkle 8, mined_chain_survives_reindex 3, offline_mine_reward 8, own_block_header_index 3, p2p_noise_only 4, parser_roundtrip 16, pathological_block_tree 1, pow 10, pq_bench_report 6, pruned_filter_index 4, pruned_reindex 6, pruned_rpc 6, qlog 6, rebuild_consistency 2, regtest_pow 3, reindex_amplification 1, reindex_validation 7, relay_block_import_cost 1, same_height_reorg_indexes 3, security_facts 10, settxfee_and_mempool_ancestry 3, sighash 8, snapshot_contextual_asert 9, snapshot_over_prefix 10, startup_sparse_cache_warm 2, stateful_reorg_stress 1, storage 10, sync_scheduler_robustness 65, template_incremental_refresh 5, template_test_block_validity 8, testmempoolaccept 9, txindex_integration 3, types_roundtrip 7, vault_cancel_vs_finalize_congestion 4, vault_state_machine_hardening 18, vectors 10, verifier_mandatory 6

Conventions


Embedded node crate (qanum-embed)

The workspace member qanum-embed/ (the library the mobile app embeds) has its own tests, not counted in the 2273 above: 34 test functions: 12 unit tests in 4 source files and 22 integration tests in 6 files. Run them with cargo test -p qanum-embed -- --test-threads=1 (crypto-oqs is a mandatory default feature).

File Tests What it covers
src/api/chain.rs 2 Status identifies height and tip; a missing block is reported explicitly
src/api/error.rs 2 Stable error codes for the FFI boundary; Display without internal debug output
src/api/wallet.rs 2 The documented default wallet is the empty string; history cursor ordered by height and outpoint
src/daemon.rs 6 Snapshot IBD off by default; snapshot candidates: connect first, then bare-IP addnode with the network port; host names and garbage skipped, duplicates removed; wallet poll log line ends with the elapsed milliseconds; the reward takes the suite of the wallet key (ML-DSA-87 or ML-DSA-44), an unknown key stays ML-DSA-44 and a recovery key is refused; on testnet (ML-DSA-44 retired at genesis) a key the wallet does not hold is refused, while an ML-DSA-87 wallet key still mines
tests/embedded_lifecycle.rs 6 Restart keeps the chain and datadirs are independent; one daemon per datadir, lock released on stop and drop; stop with mining on is prompt and final; submit_tx and traffic contracts in solo mode; invalid inputs rejected before any I/O; the mining reward follows the suite of the wallet key
tests/embedded_localnet.rs 2 A peer disappears and returns and the embedded node recovers; network stop and restart on the same datadir
tests/embedded_prune.rs 4 Pruning is off by default; a window below 288 blocks is refused before touching the datadir; a pruned node deletes the blocks below the window (genesis kept) and boots again at the same tip; a pruned node does not announce the archive service bits
tests/embedded_smoke.rs 1 Boot, mine, wallet and stop; set_mining_workers accepts 0/1/u32::MAX and with 2 workers the node keeps mining counted blocks, then stops in ≤ 1 s while mining
tests/embedded_snapshot.rs 8 A fresh datadir boots from the peer snapshot; snapshot bootstrap and pruning work together (boot at the snapshot height, mine past the window, blocks below it deleted, every block accepted by a full peer, reboot without a new snapshot); uncorroborated or contradicted snapshots are not installed; a locked datadir is refused before any snapshot I/O; dead and snapshot-less candidates fall back to normal IBD; a corroborator that is unreachable only for the first seconds does not force a full IBD, and a disagreement that arrives in a retry pass is still decisive
tests/embedded_sync_progress.rs 1 headers_height leads during IBD and follows new blocks

Functional suite (Bitcoin Core style, outside the Rust inventory)

test/functional/ starts real qanumd nodes (regtest) and drives them with qanum-cli (see test/functional/README.md): the qan_framework.py harness plus 45 end-to-end tests: 18 rpc_, 9 wallet_, 8 feature_, 6 mempool_, 2 mining_ and 2 p2p_. Examples: feature_chainstate_flush (-chainstate_flush=8, dirty state on disk, clean restart, SIGKILL + catch-up), feature_blockstore_index (getblockstoreinfo reports 0 directory listings on a clean restart, -checkblockindex, -blockstore_index=0, corrupt snapshot, -blockstore_cache=0), feature_maxuploadtarget_high (contrast: with 10 MB node B syncs; with 100 B it is blocked), feature_min_fee_rate (per-HOME isolated qanum.conf -> minrelaytxfee 0.01, FeeTooLow quotes 1000), p2p_connection (inbound peer + disconnectnode), rpc_getblockheader (304-char raw hex + object), rpc_getrawmempool (verbose map txid -> fee/fee_rate), rpc_gettxout (real UTXO in jomas, invalid vout -> None), rpc_getchaintips (one active tip = best tip), wallet_backup (backupwallet/dumpwallet/importwallet), wallet_abandontransaction (clear rejections: in mempool / confirmed) and rpc_validateaddress (own address isvalid/ismine, invalid -> isvalid: false without an error). Every node runs with the production verifier and its own empty HOME (home_conf writes a qanum.conf there), and every test mines its own regtest chain from genesis (mine_blocks(n), or fund() for spendable coins). These tests are not part of the Rust function inventory above; they verify the real binary end to end.

Naming taxonomy (Bitcoin Core convention)

Every functional test carries a family prefix (rpc_, mempool_, mining_, wallet_, p2p_, feature_, interface_, tool_); see the full table in test/functional/README.md. The complete Qanum layer map:

Layer Location Speed What it verifies
Unit inline src/**/mod tests (Rust) ms One isolated function/module
Integration tests/*.rs (test crate) s In-memory flows (mempool, reorg, RPC without a daemon)
Fuzz (proptest) tests/fuzz_*.rs s-min No panic and roundtrips on arbitrary inputs
Fuzz (libFuzzer) fuzz/fuzz_targets/ (11 targets) min-h Coverage-guided campaigns on decoders and sync state machines (fuzz/README.md)
Embedded node qanum-embed/ (unit + tests/) s-min Library API used by the mobile app (lifecycle, mining stop, snapshot, recovery)
Functional test/functional/*.py s-min (real node) End to end over qanumd + qanum-cli

The taxonomy mirrors Bitcoin Core’s in Rust’s idiomatic locations (unit tests inline in src/, integration tests in tests/), without copying Core’s src/test/ path, a need of the C++ ecosystem that adds nothing in Rust.