Skip to content

Commit 501d605

Browse files
authored
[release/3.x] Cherry pick: Add populate_service_endorsements to public headers (#5242) (#5244)
1 parent e99a232 commit 501d605

7 files changed

+187
-3
lines changed

CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

8+
## [3.0.11]
9+
10+
[3.0.11]: https://github.com/microsoft/CCF/releases/tag/ccf-3.0.11
11+
12+
## Added
13+
14+
- Added `ccf::historical::populate_service_endorsements` to public C++ API, allowing custom historical endpoints to do the same work as adapters.
15+
816
## [3.0.10]
917

1018
[3.0.10]: https://github.com/microsoft/CCF/releases/tag/ccf-3.0.10

cmake/common.cmake

+1
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ set(CCF_ENDPOINTS_SOURCES
189189
${CCF_DIR}/src/indexing/strategies/seqnos_by_key_in_memory.cpp
190190
${CCF_DIR}/src/indexing/strategies/visit_each_entry_in_map.cpp
191191
${CCF_DIR}/src/node/historical_queries_adapter.cpp
192+
${CCF_DIR}/src/node/historical_queries_utils.cpp
192193
${CCF_DIR}/src/node/receipt.cpp
193194
)
194195

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the Apache 2.0 License.
3+
4+
#include "ccf/historical_queries_interface.h"
5+
#include "ccf/network_identity_interface.h"
6+
#include "ccf/rpc_context.h"
7+
#include "ccf/tx.h"
8+
9+
namespace ccf::historical
10+
{
11+
// Modifies the receipt stored in state to include historical service
12+
// endorsements, where required. If the state talks about a different service
13+
// identity, which is known to be a predecessor of this service (via disaster
14+
// recoveries), then an endorsement of the receipt's node certificate will be
15+
// created. This may need to use the state_cache to request additional
16+
// historical entries to construct this endorsement, and may read from the
17+
// current/latest state via tx. Returns true if the operation is complete,
18+
// though it may still have failed to produce an endorsement. Returns false if
19+
// additional entries have been requested, in which case the caller should
20+
// retry later.
21+
bool populate_service_endorsements(
22+
kv::ReadOnlyTx& tx,
23+
ccf::historical::StatePtr& state,
24+
AbstractStateCache& state_cache,
25+
std::shared_ptr<NetworkIdentitySubsystemInterface>
26+
network_identity_subsystem);
27+
}

src/node/historical_queries_adapter.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include "ccf/historical_queries_adapter.h"
55

6+
#include "ccf/historical_queries_utils.h"
67
#include "ccf/rpc_context.h"
78
#include "ccf/service/tables/service.h"
89
#include "kv/kv_types.h"
@@ -448,8 +449,8 @@ namespace ccf::historical
448449
state_cache.get_state_at(historic_request_handle, target_tx_id.seqno);
449450
if (
450451
historical_state == nullptr ||
451-
(!get_service_endorsements(
452-
args, historical_state, state_cache, network_identity_subsystem)))
452+
(!populate_service_endorsements(
453+
args.tx, historical_state, state_cache, network_identity_subsystem)))
453454
{
454455
args.rpc_ctx->set_response_status(HTTP_STATUS_ACCEPTED);
455456
constexpr size_t retry_after_seconds = 3;

src/node/historical_queries_utils.cpp

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the Apache 2.0 License.
3+
4+
#include "ccf/historical_queries_utils.h"
5+
6+
#include "ccf/rpc_context.h"
7+
#include "ccf/service/tables/service.h"
8+
#include "kv/kv_types.h"
9+
#include "node/tx_receipt_impl.h"
10+
11+
namespace ccf
12+
{
13+
static std::map<crypto::Pem, std::vector<crypto::Pem>>
14+
service_endorsement_cache;
15+
16+
namespace historical
17+
{
18+
std::optional<ServiceInfo> find_previous_service_identity(
19+
kv::ReadOnlyTx& tx,
20+
ccf::historical::StatePtr& state,
21+
AbstractStateCache& state_cache)
22+
{
23+
SeqNo target_seqno = state->transaction_id.seqno;
24+
25+
// We start at the previous write to the latest (current) service info.
26+
auto service = tx.template ro<Service>(Tables::SERVICE);
27+
28+
// Iterate until we find the most recent write to the service info that
29+
// precedes the target seqno.
30+
std::optional<ServiceInfo> hservice_info = service->get();
31+
SeqNo i = -1;
32+
do
33+
{
34+
if (!hservice_info->previous_service_identity_version)
35+
{
36+
// Pre 2.0 we did not record the versions of previous identities in
37+
// the service table.
38+
throw std::runtime_error(
39+
"The service identity that signed the receipt cannot be found "
40+
"because it is in a pre-2.0 part of the ledger.");
41+
}
42+
i = hservice_info->previous_service_identity_version.value_or(i - 1);
43+
LOG_TRACE_FMT("historical service identity search at: {}", i);
44+
auto hstate = state_cache.get_state_at(i, i);
45+
if (!hstate)
46+
{
47+
return std::nullopt; // Not available yet - retry later.
48+
}
49+
auto htx = hstate->store->create_read_only_tx();
50+
auto hservice = htx.ro<Service>(Tables::SERVICE);
51+
hservice_info = hservice->get();
52+
} while (i > target_seqno || (i > 1 && !hservice_info));
53+
54+
if (!hservice_info)
55+
{
56+
throw std::runtime_error("Failed to locate previous service identity");
57+
}
58+
59+
return hservice_info;
60+
}
61+
62+
bool populate_service_endorsements(
63+
kv::ReadOnlyTx& tx,
64+
ccf::historical::StatePtr& state,
65+
AbstractStateCache& state_cache,
66+
std::shared_ptr<NetworkIdentitySubsystemInterface>
67+
network_identity_subsystem)
68+
{
69+
try
70+
{
71+
if (!network_identity_subsystem)
72+
{
73+
throw std::runtime_error(
74+
"The service identity endorsement for this receipt cannot be "
75+
"created "
76+
"because the current network identity is not available.");
77+
}
78+
79+
const auto& network_identity = network_identity_subsystem->get();
80+
81+
if (state && state->receipt && state->receipt->node_cert)
82+
{
83+
auto& receipt = *state->receipt;
84+
85+
if (receipt.node_cert->empty())
86+
{
87+
// Pre 2.0 receipts did not contain node certs.
88+
throw std::runtime_error(
89+
"Node certificate in receipt is empty, likely because the "
90+
"transaction is in a pre-2.0 part of the ledger.");
91+
}
92+
93+
auto v = crypto::make_unique_verifier(*receipt.node_cert);
94+
if (!v->verify_certificate(
95+
{&network_identity->cert}, {}, /* ignore_time */ true))
96+
{
97+
// The current service identity does not endorse the node
98+
// certificate in the receipt, so we search for the the most recent
99+
// write to the service info table before the historical transaction
100+
// ID to get the historical service identity.
101+
102+
auto opt_psi =
103+
find_previous_service_identity(tx, state, state_cache);
104+
if (!opt_psi)
105+
{
106+
return false;
107+
}
108+
109+
auto hpubkey = crypto::public_key_pem_from_cert(
110+
crypto::cert_pem_to_der(opt_psi->cert));
111+
112+
auto eit = service_endorsement_cache.find(hpubkey);
113+
if (eit != service_endorsement_cache.end())
114+
{
115+
// Note: validity period of service certificate may have changed
116+
// since we created the cached endorsements.
117+
receipt.service_endorsements = eit->second;
118+
}
119+
else
120+
{
121+
auto ncv = crypto::make_unique_verifier(network_identity->cert);
122+
auto endorsement = create_endorsed_cert(
123+
hpubkey,
124+
ReplicatedNetworkIdentity::subject_name,
125+
{},
126+
ncv->validity_period(),
127+
network_identity->priv_key,
128+
network_identity->cert,
129+
true);
130+
service_endorsement_cache[hpubkey] = {endorsement};
131+
receipt.service_endorsements = {endorsement};
132+
}
133+
}
134+
}
135+
}
136+
catch (std::exception& ex)
137+
{
138+
LOG_DEBUG_FMT(
139+
"Exception while extracting previous service identities: {}",
140+
ex.what());
141+
// (We keep the incomplete receipt, no further error reporting)
142+
}
143+
144+
return true;
145+
}
146+
}
147+
}

src/node/rpc/network_identity_subsystem.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
// Licensed under the Apache 2.0 License.
33
#pragma once
44

5+
#include "ccf/network_identity_interface.h"
56
#include "node/identity.h"
6-
#include "node/rpc/network_identity_interface.h"
77
#include "node/rpc/node_interface.h"
88

99
namespace ccf

0 commit comments

Comments
 (0)