unicitynetwork / unicitynetwork/state-transition-sdk-java
Commit the round reference time in the SMT leaf value
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 3
- Forks
- 0
- Avg merge
- 5h 14m
- Merged PRs (30d)
- 1
Description
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 valueSHA-256(CBOR([txhash, τ]))instead of from the bare transaction hash. A mismatch fails the proof, which is the yellowpaper'srequire 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
- Verification of a token never reads
τfrom a unicity certificate. - A token whose carried
τdoes not reproduce the certified leaf value is rejected. - A proof fetched against a later root still verifies a transition certified earlier.
- Leaf-value encoding pinned by a test vector shared with the other implementations.
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
| Repository | Reference time in the leaf value | Request timeout |
|---|---|---|
aggregator-go |
unicitynetwork/aggregator-go#176 | unicitynetwork/aggregator-go#177 |
rugregator |
ristik/rugregator#3 | ristik/rugregator#4 |
bft-core (branch l1) |
unicitynetwork/bft-core#23 (RSMT), unicitynetwork/bft-core#24 (ZK) | out of scope |
state-transition-sdk-js |
unicitynetwork/state-transition-sdk-js#144 | unicitynetwork/state-transition-sdk-js#145 |
state-transition-sdk-java |
unicitynetwork/state-transition-sdk-java#81 | unicitynetwork/state-transition-sdk-java#82 |
state-transition-sdk-rust |
unicitynetwork/state-transition-sdk-rust#16 | unicitynetwork/state-transition-sdk-rust#17 |
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the listed touch points, especially api/InclusionProof.java, transaction/verification/InclusionProofVerificationRule.java, transaction/Token.java, and util/InclusionProofUtils.java, then trace how τ is currently obtained from proofs. Use the supplied CBOR/SHA-256 test vector and wire formats as the interoperability reference; done means the four acceptance criteria pass without reading τ from a unicity certificate.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- api, backend, cryptography
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100