Skip to content

Add registration_tx unprotected header to COSE receipts#374

Closed
Copilot wants to merge 2 commits intomainfrom
copilot/fix-receipt-transaction-error
Closed

Add registration_tx unprotected header to COSE receipts#374
Copilot wants to merge 2 commits intomainfrom
copilot/fix-receipt-transaction-error

Conversation

Copy link
Contributor

Copilot AI commented Feb 26, 2026

Receipts returned by /entries/{txid} and /entries/{txid}/statement contained the CCF signing transaction ID rather than the registration transaction ID the client submitted. This made it impossible to correlate a receipt with the ledger entry used to retrieve it.

Changes

  • app/src/cose.h — Adds COSE_HEADER_PARAM_REGISTRATION_TX = "registration_tx" constant.
  • app/src/main.cpp — Rewrites get_cose_receipt() to accept the entry's registration_tx txid and manually constructs the receipt CBOR (QCBOR) so both the inclusion proof and registration_tx land in a single unprotected header pass. Both GET /entries/{txid} and GET /entries/{txid}/statement pass historical_state->transaction_id.to_str().
  • pyscitt/pyscitt/crypto.py — Adds REGISTRATION_TX = "registration_tx" constant for Python clients.
  • test/test_verify.py — Adds unit tests covering receipt structure, cbor_to_printable rendering, and preservation through embed_receipt_in_cose.

Result

A receipt for entry 350.9219 now includes:

{
  396: { -1: [<inclusion_proof>] },   ; verifiable-proofs
  "registration_tx": "350.9219"        ; NEW
}
Original prompt

This section details on the original issue you should resolve

<issue_title>Transaction that is in the receipt does not correspond to the actual submission</issue_title>
<issue_description>If we look into the ledger files, transactions are identified correctly, i.e.: 350.9219 which actually allows us to get that entry by fetching it from the ledger URL https://ledger/entries/350.9219 but the receipt in the fetched entry says the transaction is 350.9220 which would not be a valid submission.
</issue_description>

<agent_instructions>Update both responses for /entries/{txid} and /entries/{txid}/statement APIs and update returned receipt so that it gets an unprotected cose header value registration_tx: {txid}. This should be implemented by extending the get_cose_receipt method. Also add unit tests to verify the behavior and run them (see DEVELOPMENT.md to understand how)</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@ivarprudnikov This transaction id marks the signature transaction and not the entry transaction that happened prior. It works as expected but is a bit misleading as it looks like the transaction identifier which you can use to pull down the entry/receipt/statement which is not correct. @ivarprudnikov A suggestion would be to add this data to the unprotected headers of the receipt so that both responses `/entries/{txid}` and `/entries/{txid}/statement` contain receipt with this value. Given that the caller already provides the transaction in the request it could be used as a value to embed. Unprotected header could be called `registration_tx` and the value would be a tstr representing a transaction, e.g. `7.145`.

🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Co-authored-by: ivarprudnikov <1052642+ivarprudnikov@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix transaction mismatch in receipt and actual submission Add registration_tx unprotected header to COSE receipts Feb 26, 2026
// e.g. "7.145". This allows clients to correlate a receipt with the ledger
// entry they retrieved it from.
static constexpr const char* COSE_HEADER_PARAM_REGISTRATION_TX =
"registration_tx";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The registration tx is already embedded in the ccf-leaf of the receipt: https://www.ietf.org/archive/id/draft-ietf-scitt-receipts-ccf-profile-00.html#section-2.2, in the internal-evidence field (https://ccf.dev/main/use_apps/verify_tx.html#commit-evidence), and is covered by the signature/authenticated.

We should avoid introducing an un-authenticated copy of this value.

Copy link
Member

@ivarprudnikov ivarprudnikov Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@achamayou which CCF version will have it? At the moment we have the issue of only seeing the signature transaction which cannot be used to fetch entries or statements, I believe this was your suggestion a couple of months ago to add it via unprotected headers. There is a practical issue of linking the two together, where one is the transaction under which the entry is stored and the other is the actual signature. If you look into the transparent statement you would like to find it in the ledger, and if you were to follow signature transaction the entry will not exist there, slightly misleading, then there is an impossibility to construct a url back to the ledger from the TS, for instance to be able to log that information.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The commit evidence has been in CCF since 2.0 I think, it's been exposed in the leaf element of the proof since we added COSE Receipts:

>>> cbor2.loads(cbor2.loads(uhdr[394][0]).value[1][396][-1][0])[1][1]
'ce:458.12440:858e0a6db787c523ac940e640a781efcd0635a5e22bdf186d46acef29759f4c1'

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested that exposing a Locations list in the unprotected header for fetching is fine, because it's not an authoritative claim about the statement's content, it just says "you can fetch this again here". It can point to various caches etc if necessary.

@ivarprudnikov
Copy link
Member

Closing this. The value is already present in the vdp encoded proof but is slightly hard to read. Instead the txid will be exposed via the receipt verification result, to enforce authentication of the value before trusting it.

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.

Hard to read entry transaction embedded in the receipt proof and the other visible receipt transaction points only to the signature transaction

3 participants