Skip to content

Implement economy-transition-v6 in the C++20 kernel and retire version four's codec - #178

Merged
kaikisegfault merged 3 commits into
mainfrom
feat/177-economy-transition-v6-kernel
Aug 17, 2026
Merged

Implement economy-transition-v6 in the C++20 kernel and retire version four's codec#178
kaikisegfault merged 3 commits into
mainfrom
feat/177-economy-transition-v6-kernel

Conversation

@kaikisegfault

Copy link
Copy Markdown
Owner

Closes #177. Requirement 10 of docs/project/first-goal.md, for the byte and derivation surface.

What this delivers

include/protocol/v6/economy.hpp, nine sources under src/v6/, and five test translation units checked against test-vectors/economy-transition-v6.txt. It is a codec plus the pure derivations and performs no state transition — every entry point is a function of its arguments:

  • the envelope, its two authorization schemes, the fourteen bodies and the five retired numbers;
  • the admission shape rules the specification lists at step 1;
  • the six HUB messages;
  • the escrow and signer derivations;
  • the security posture: slot_of, the confirmation predicate, and the relaxation predicate;
  • the fourteen state entry encodings and the two retired ones;
  • the economy tree, the version-six state root, genesis, chain identity, the receipt;
  • the bounded mint walk and the verified-user arithmetic.

Version four's codec is removed rather than kept beside it

The kernel was compiling exactly one economy contract and it was the one already known to have no conforming implementation — version four's kind 11 names an identity the transaction never carries, which is why versions five and six exist.

Every Python model and vector file is retained: a model plus its vectors is the record of what the hosted matrix verified, and tools/economy-transition-v4-vectors/ still verifies its 441. A codec records nothing. ADR 0046 states the decision and the two rejected alternatives.

The shared account derivation

H(D("protocol-stack:v1:account") || 0x01 || pk) is now defined once in src/v1/account.hpp and used by both version one's admission path and version six's signer_id, rather than copied. The test checks it against test-vectors/protocol-primitives-v1.txt's recorded account_id — a third source, not a second restatement.

What it establishes, and what it does not

Of ADR 0045's four derived rules a codec can reach one, and it reaches it. NOTHING_TO_MINT is the empty walk range rather than the literal equality, so a mark can never decrease. DEBIT_OVERFLOW's position in the envelope order, the unrequested-confirmation refusal, and the cycle-assignment prologue need a ledger this does not have and are the next slice.

One of the three is pinned from the admitting side even so, and a probe is why. A mutation making the codec refuse a mint carrying a nonzero confirmation field — the rule version six's text literally states, at admission, under a code the result space does not contain — passed. Nothing in the test or in either accepted vector file noticed an implementation stricter than the contract can be. The test now requires such a mint to be admitted.

Evidence

Three checks reach a third source rather than a second opinion of the version-six file: the kind-1 identity and the signer derivation against protocol-primitives-v1.txt, the accounts tree against the same, and the two cycle-assignment records against economy-transition-v3.txt, because version six's settlement is version three's imported rather than reimplemented.

The populated economy root is what makes this more than a width check. The 44-entry fixture covers all fourteen assigned entry kinds, so one recorded root constrains every value encoding at once. Two probes swapping adjacent same-width fields — signer_count with exempt_slot_mask, and next_escrow_index with escrow_count — failed there and nowhere else.

Nineteen mutation probes fail closed, including the escrow label, the version-one account octet, the state-root schema version (a number, which the handoff warned would not appear in a search for v4), the RFC 9162 split replaced by a halving, the bitmap packed least-significant-bit first, a retired kind given a width, the scheme rule dropped, and genesis admitting an account.

Nothing accepted was edited. The v6 specification gains a status line and an evidence pointer and no rule; the v5 specification gains a corrected sentence about where the kernel's codec lives.

Verification

Local: the codec and all five test units compile clean under -Wall -Wextra -Wpedantic -Werror and pass; the version-one admission, execution, genesis, commitment, and block tests pass after the shared-derivation change; tests/tools/test_registration_test.py passes. The full compiler and sanitizer matrix is this PR's gate.

