Skip to content

feat(economic): the 0x0029 issuance producer — token.mint is the one mint channel - #751

Merged
cryptskii merged 1 commit into
mainfrom
feat/0x0029-issuance-producer
Aug 30, 2026
Merged

feat(economic): the 0x0029 issuance producer — token.mint is the one mint channel#751
cryptskii merged 1 commit into
mainfrom
feat/0x0029-issuance-producer

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

The producer half of the 0x0029 core/producer split. After this cut the
definition-of-done statement is literally true:

The only way a positive user-token Mint can enter canonical DeviceState is
through an economic admission whose CreditSource is AuthorizedIssuance and
whose immutable 0x0029 evidence independently proves that the committed
token policy authorized that exact asset, amount, recipient lineage/device,
operation digest, and write-once economic position.

One authorization channel

Operation::Mint is now { amount, token_id, policy_commit, message }
nothing in it asserts issuance authority. The legacy channel is deleted
outright, not disabled: the authorized_by/proof_of_authorization fields
(wire tag 4 layout change, beta wipe), the mint|v2| transition verifier, the
token-state-manager verifier, the TokenAuthority gate on "mint" (burn and
create_token keep it — with a severance pin test), the TokenSDK direct-mint
producer (now a named refusal; its only caller chain was the dBTC deposit
completion, whose success was already structurally impossible against the
builtin accepting arm), and the env-gated dev-seed ERA mint. A second channel
inside the operation is now unrepresentable, which retired the arm's
empty-field guard and its test.

The producer

token.mint follows the frozen acyclic order — resolve → refuse builtins →
load the EXACT committed policy bytes (verified against their own commit) →
run core's parse_issuance_policy + check_issuance_permitted pre-flight →
require this wallet's key among the policy's signers with a locally
satisfiable threshold → freeze the Mint (canonical token id, so a mint by
ticker and one by id commit IDENTICAL bytes) → derive the digest → build the
0x0029 body at the TARGET position → sign → bundle → admission. The evidence
bytes freeze in the same transaction as the advance and the pending admission;
the route reports success only after ECON_ADMITTED.

admitted_self_loop_operation gains a facts_for_position closure (runs
after the target position and operation digest are fixed, before anything
durable) instead of hardcoding CreditSourceFacts::None — one admission
pipeline, no sibling.

The accepting layer's blanket refusal becomes the admission gate: a positive
non-builtin mint requires an attached DsmBacked admission bound to the exact
operation (require_attached_dsm_admission, the faucet/transfer-credit
discipline). Builtin mints stay unconditionally refused. canonical_rebuild
whitelists Mint beside FaucetClaim and credit-Transfers — without that, every
head rebuild would truncate at the first historical mint.

Three core defects this cut's tests caught in #748's verifier

  1. The write-set verifier arm did not exist. verify_operation_write_set
    had builder-side facts for issuance but no
    (AuthorizedIssuance, Mint) verification arm — every issuance witness fell
    through to "credit source kind does not match the operation". No mint could
    ever have validated. The arm now pins the shape half (exactly one balance
    credit, no consumed-source leaf, descriptor funds THE credit); the
    semantics stay in the 0x0023 provenance arm. One feat(economic): class 0x0029 AuthorizedIssuanceV1 — the missing source predicate #748 test had pinned the
    defect as the behavior: a defined-but-unsatisfied predicate now correctly
    fails at the PROVENANCE layer (unresolvable evidence) instead of the shape
    wildcard, and the test was re-cut to say so.
  2. The evidence address was the wrong form. The evidence DAG addresses
    every object by its INNER content identity (immutable_inner), and the
    resolver's fetch derives the store key from an inner address. The 0x0029
    arm and descriptor committed to the OUTER immutable_addr — so every live
    fetch missed by construction, invisible until the held-mint resume test
    fetched from a real (fake-fleet) quorum. Aligned to inner. Flag: the
    producer-less 0x0026/0x0027 DLV arms carry the identical outer-form
    mismatch; their fix belongs to the PR5/PR6 producer cuts and is recorded
    here so it is not rediscovered the hard way.
  3. The committed policy-blob tail could not be parsed (owner-ruled
    conformance repair): the SDK packer always writes the u16 allowlist count —
    an explicit zero under kind NONE — and core read no count for NONE, then
    failed closed on trailing bytes: every allowlist-free token was permanently
    unissuable. Core now parses exactly what the packer commits (NONE ⇒ count
    present and zero; INLINE ⇒ count > 0; flag must agree with payload), pinned
    by SDK→core round-trip controls for both shapes.

