feat(economic): provenance wire freeze — inline credit sources in 0x001D - #726
Merged
Conversation
Step 3 sub-PR 3.2 of 6. Exact field tables for the six credit-source
descriptors and for the transition witness that carries them. Encoders and
decoders only; no acceptance semantics.
The shape
---------
Credit-source CCBs are INLINE inside the witness; only bulky proof material is
content-addressed:
manifest -> witness -> inline CreditSource descriptors -> heavy evidence
Addressing the descriptors themselves would mean fetching an object just to
learn that mutation 3 is funded by mutation 1 — absurd for SameTransitionMove,
which is intra-transition by definition. So the witness commits directly to
which source type funds which credit, and the small descriptors carry
addresses only where the evidence behind them is heavy.
0x001C.provenance_evidence_addrs becomes DERIVED and non-authoritative:
manifest.provenance_evidence_addrs
== sort_unique(every direct external evidence address referenced by
witness.credit_sources)
Mismatch rejects. It is a publication and durability index, not a second
description of provenance — the semantics live in the inline sources, so
there is one description and an index over it rather than two things that can
disagree. SameTransitionMove references nothing external, so a transition
funded entirely by internal moves has an empty index, correctly.
The bijection is fully structural
---------------------------------
is_positive_credit() reads ONE mutation — post amount > pre amount, absent
counting as zero. No identity, no tree, no fetch. So all four frozen rules are
decidable from the witness bytes before retrieving a single blob:
credit_sources strictly ascending by credit_mutation_index
exactly one source for every positive credit
no duplicate credit_mutation_index (implied by strict ascent)
no source for a non-credit mutation
Settlement receipts and consumed-source markers report no amount: they are
RECORDS, not credits. Demanding a funding source for a bookkeeping entry
would be incoherent.
Deliberate omissions
--------------------
No descriptor carries source_id — every SourceId is derived from
authenticated facts, and a descriptor able to supply one could name a source
it never established. None carries policy_commit or amount either; those live
in the credit mutation being pointed at, and duplicating them creates a second
place for one fact to disagree with itself. 0x0026 carries x but not
receipt_id, which derives from (vault_id, x).
0x0029 stays reserved
---------------------
Its field table would encode WHO MAY ISSUE WHAT, and this protocol has no
authenticated issuance predicate — the same absence behind the builtin
ERA/dBTC mint repair, where the accepting layer refuses builtin issuance
precisely because nothing exists to validate against. Nothing is blocked:
0x0023 references its authorization by ADDRESS, so the credit source is
complete on the wire while the object behind it stays undefined until the
predicate does.
0x001D and 0x0023-0x0028 were promoted out of ccb::reserved into ccb::class.
Having to edit the reserved-set test is the deliberate-diff signal that
reservation exists to produce.
Decoders
--------
Rebuilt through the encoder's own validating constructors, so a zero-amount
balance leaf and a forged receipt_id both FAIL to decode. A laxer decoder
would be a second, weaker definition of the protocol. Reuses
ccb::decode::Cursor (now pub(crate), plus peek_class for heterogeneous inline
sequences) rather than a second reader whose truncation semantics could drift.
Verification
------------
16 new tests. Five gates mutation-proven, each with a positive control
asserting the mutation edit actually landed before the run:
every-credit-is-funded removed -> a_credit_with_no_source_is_refused
no-source-for-non-credit -> a_source_for_a_debit_is_provenance_for_nothing
strict ascent -> credit_sources_must_be_strictly_ascending
manifest index equality -> the_manifest_index_must_equal_what_the_
sources_reference
decoder receipt_id recompute -> a_receipt_cannot_assert_a_receipt_id_its_
contents_do_not_produce
Board: 65 suites, 3777 passed, 0 failed, 19 ignored, exit 0.
make lint: exit 0, 0 errors.
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.
Step 3, sub-PR 3.2 of 6 — the provenance wire freeze. Exact field tables for the six credit-source descriptors and for the witness that carries them. Encoders and decoders only; no acceptance semantics.
Field tables — the four marked ⚠ contain choices I made
Your spec gave
credit_mutation_indexplus...for four arms. Those fills are mine; flagging them so review lands on the invented parts rather than the dictated ones.credit_mutation_index0x0023AuthorizedIssuanceissuance_authorization_addr0x0024SameTransitionMovedebit_mutation_index0x0025ValidatedPeerDebitpeer_genesis,peer_devid,peer_economic_position,peer_debit_mutation_index,acceptance_evidence_addr0x0026DlvReserveConsumptionvault_id,parent_sequence,x,reserve_consumption_evidence_addr0x0027ValidatedDlvSettlementPaymentvault_id,settlement_receipt_id,parent_sequence,trader_genesis,trader_devid,payment_evidence_addr0x0028VerifiedOfflineReentryprior_boundary_id,unload_boundary_id,branch_evidence_addr0x0025carries peer coordinates because a debit must be locatable in a specific position of a specific identity's lineage — "some peer debited something" is not a source.0x0028leads withprior_boundary_idbecause that is the anti-fork field: deriving from the terminal offline state instead is the inflation bug where two forks of one branch both reenter and 100 exported returns as 130.Deliberate omissions, all three arms-wide
source_id. EverySourceIdis derived from authenticated facts; a descriptor able to supply one could name a source it never established.policy_commit/amount. They live in the credit mutation being pointed at. Duplicating them creates a second place for one fact to disagree with itself.0x0026carriesx, notreceipt_id— the latter derives from(vault_id, x), and a derived name beside its own inputs is a place for the two to diverge.The bijection turned out fully structural
is_positive_credit()reads one mutation — post amount > pre amount, absent counting as zero. No identity, no tree, no fetch. So all four frozen rules are decidable from the witness bytes before retrieving a single blob, exactly as you predicted:Settlement receipts and consumed-source markers report no amount: they are records, not credits. Demanding a funding source for a bookkeeping entry would be incoherent, and treating an insertion as a credit would have forced exactly that.
The manifest index is now derived
A publication and durability index, not a second description of provenance.
SameTransitionMovereferences nothing external, so a transition funded entirely by internal moves has an empty index — correct, not missing.One narrowing:
0x0029stays reservedOverrule this if you disagree. Its field table would encode who may issue what, and this protocol has no authenticated issuance predicate — the same absence behind the builtin ERA/dBTC mint repair, where the accepting layer refuses builtin issuance precisely because nothing exists to validate against. Nothing is blocked:
0x0023references its authorization by address, so the credit source is complete on the wire while the object behind it stays undefined until the predicate does.0x001Dand0x0023–0x0028were promoted out ofccb::reservedintoccb::class. Having to edit the reserved-set test is the deliberate-diff signal that reservation exists to produce.Decoders
Rebuilt through the encoder's own validating constructors, so a zero-amount balance leaf and a forged
receipt_idboth fail to decode — a laxer decoder would be a second, weaker definition of the protocol. Reusesccb::decode::Cursor(nowpub(crate), pluspeek_classfor heterogeneous inline sequences) rather than a second reader whose truncation semantics could drift from the first.Verification
16 new tests. Five gates mutation-proven, each with a positive control asserting the mutation edit actually landed before the run — the control added after 3.1, where a mutation reported green against a control test an earlier edit had silently deleted:
a_credit_with_no_source_is_refuseda_source_for_a_debit_is_provenance_for_nothingcredit_sources_must_be_strictly_ascendingthe_manifest_index_must_equal_what_the_sources_referencereceipt_idrecomputationa_receipt_cannot_assert_a_receipt_id_its_contents_do_not_produceScoring the work surfaced a gap I closed rather than logged: nothing crossed the 3.2 wire object to the 3.1 verifier, so a witness could have round-tripped carrying unusable Merkle paths.
a_witness_that_survived_the_wire_still_verifies_against_a_real_treebuilds a real tree, encodes, decodes, and confirms the decoded witness still recomputes its post-root.Board — both halves, on this exact tree