WO-03b — Count-bound Merkle roots

7 October 2026 · Local checks passed. DONE WO-03b handoff authorized by the owner; Open entries 4–10 remain for reviews and audits. Claude’s fresh surprise exam is pending. No merge or next work order is authorized by this handoff.

Branch: wo-03b-count-bound, from WO-03 at ac9cd56. New Python reference and vectors frozen in fc8010a before the C++ change. Implements the owner-approved CHAIN-DESIGN §2b revision. All earlier golden files and evidence remain unchanged.

Change

For both transaction and event roots:

count > 0: SHAKE256("E46-merkle\0" || 0x02 || u64_LE(count) || tree_root), 32 bytes
count = 0: 32 zero bytes

The wrapper is applied once, after the tree is complete. Leaf/node tags and odd carry are unchanged. All nonempty roots, including singleton roots, now use the count seal. The C++ library serializes the count byte by byte; native endianness does not affect the output. There are no difficulty constants in this change.

Proof now owns {leaf, index, leaf_count, siblings}. Index/count are uint64_t. Tree::proof() returns the complete record; the tree retains leaf bytes to do so. verify(expected_root, proof) reconstructs the raw tree root and seals it with the supplied count before comparing. The field-based overload uses the same path. Zero-count inclusion proofs, impossible indices and extra/missing siblings fail.

The expected root must still come from the caller’s authenticated context. Changing both a proof and an untrusted expected root does not prove membership in a particular block. The count seal addresses the previously demonstrated three-versus-four-leaf ambiguity under the hash’s security assumptions.

Tree::tree_root() is an explicitly documented diagnostic/intermediate value. Chain callers use root(), merkle_root() or event_root(), all count-bound. bind_root(raw_root, count) exposes the exact wrapper for independent encoding tests; its zero-count result is zero regardless of the supplied raw root.

Event sorting, duplicate rejection and the WO-08/09 validation boundary are unchanged. Supplied IDs are sort keys; leaves contain type || body. WO-03b neither mines nor validates Rads or commitment stamps.

Results

Evidence: summary, source fingerprints and command logs alongside them.

Check

Result

Python/C++ comparisons

104,570 passed

Altered-count checks in differential run

30,279 rejected

Random trees

1,000 new seeded trees plus the 1,000 earlier frozen trees

Raw-tree regression cases

2,015 match the independent reference, including earlier frozen roots

Required tree sizes

0, 1, 2, 3, 5, 1,000 for transactions/events; additional real trees at 255, 256, 257 leaves

Count encoding

12 wrapper vectors, including byte boundaries, 2^32, 2^63, 2^64−1

C++ unit coverage

8,385 inclusion paths; wrong counts, leaf/root/index/sibling tampering, missing/extra siblings

Release suite

12/12 passed

ASan/UBSan suite

12/12 passed, recovery disabled

New and prior golden regeneration

Byte-for-byte matches; no frozen files edited

Formatting

Changed C++ files pass clang-format --dry-run --Werror

Fuzz

101,601 executions in 31 seconds, one worker, no crashes or sanitizer errors

Benchmark

1,150.69 roots/second median, 1,000 transaction IDs, one worker, five approximately 0.25-second samples

The very large count vectors test wrapper encoding, not construction of enormous trees. Parser checks reject negative and above-uint64_t values. Valid paths are also tested against the old unbound roots and rejected.

Golden/fuzzer seed: 2026100730. Tools: installed Apple Clang 21 for release and sanitizers; installed Homebrew LLVM 22 for libFuzzer. Benchmarks include tree allocation and retained leaf bytes, are host/load-specific, and make no security claim. An initial build caught an obsolete unit-test call after the Proof API change; it was corrected before the recorded final checks.

Open decisions and limitations — mandatory handoff

Recorded in DECISIONS-NEEDED.md, not only in chat:

Forward status: implementation complete locally and ready for independent review; no known unresolved formula choice or failed test blocks WO-03b. Items 4–9 are retrospective documentation/claim review. Item 10 is a downstream integration handoff. The owner subsequently authorized a DONE handoff while these entries remain Open for reviews and audits. They have not been silently resolved or removed.

Item

Finding and recommended disposition

4 — light clients