Tests

  • Honest mint, end to end, foreign-walked: faucet(1) → create(2) →
    mint(3) → unfenced head → a foreign verifier with cleared caches walks
    position 3 through the live 0x0023 arm, fetching the bundle by address from
    the fleet, and yields the Mint.
  • Policy shapes refuse by name at the producer: mint/burn disabled;
    allowlist excluding this device; allowlist positive control mints.
  • Atomicity: a failed evidence build leaves no advance, no fence, no
    admitted movement, no frozen artifact.
  • Held → resume: quorum dies after the staged commit; the mint, its
    pending admission and the exact evidence bytes are all durable; resume
    completes the SAME admission at the signed position with byte-identical
    evidence — nothing re-signed.
  • Consume-once, wrong signer, threshold, tampered bundle, wrong
    amount/digest/position, finite cap
    : the feat(economic): class 0x0029 AuthorizedIssuanceV1 — the missing source predicate #748 core suite, still green on
    the committed blob format.
  • No raw bypass (mutation-controlled): neutralize the accepting gate and
    the named test goes red by actually crediting 1,000 units from air;
    restored green.
  • TokenAuthority mechanism tests re-keyed to burn (which still carries the
    embedded witness) plus token_authority_does_not_gate_mint.

Stale text

Every "class 0x0029 is not written / does not exist yet" claim is corrected —
the route stub, the accepting-layer comments and errors, credit.rs's
"still reserved" doc, economic/mod.rs's "no encoder" doc, a fused variant
doc in issuance.rs, and the dead AdmissibleIssuancePolicy struct is
deleted.

Verification (exact gate commands, pinned 1.98.0)

workspace  --release   3925 passed / 0 failed across 72 suites, exit 0
node       --release   270 passed / 0 failed, exit 0
make lint              exit 0
production safety      exit 0

…mint channel

The producer half of the 0x0029 core/producer split. After this cut, the only
way a positive user-token Mint enters canonical DeviceState is through an
economic admission whose CreditSource is AuthorizedIssuance and whose
immutable 0x0029 evidence independently proves the committed policy
authorized that exact asset, amount, recipient lineage/device, operation
digest, and write-once economic position.

Operation::Mint is now {amount, token_id (CANONICAL id), policy_commit,
message}. The legacy authorization channel is deleted outright: the
authorized_by/proof_of_authorization wire fields (tag-4 layout change under
the beta wipe), the mint|v2| verifiers in transition.rs and
token_state_manager, TokenAuthority's gating of "mint" (burn/create_token
keep it, with a severance pin test), the TokenSDK direct-mint arm (named
refusal; its only caller was the dBTC deposit completion, whose success was
already structurally impossible), and the dev-seed ERA mint. A second channel
inside the operation is unrepresentable, not merely forbidden.

token.mint follows the frozen acyclic order: resolve -> refuse builtins ->
load the exact committed policy bytes -> core pre-flight
(parse_issuance_policy + check_issuance_permitted) -> own key among the
policy's signers, locally satisfiable threshold -> freeze the Mint -> digest
-> 0x0029 body at the TARGET position -> sign -> bundle -> admission.
admitted_self_loop_operation gains facts_for_position; the evidence freezes
in the accept transaction; success only after ECON_ADMITTED. The accepting
layer gates positive non-builtin mints on require_attached_dsm_admission;
canonical_rebuild whitelists Mint so head rebuilds no longer truncate at the
first historical mint.

Three defects in the merged #748 verifier, all found by this cut's own tests
and none by #748's green suite: (1) verify_operation_write_set had no
(AuthorizedIssuance, Mint) arm — no issuance witness could ever have
validated; the defined-but-unsatisfied-predicate test was re-cut to the
provenance layer where the refusal now correctly lives; (2) the evidence
address used the outer immutable_addr while the whole evidence DAG and the
resolver fetch path use the inner immutable_inner — every live fetch missed
by construction (the producer-less 0x0026/0x0027 arms carry the identical
mismatch; their fix belongs to the PR5/PR6 producer cuts); (3) core's
policy-blob parser refused the SDK packer's always-present u16 allowlist
count as trailing bytes, leaving every allowlist-free token permanently
unissuable — core now parses the committed format, pinned by SDK-to-core
round-trip controls for both allowlist shapes.

Producer suite: honest mint e2e foreign-walked through the live 0x0023 arm;
policy shapes refused by name with an allowlist positive control; atomicity
(a failed evidence build leaves nothing durable); held-then-resume completing
the same admission with byte-identical evidence. Mutation control: the
accepting gate neutralized -> the named test goes red by actually crediting
1,000 units from air -> restored.

Boards on the final tree: workspace --release 3925/0 across 72 suites
(REAL_EXIT=0), node --release 270/0, make lint exit 0, production safety
exit 0.
@cryptskii
cryptskii merged commit 0efa976 into main Aug 30, 2026
18 checks passed
@cryptskii
cryptskii deleted the feat/0x0029-issuance-producer branch August 30, 2026 20:12
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.

1 participant