Background
Yellowpaper commit 407b07f ("commit reference time in the SMT leaf value") changes the Unicity Service SMT leaf value from the transaction hash alone to H(txhash, τ), where τ is the reference time of the round the request was validated in (UC.IR.t).
Predicate evaluation already takes τ as an argument, but τ was recoverable only from the inclusion proof, as UC.IR.t. The SMT is append-only, so a leaf can be certified afresh against any later root, and a later proof carries a later round's IR.t. Reference time was therefore a property of the proof rather than of the leaf, and re-presenting a leaf changed the predicate evaluation outcome. Binding τ into the leaf value fixes the value the transition was validated under, for any proof of that leaf.
τ cannot go into txhash: it is chosen by the Aggregator after the sender has signed. The leaf value is the first field written once τ is known, and it is the one field re-presentation preserves verbatim.
Affected yellowpaper sections: platform.tex (Unicity Service Request, request validation, SMT leaf structure, inclusion proof), execution-layer.tex (mint/transfer transaction structure and verification, sec:time-extraction, Unicity Service processing), appendix-hashtrees.tex (ZK-compressed consistency proof).
Normative encodings
These must be byte-identical across aggregator-go, rugregator, bft-core, and the three SDKs.
| Symbol |
Meaning |
Type |
τ |
Round reference time: UC.InputRecord.Timestamp of the round the leaf was certified in. BFT Core requires it to equal the previous round's Unicity Seal timestamp. |
uint64, Unix seconds |
τ_Q |
Exclusive request timeout, chosen by the sender |
uint64, Unix seconds |
Transaction hash. τ_Q is the last element of the transaction's own deterministic CBOR array, so
txhash = SHA-256( <transaction CBOR, ending in τ_Q> )
is unchanged in form and is exactly appendix-token.tex's SHA-256(CBOR(…, τ_Q)). Keeping τ_Q inside the transaction rather than beside it (as the appendix's four-element certified transaction does) means the request, the certified transaction and the hash preimage cannot disagree about it, and the unlock script signs it along with the rest of the transaction.
SMT leaf value.
v = SHA-256( CBOR([ txhash, τ ]) ) // 32 raw bytes
txhash enters as a CBOR byte string of the raw 32-byte digest (no algorithm-id prefix), τ as a CBOR unsigned integer. This is the same construction already used for StateID and for the signature preimage.
Shared test vector:
txhash = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
τ = 1755000000
preimage = 825820000102…1e1f 1a689b2cc0
v = 0235bd52cfa10c9785dfa01942bc396f201fe715dbc3896ee117a97e895e1e36
Certification request. CertificationData carries τ_Q between the transaction hash and the witness, matching Q = (ρ, sthash, txhash, τ_Q, u). The aggregator never sees the transaction, so it needs the value explicitly to enforce expiry.
Inclusion proof. Carries τ. A verifier cannot recover it from the certificate chain, because an aggregator serves proofs against the current certified root rather than the one the leaf was created under.
Certified transaction. [ transaction, τ, inclusionProof ]. τ_Q is already inside element 0.
Admission rule. A request may be inserted only in a round whose reference time satisfies τ < τ_Q.
Scope in this SDK
Reference time is carried, not read off the proof
τ used to be recoverable as inclusionProof.unicityCertificate.inputRecord.timestamp. That is correct only for the proof issued in the transition's own round: the SMT is append-only, an aggregator may serve a proof against any later certified root, and such a proof carries a later round's IR.t. τ becomes a field of the certified transaction, fixed at the moment the transaction is bound to its first proof, and every later verification uses the carried value.
Changes
- Inclusion proof gains the round reference time returned by the aggregator, so a client can learn
τ when it first fetches a proof.
- Inclusion proof verification takes
τ as an argument and reconstructs the root from the leaf value SHA-256(CBOR([txhash, τ])) instead of from the bare transaction hash. A mismatch fails the proof, which is the yellowpaper's require H(txhash, τ) = π.v.
- Certified mint and transfer transactions carry
τ and expose it. Callers that bind a transaction to a proof take τ from the proof they are binding to; callers that re-verify an existing token use the carried τ.
- Predicate evaluation receives the carried
τ rather than one derived from whichever proof is attached.
Wire format
InclusionProof [version, certificationData, τ, inclusionCertificate, unicityCertificate]
certified transaction [transaction, τ, inclusionProof]
Notes
Non-backward-compatible. There is no mainnet, so no migration path is required, but tokens serialised before the change will not verify afterwards.
Acceptance criteria
Touch points
api/InclusionProof.java, api/CertificationData.java, transaction/verification/InclusionProofVerificationRule.java, transaction/CertifiedMintTransaction.java, transaction/CertifiedTransferTransaction.java, transaction/Token.java, util/InclusionProofUtils.java, predicate/verification/PredicateVerifierService.java.
Cross-repository change. The same encoding lands in aggregator-go, rugregator, bft-core (branch l1, consistency proof only) and the three state transition SDKs, and is switched on across the deployment in one coordinated step. Cross-implementation test vectors are part of the work.
Companion issues
Background
Yellowpaper commit
407b07f("commit reference time in the SMT leaf value") changes the Unicity Service SMT leaf value from the transaction hash alone toH(txhash, τ), whereτis the reference time of the round the request was validated in (UC.IR.t).Predicate evaluation already takes
τas an argument, butτwas recoverable only from the inclusion proof, asUC.IR.t. The SMT is append-only, so a leaf can be certified afresh against any later root, and a later proof carries a later round'sIR.t. Reference time was therefore a property of the proof rather than of the leaf, and re-presenting a leaf changed the predicate evaluation outcome. Bindingτinto the leaf value fixes the value the transition was validated under, for any proof of that leaf.τcannot go intotxhash: it is chosen by the Aggregator after the sender has signed. The leaf value is the first field written onceτis known, and it is the one field re-presentation preserves verbatim.Affected yellowpaper sections:
platform.tex(Unicity Service Request, request validation, SMT leaf structure, inclusion proof),execution-layer.tex(mint/transfer transaction structure and verification,sec:time-extraction, Unicity Service processing),appendix-hashtrees.tex(ZK-compressed consistency proof).Normative encodings
These must be byte-identical across
aggregator-go,rugregator,bft-core, and the three SDKs.τUC.InputRecord.Timestampof the round the leaf was certified in. BFT Core requires it to equal the previous round's Unicity Seal timestamp.τ_QTransaction hash.
τ_Qis the last element of the transaction's own deterministic CBOR array, sois unchanged in form and is exactly
appendix-token.tex'sSHA-256(CBOR(…, τ_Q)). Keepingτ_Qinside the transaction rather than beside it (as the appendix's four-element certified transaction does) means the request, the certified transaction and the hash preimage cannot disagree about it, and the unlock script signs it along with the rest of the transaction.SMT leaf value.
txhashenters as a CBOR byte string of the raw 32-byte digest (no algorithm-id prefix),τas a CBOR unsigned integer. This is the same construction already used forStateIDand for the signature preimage.Shared test vector:
Certification request.
CertificationDatacarriesτ_Qbetween the transaction hash and the witness, matchingQ = (ρ, sthash, txhash, τ_Q, u). The aggregator never sees the transaction, so it needs the value explicitly to enforce expiry.Inclusion proof. Carries
τ. A verifier cannot recover it from the certificate chain, because an aggregator serves proofs against the current certified root rather than the one the leaf was created under.Certified transaction.
[ transaction, τ, inclusionProof ].τ_Qis already inside element 0.Admission rule. A request may be inserted only in a round whose reference time satisfies
τ < τ_Q.Scope in this SDK
Reference time is carried, not read off the proof
τused to be recoverable asinclusionProof.unicityCertificate.inputRecord.timestamp. That is correct only for the proof issued in the transition's own round: the SMT is append-only, an aggregator may serve a proof against any later certified root, and such a proof carries a later round'sIR.t.τbecomes a field of the certified transaction, fixed at the moment the transaction is bound to its first proof, and every later verification uses the carried value.Changes
τwhen it first fetches a proof.τas an argument and reconstructs the root from the leaf valueSHA-256(CBOR([txhash, τ]))instead of from the bare transaction hash. A mismatch fails the proof, which is the yellowpaper'srequire H(txhash, τ) = π.v.τand expose it. Callers that bind a transaction to a proof takeτfrom the proof they are binding to; callers that re-verify an existing token use the carriedτ.τrather than one derived from whichever proof is attached.Wire format
Notes
Non-backward-compatible. There is no mainnet, so no migration path is required, but tokens serialised before the change will not verify afterwards.
Acceptance criteria
τfrom a unicity certificate.τdoes not reproduce the certified leaf value is rejected.Touch points
api/InclusionProof.java,api/CertificationData.java,transaction/verification/InclusionProofVerificationRule.java,transaction/CertifiedMintTransaction.java,transaction/CertifiedTransferTransaction.java,transaction/Token.java,util/InclusionProofUtils.java,predicate/verification/PredicateVerifierService.java.Cross-repository change. The same encoding lands in
aggregator-go,rugregator,bft-core(branchl1, consistency proof only) and the three state transition SDKs, and is switched on across the deployment in one coordinated step. Cross-implementation test vectors are part of the work.Companion issues
aggregator-gorugregatorbft-core(branchl1)state-transition-sdk-jsstate-transition-sdk-javastate-transition-sdk-rust