Skip to content

Extract describe_cose_receipt_v1 into reusable API#7700

Merged
maxtropets merged 13 commits intomainfrom
copilot/fix-6154722-180112558-2b9469fb-3f60-424d-b35f-2deb1fe55701
Mar 5, 2026
Merged

Extract describe_cose_receipt_v1 into reusable API#7700
maxtropets merged 13 commits intomainfrom
copilot/fix-6154722-180112558-2b9469fb-3f60-424d-b35f-2deb1fe55701

Conversation

Copy link
Contributor

Copilot AI commented Mar 4, 2026

The COSE receipt construction logic was inlined in the logging sample app instead of being a first-class library function alongside the other describe_* helpers.

Changes

  • include/ccf/receipt.h: Add SerialisedCoseReceipt type alias (grouped with SerialisedCoseSignature/SerialisedCoseEndorsement) and declare describe_cose_receipt_v1.
  • src/node/historical_queries_adapter.cpp: Implement describe_cose_receipt_v1 using named constants ccf::cose::header::iana::VDP and ccf::cose::header::iana::INCLUSION_PROOFS from src/crypto/cose.h. For non-signature TXs, the Merkle proof is embedded in the UHDR; for signature TXs, the COSE signature is returned as-is with an empty UHDR.
  • samples/apps/logging/logging.cpp: Replace inlined receipt construction with a call to describe_cose_receipt_v1.
  • tests/e2e_logging.py: Fix test_cbor_receipts to check for VDP key presence before accessing it; skip signature TXs with empty UHDR and keep iterating to find a non-signature TX with Merkle proof.

Usage

auto cose_receipt = ccf::describe_cose_receipt_v1(*historical_state->receipt);
if (!cose_receipt.has_value())
{
  ctx.rpc_ctx->set_error(HTTP_STATUS_NOT_FOUND, ...);
  return;
}
ctx.rpc_ctx->set_response_body(*cose_receipt);
Original prompt

Background

The samples/apps/logging/logging.cpp file has a get_cose_receipt endpoint handler (around line 2142) that manually constructs a COSE receipt by:

  1. Calling describe_cose_signature_v1(*historical_state->receipt) to get the signature
  2. Calling describe_merkle_proof_v1(*historical_state->receipt) to get the proof
  3. Constructing a ccf::cose::edit::desc::Value with pos::AtKey{-1}, key 396, and the proof
  4. Calling ccf::cose::edit::set_unprotected_header(*signature, desc) to produce the final receipt bytes

This logic should be extracted into a reusable describe_cose_receipt function, following the same pattern as the existing describe_receipt_v1, describe_receipt_v2, describe_merkle_proof_v1, describe_cose_signature_v1, and describe_cose_endorsements_v1 functions.

Requirements

1. Add describe_cose_receipt function

In include/ccf/receipt.h (around line 162, after the describe_cose_signature_v1 declaration):
Add a new function declaration:

  using SerialisedCoseReceipt = std::vector<uint8_t>;
  std::optional<SerialisedCoseReceipt> describe_cose_receipt(
    const TxReceiptImpl& receipt);

In src/node/historical_queries_adapter.cpp (after describe_cose_signature_v1, around line 254):
Add the implementation that mirrors the logic from the logging app's get_cose_receipt:

  std::optional<SerialisedCoseReceipt> describe_cose_receipt(
    const TxReceiptImpl& receipt)
  {
    auto signature = describe_cose_signature_v1(receipt);
    if (!signature.has_value())
    {
      return std::nullopt;
    }

    auto proof = describe_merkle_proof_v1(receipt);
    if (!proof.has_value())
    {
      return std::nullopt;
    }

    constexpr int64_t vdp = 396;
    auto inclusion_proof = ccf::cose::edit::pos::AtKey{-1};
    ccf::cose::edit::desc::Value desc{inclusion_proof, vdp, *proof};

    return ccf::cose::edit::set_unprotected_header(*signature, desc);
  }

Note: src/node/historical_queries_adapter.cpp already includes "ccf/historical_queries_adapter.h" which in turn includes headers that bring in the COSE edit types. You will need to add #include "ccf/crypto/cose.h" to src/node/historical_queries_adapter.cpp if it's not already present (check the existing includes).

2. Update samples/apps/logging/logging.cpp

Replace the inlined logic in the get_cose_receipt lambda (around lines 2142-2185) with a call to describe_cose_receipt. The lambda should become:

      auto get_cose_receipt = [](
                                ccf::endpoints::ReadOnlyEndpointContext& ctx,
                                ccf::historical::StatePtr historical_state) {
        assert(historical_state->receipt);
        auto cose_receipt =
          describe_cose_receipt(*historical_state->receipt);
        if (!cose_receipt.has_value())
        {
          ctx.rpc_ctx->set_error(
            HTTP_STATUS_NOT_FOUND,
            ccf::errors::ResourceNotFound,
            "No COSE receipt available for this transaction");
          return;
        }

        ctx.rpc_ctx->set_response_status(HTTP_STATUS_OK);
        ctx.rpc_ctx->set_response_header(
          ccf::http::headers::CONTENT_TYPE,
          ccf::http::headervalues::contenttype::COSE);
        ctx.rpc_ctx->set_response_body(*cose_receipt);
      };

