feat(economic): the 0x0029 issuance producer — token.mint is the one mint channel - #751
Merged
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The producer half of the
0x0029core/producer split. After this cut thedefinition-of-done statement is literally true:
One authorization channel
Operation::Mintis 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_authorizationfields(wire tag 4 layout change, beta wipe), the
mint|v2|transition verifier, thetoken-state-manager verifier, the
TokenAuthoritygate on"mint"(burn andcreate_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.mintfollows 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_permittedpre-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
0x0029body at the TARGET position → sign → bundle → admission. The evidencebytes freeze in the same transaction as the advance and the pending admission;
the route reports success only after ECON_ADMITTED.
admitted_self_loop_operationgains afacts_for_positionclosure (runsafter the target position and operation digest are fixed, before anything
durable) instead of hardcoding
CreditSourceFacts::None— one admissionpipeline, 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-creditdiscipline). Builtin mints stay unconditionally refused.
canonical_rebuildwhitelists 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
verify_operation_write_sethad builder-side facts for issuance but no
(AuthorizedIssuance, Mint)verification arm — every issuance witness fellthrough 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.
every object by its INNER content identity (
immutable_inner), and theresolver's fetch derives the store key from an inner address. The 0x0029
arm and descriptor committed to the OUTER
immutable_addr— so every livefetch 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.
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
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.
allowlist excluding this device; allowlist positive control mints.
admitted movement, no frozen artifact.
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.
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.
the named test goes red by actually crediting 1,000 units from air;
restored green.
TokenAuthoritymechanism tests re-keyed to burn (which still carries theembedded 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 variantdoc in
issuance.rs, and the deadAdmissibleIssuancePolicystruct isdeleted.
Verification (exact gate commands, pinned 1.98.0)