Correct the claim that count binding first makes inclusion proofs possible. It authenticates the count; proofs still do not establish full validity or unspent status. Height is inferred from chain position. Qualify QTL/E46i claims with the required context.

5 — security arithmetic

CHAIN-DESIGN §2a/§7 quotes about 2^128 classical preimage cost for the combined 256-bit payload; §9 quotes 2^256 for a 512-bit output. Under an ideal-function model the generic classical preimage scales are 2^256 and 2^512 respectively, with Grover scales 2^128 and 2^256. Clarify any alternative attack model; do not label actual joint-address security proven. PHASE3-RESULTS §L.5 already qualifies the combined construction.

6 — evidence versus security

White-paper §3 turns Grover simulation and finite collision measurements into stronger statements about full preimage work/equality with SHA-256. Recommend retaining the measured outcomes while labeling ideal-model estimates and structural-security uncertainty, as in PHASE3-RESULTS §L.3–L.7. No quantum hardware or full-width security proof is supplied by WO-03b.

7 — PoW weakness

Correct the white paper’s claim that difficulty adjustment makes a weak PoW hash harmless. Restored block cadence does not remove a miner’s relative advantage or reorganization/censorship risk; see PHASE3-RESULTS §L.6.

8 — QTL sequentiality

Request the exact assumptions, construction-specific argument and resource model behind the blanket quantum “no shortcut” claim for the planned SHAKE chain. Qualify the text until reviewed. This is not a newly demonstrated QTL attack; QTL was not changed or tested here.

9 — checksum wording

Qualify “always catches a typo” using the actual BIP-350 guarantees and preserve WO-04’s separation between framing/checksum acceptance and address/consensus validity.

10 — integration scope

Record responsibility for authenticating expected roots, validating event IDs/bodies, defining a proof wire format and enforcing block/P2P resource limits. The diagnostic raw root and test CLI must not become consensus interfaces by accident. Recommend retaining WO-03b’s approved scope and assigning these downstream tasks explicitly.

Already resolved in the decision log: event sort/format decision 1, count binding decision 2, and provisional mining-cost correction 3. They are not reopened here. The enormous-count fixtures are wrapper tests only; benchmark rates are host-specific; local tests do not replace independent review. Those evidence limits remain explicit in the Results section above.

Reproduce / surprise exam commands

cmake --preset release && cmake --build --preset release && ctest --preset release
cmake --preset sanitize && cmake --build --preset sanitize && ctest --preset sanitize
python3 -B e46/golden/generate_wo03b.py --check

Fresh reference: e46/golden/merkle_bound_reference.py. Historical merkle_reference.py remains frozen and describes the old unbound tree. The active differential script is e46/tests/verify_wo03b.py; the original verify_wo03.py remains historical and should not be run against the new API.

python3 -B e46/tests/verify_wo03b.py --cli build/release/merkle_cli --golden e46/golden/wo03b-golden.json --seed <reviewer-seed>

The optional seed adds 1,000 fresh trees and is not printed by the verifier. Builder tests do not constitute Claude’s independent exam.

CLI change

Requests retain their WO-03 spelling. proof and event_proof now return:

OK <leaf-hex-or-dash> <index> <leaf_count> <sibling-hex> ...

That record can be supplied directly after verify <bound-root-hex>. Verification returns OK 1 or OK 0; malformed input returns ERR. Counts/indices in this test protocol are decimal; the hashed count is always eight little-endian bytes. No consensus/network proof wire format has been introduced.

Two diagnostic commands are available:

tree_root <leaf-hex> ...
bind <count> <raw-tree-root-hex>

Light-client clarification

The transaction and event roots sit alongside each other in the header. Merkle inclusion was possible before count binding; this change also authenticates the claimed count. A proof does not establish full block validity or current unspent status. Header height is inferred from chain position, not a height field in the 120-byte header. QTL depth/decay calculations additionally need the relevant validated chain context and transaction/output data. No light client, ASERT validator or Rad validator was implemented by this work order.

Paradox A remains in force: WO-08/09 must measure exact §2g message formats and nonce-bound puzzles before fixing mining parameters. No installations, pushes, merges or later work orders. Owner map edits and the separate PR proposal remain outside these implementation commits.