Note: the auto historical_tx = historical_state->store->create_read_only_tx(); line that was previously in the lambda is unused and should be removed.

3. Add a CHANGELOG entry

Add a line under the ### Added section of the current [7.0.0-dev12] entry in CHANGELOG.md:

- Added `ccf::describe_cose_receipt(receipt)` to produce a complete COSE receipt from a `TxReceiptImpl`, combining signature and merkle proof.

Guidelines

  • Keep comments and implementation concise.
  • Follow the existing code style (2-space indentation in C++, same formatting patterns).
  • The new function should sit alongside the other describe_* functions — same files, same namespace.

This pull request was created from Copilot chat.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@maxtropets maxtropets changed the title [WIP] Add describe_cose_receipt alongside existing v1/v2 describe functions and reuse it in samples/logging [WIP] Add describe_cose_receipt API Mar 4, 2026
Co-authored-by: maxtropets <16566519+maxtropets@users.noreply.github.com>
Copilot AI changed the title [WIP] Add describe_cose_receipt API Extract describe_cose_receipt from logging app into reusable API Mar 4, 2026
…type alias

Co-authored-by: maxtropets <16566519+maxtropets@users.noreply.github.com>
Copilot AI changed the title Extract describe_cose_receipt from logging app into reusable API Extract describe_cose_receipt into reusable API Mar 4, 2026
@maxtropets maxtropets marked this pull request as ready for review March 4, 2026 14:00
@maxtropets maxtropets requested a review from a team as a code owner March 4, 2026 14:00
Copilot AI review requested due to automatic review settings March 4, 2026 14:00
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extracts COSE receipt construction (COSE signature + Merkle proof combined into a single COSE_Sign1 with unprotected header fields) into a reusable ccf::describe_cose_receipt() helper, and updates the logging sample app to use it.

Changes:

  • Added SerialisedCoseReceipt and declared describe_cose_receipt(const TxReceiptImpl&) in the public receipt API.
  • Implemented describe_cose_receipt in the historical queries adapter using named COSE header constants (VDP, INCLUSION_PROOFS) instead of magic numbers.
  • Simplified the logging sample’s /log/public/cose_receipt handler by calling describe_cose_receipt and removing now-inlined logic.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
include/ccf/receipt.h Exposes the new SerialisedCoseReceipt alias and describe_cose_receipt declaration as part of the public receipt helpers.
src/node/historical_queries_adapter.cpp Implements describe_cose_receipt by combining the existing COSE signature and Merkle proof helpers and setting the appropriate COSE unprotected header fields.
samples/apps/logging/logging.cpp Replaces manual receipt construction with the new helper for the COSE receipt endpoint.
CHANGELOG.md Documents the addition of the new public helper API.

…signature TXs; update changelog

Co-authored-by: maxtropets <16566519+maxtropets@users.noreply.github.com>
@maxtropets
Copy link
Collaborator

@copilot e2e test test_cbor_receipts fails on

                if r.status_code == http.HTTPStatus.OK:
                    found_receipt = True
                    cose_receipt = r.body.data()
                    uhdr = cbor2.loads(cose_receipt).value[1]
                    proofs = uhdr[396][-1]  # THROWS
                    assert len(proofs) > 0, "No Merkle proofs found in receipt"

because it now returns a receipt for a signature TX with an empty path. I want to keep returning that, as I consider this a valid receipt, but the test should check for uhdr key presence and keep iterating until it finds non-signature TX.

…ate to find non-signature TX

Co-authored-by: maxtropets <16566519+maxtropets@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Mar 4, 2026

@copilot e2e test test_cbor_receipts fails on

                if r.status_code == http.HTTPStatus.OK:

...

Fixed in 029cf52. The test now checks for the VDP key (396) in UHDR before accessing it. If the key is absent (signature TX with empty UHDR), it breaks the inner retry loop and continues the outer seqno loop to find a non-signature TX with Merkle proof.

…istency

Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
Copilot AI changed the title Extract describe_cose_receipt into reusable API Extract describe_cose_receipt_v1 into reusable API Mar 5, 2026
@maxtropets maxtropets enabled auto-merge (squash) March 5, 2026 11:46
@maxtropets maxtropets merged commit 8aa4e8f into main Mar 5, 2026
17 checks passed
@maxtropets maxtropets deleted the copilot/fix-6154722-180112558-2b9469fb-3f60-424d-b35f-2deb1fe55701 branch March 5, 2026 12:28
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.

4 participants