Skip to content

Commit 8aa4e8f

Browse files
Copilotmaxtropetsachamayou
authored
Extract describe_cose_receipt_v1 into reusable API (#7700)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: maxtropets <16566519+maxtropets@users.noreply.github.com> Co-authored-by: Max <maxtropets@microsoft.com> Co-authored-by: Amaury Chamayou <amchamay@microsoft.com> Co-authored-by: achamayou <4016369+achamayou@users.noreply.github.com>
1 parent 7ad876c commit 8aa4e8f

File tree

5 files changed

+44
-25
lines changed

5 files changed

+44
-25
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
### Added
1313

14+
- Added `ccf::describe_cose_receipt_v1(receipt)` to obtain COSE receipts with Merkle proof in unprotected header for non-signature TXs, and empty unprotected header for signature TXs (#7700).
1415
- `NetworkIdentitySubsystemInterface` now exposes `get_trusted_keys()`, returning all trusted network identity keys as a `TrustedKeys` map (#7690).
1516

1617
### Changed

include/ccf/receipt.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,10 +156,13 @@ namespace ccf
156156
using SerialisedCoseEndorsement = std::vector<uint8_t>;
157157
using SerialisedCoseSignature = std::vector<uint8_t>;
158158
using SerialisedCoseEndorsements = std::vector<SerialisedCoseEndorsement>;
159+
using SerialisedCoseReceipt = std::vector<uint8_t>;
159160
std::optional<SerialisedCoseEndorsements> describe_cose_endorsements_v1(
160161
const TxReceiptImpl& receipt);
161162
std::optional<SerialisedCoseSignature> describe_cose_signature_v1(
162163
const TxReceiptImpl& receipt);
164+
std::optional<SerialisedCoseReceipt> describe_cose_receipt_v1(
165+
const TxReceiptImpl& receipt);
163166

164167
// Manual JSON serializers are specified for these types as they are not
165168
// trivial POD structs

samples/apps/logging/logging.cpp

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2178,41 +2178,23 @@ namespace loggingapp
21782178
auto get_cose_receipt = [](
21792179
ccf::endpoints::ReadOnlyEndpointContext& ctx,
21802180
ccf::historical::StatePtr historical_state) {
2181-
auto historical_tx = historical_state->store->create_read_only_tx();
2182-
21832181
assert(historical_state->receipt);
2184-
auto signature = describe_cose_signature_v1(*historical_state->receipt);
2185-
if (!signature.has_value())
2186-
{
2187-
ctx.rpc_ctx->set_error(
2188-
HTTP_STATUS_NOT_FOUND,
2189-
ccf::errors::ResourceNotFound,
2190-
"No COSE signature available for this transaction");
2191-
return;
2192-
}
2193-
auto proof = describe_merkle_proof_v1(*historical_state->receipt);
2194-
if (!proof.has_value())
2182+
auto cose_receipt =
2183+
describe_cose_receipt_v1(*historical_state->receipt);
2184+
if (!cose_receipt.has_value())
21952185
{
21962186
ctx.rpc_ctx->set_error(
21972187
HTTP_STATUS_NOT_FOUND,
21982188
ccf::errors::ResourceNotFound,
2199-
"No merkle proof available for this transaction");
2189+
"No COSE receipt available for this transaction");
22002190
return;
22012191
}
22022192

2203-
constexpr int64_t vdp = 396;
2204-
auto inclusion_proof = ccf::cose::edit::pos::AtKey{-1};
2205-
2206-
ccf::cose::edit::desc::Value desc{inclusion_proof, vdp, *proof};
2207-
2208-
auto cose_receipt =
2209-
ccf::cose::edit::set_unprotected_header(*signature, desc);
2210-
22112193
ctx.rpc_ctx->set_response_status(HTTP_STATUS_OK);
22122194
ctx.rpc_ctx->set_response_header(
22132195
ccf::http::headers::CONTENT_TYPE,
22142196
ccf::http::headervalues::contenttype::COSE);
2215-
ctx.rpc_ctx->set_response_body(cose_receipt);
2197+
ctx.rpc_ctx->set_response_body(*cose_receipt);
22162198
};
22172199
make_read_only_endpoint(
22182200
"/log/public/cose_receipt",

src/node/historical_queries_adapter.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33

44
#include "ccf/historical_queries_adapter.h"
55

6+
#include "ccf/crypto/cose.h"
67
#include "ccf/historical_queries_utils.h"
78
#include "ccf/rpc_context.h"
89
#include "ccf/service/tables/service.h"
910
#include "crypto/cbor.h"
11+
#include "crypto/cose.h"
1012
#include "kv/kv_types.h"
1113
#include "node/rpc/network_identity_subsystem.h"
1214
#include "node/tx_receipt_impl.h"
@@ -253,6 +255,30 @@ namespace ccf
253255
{
254256
return receipt.cose_signature;
255257
}
258+
259+
std::optional<SerialisedCoseReceipt> describe_cose_receipt_v1(
260+
const TxReceiptImpl& receipt)
261+
{
262+
auto signature = describe_cose_signature_v1(receipt);
263+
if (!signature.has_value())
264+
{
265+
return std::nullopt;
266+
}
267+
268+
auto proof = describe_merkle_proof_v1(receipt);
269+
if (!proof.has_value())
270+
{
271+
// Signature TX: return COSE signature as-is, with empty UHDR
272+
return signature;
273+
}
274+
275+
auto inclusion_proof =
276+
ccf::cose::edit::pos::AtKey{ccf::cose::header::iana::INCLUSION_PROOFS};
277+
ccf::cose::edit::desc::Value desc{
278+
inclusion_proof, ccf::cose::header::iana::VDP, *proof};
279+
280+
return ccf::cose::edit::set_unprotected_header(*signature, desc);
281+
}
256282
}
257283

258284
namespace ccf::historical

tests/e2e_logging.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -910,10 +910,17 @@ def test_cbor_receipts(network, args):
910910
log_capture=[], # Do not emit raw binary to stdout
911911
)
912912
if r.status_code == http.HTTPStatus.OK:
913-
found_receipt = True
914913
cose_receipt = r.body.data()
915914
uhdr = cbor2.loads(cose_receipt).value[1]
916-
proofs = uhdr[396][-1]
915+
VDP_KEY = 396
916+
if VDP_KEY not in uhdr:
917+
# Signature TX: valid receipt with empty UHDR, skip to next seqno
918+
LOG.debug(
919+
f"Transaction {txid} is a signature TX (empty UHDR), skipping"
920+
)
921+
break
922+
found_receipt = True
923+
proofs = uhdr[VDP_KEY][-1]
917924
assert len(proofs) > 0, "No Merkle proofs found in receipt"
918925

919926
r = client.get(

0 commit comments

Comments
 (0)