…four's codec

The C++20 kernel carried a codec for `economy-transition-v4`, which is the one
economy contract already known to have no conforming implementation: its kind 11
opens its rejection conditions with "an unregistered `hub_identity_hash` is
`NOT_HUB_VERIFIED`" over an identity the transaction never carries. Versions five
and six both exist because of that defect, so the repository was compiling
exactly the contract nothing can implement.

`src/v6/` replaces `src/v4/` rather than sitting beside it. Every Python model
and vector file is retained, because a model plus its vectors is the record of
what the hosted matrix verified; a codec records nothing and is one
implementation of a byte surface. ADR 0046 states the decision and its
alternatives.

It is a codec plus the pure derivations and it performs no state transition: the
envelope and its two authorization schemes, the fourteen bodies and five retired
numbers, the six HUB messages, the escrow and signer derivations, the posture
predicates, the fourteen state entry encodings, the economy tree, the version-six
state root, genesis, chain identity, the receipt, the bounded mint walk, and the
verified-user arithmetic.

The accepted version-one account derivation is now defined once in
`src/v1/account.hpp` and shared by version one's admission path and version six's
signer derivation, rather than copied. A second implementation of one derivation
is a second place for it to drift, and the drift would be silent.

Of the four rules ADR 0045 derived, a codec can reach one and does:
`NOTHING_TO_MINT` is the empty walk range rather than the literal equality, so a
mark can never decrease. The other three need a ledger this does not have.
Three entry points take untrusted input — the signed transaction, the receipt,
and the one variable-width state value. M3.9a's version-four codec shipped with
no fuzz target, which was a gap in the required evidence rather than a judgement
that one did not apply.

It asserts two properties beyond the absence of a crash, and both are what
consensus rests on. Decoding is deterministic, so two nodes handed identical
bytes reach identical answers. And decoding round-trips: anything the decoder
accepts re-encodes to exactly the bytes it came from, which is what makes a
canonical encoding canonical and what catches a decoder that quietly tolerated a
second representation of one transaction.

Registered in `PROTOCOL_STACK_TARGETS`, which carries `-Werror`, the sanitizer
flags, and the libsodium link, and given a bounded smoke entry beside the other
three kernel fuzz targets. M3.9a declared a target and left it off that list.
…registrations

`clang-sanitizers` failed at the link: `economy_v6_fuzz` was declared, listed in
`PROTOCOL_STACK_TARGETS`, and left out of the loop applying `-fsanitize=fuzzer`,
so it had no libFuzzer `main`.

A fuzz executable is registered in four places and only two omissions fail
loudly. Out of `PROTOCOL_STACK_TARGETS` a target builds without `-Werror`, the
sanitizer flags, and the libsodium link, which is the M3.9a defect; out of the
instrumentation loop it runs with no coverage feedback and explores nothing while
reporting success. Both are silent. `tests/tools/test_registration_test.py` now
requires every file under `tests/fuzz/` to appear in all four, and each of the
five omissions was demonstrated to fail it.

The guard's first draft was vacuous in the shape it exists to catch: it split
`CMakeLists.txt` at the first `PROTOCOL_STACK_TARGETS` and searched everything
after it, which matched the target's own `add_executable` block and passed with
the target removed from the list. The `list(APPEND)` block is nested inside
`if(PROTOCOL_STACK_ENABLE_FUZZING)` and closes on an indented paren, so a pattern
anchored to column zero does not terminate there — the same defect M3.7a found in
this file's `add_test` parser. The parse is now asserted to find two blocks.
@kaikisegfault
kaikisegfault merged commit 5f6f70a into main Aug 17, 2026
6 checks passed
@kaikisegfault
kaikisegfault deleted the feat/177-economy-transition-v6-kernel branch August 17, 2026 14:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

M3.10c: the C++20 kernel codec for economy-transition-v6

1 participant