Skip to content

Commit

Permalink
[release/4.x] Cherry pick: Add populate_service_endorsements to pub…
Browse files Browse the repository at this point in the history
…lic headers (#5242) (#5243)
  • Loading branch information
eddyashton authored May 9, 2023
1 parent 1149158 commit 816454a
Show file tree
Hide file tree
Showing 9 changed files with 186 additions and 154 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

- For security reasons, OpenSSL `>=1.1.1f` must be first installed on the system (Ubuntu) before installing the CCF Debian package (#5227).

## Added

- Added `ccf::historical::populate_service_endorsements` to public C++ API, allowing custom historical endpoints to do the same work as adapters.

## [4.0.0]

In order to upgrade an existing 3.x service to 4.x, CCF must be on the latest 3.x version (at least 3.0.10). For more information, see [our documentation](https://microsoft.github.io/CCF/main/operations/code_upgrade.html)
Expand Down
1 change: 1 addition & 0 deletions cmake/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ set(CCF_ENDPOINTS_SOURCES
${CCF_DIR}/src/indexing/strategies/seqnos_by_key_in_memory.cpp
${CCF_DIR}/src/indexing/strategies/visit_each_entry_in_map.cpp
${CCF_DIR}/src/node/historical_queries_adapter.cpp
${CCF_DIR}/src/node/historical_queries_utils.cpp
${CCF_DIR}/src/node/receipt.cpp
)

Expand Down
27 changes: 27 additions & 0 deletions include/ccf/historical_queries_utils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.

#include "ccf/historical_queries_interface.h"
#include "ccf/network_identity_interface.h"
#include "ccf/rpc_context.h"
#include "ccf/tx.h"

namespace ccf::historical
{
// Modifies the receipt stored in state to include historical service
// endorsements, where required. If the state talks about a different service
// identity, which is known to be a predecessor of this service (via disaster
// recoveries), then an endorsement of the receipt's node certificate will be
// created. This may need to use the state_cache to request additional
// historical entries to construct this endorsement, and may read from the
// current/latest state via tx. Returns true if the operation is complete,
// though it may still have failed to produce an endorsement. Returns false if
// additional entries have been requested, in which case the caller should
// retry later.
bool populate_service_endorsements(
kv::ReadOnlyTx& tx,
ccf::historical::StatePtr& state,
AbstractStateCache& state_cache,
std::shared_ptr<NetworkIdentitySubsystemInterface>
network_identity_subsystem);
}
File renamed without changes.
6 changes: 3 additions & 3 deletions src/apps/external_executor/external_executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "ccf/crypto/verifier.h"
#include "ccf/entity_id.h"
#include "ccf/historical_queries_adapter.h"
#include "ccf/historical_queries_utils.h"
#include "ccf/http_consts.h"
#include "ccf/http_responder.h"
#include "ccf/json_handler.h"
Expand All @@ -20,7 +21,6 @@
#include "kv.pb.h"
#include "misc.pb.h"
#include "node/endpoint_context_impl.h"
#include "node/historical_queries_utils.h"
#include "node/rpc/network_identity_subsystem.h"
#include "node/rpc/rpc_context_impl.h"

Expand Down Expand Up @@ -690,8 +690,8 @@ namespace externalexecutor

if (
historical_state == nullptr ||
(!get_service_endorsements(
ctx, historical_state, state_cache, network_identity_subsystem)))
(!populate_service_endorsements(
ctx.tx, historical_state, state_cache, network_identity_subsystem)))
{
externalexecutor::protobuf::QueryResponse response;
response.set_retry(true);
Expand Down
6 changes: 3 additions & 3 deletions src/node/historical_queries_adapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

#include "ccf/historical_queries_adapter.h"

#include "ccf/historical_queries_utils.h"
#include "ccf/rpc_context.h"
#include "ccf/service/tables/service.h"
#include "kv/kv_types.h"
#include "node/historical_queries_utils.h"
#include "node/rpc/network_identity_subsystem.h"
#include "node/tx_receipt_impl.h"

Expand Down Expand Up @@ -318,8 +318,8 @@ namespace ccf::historical
state_cache.get_state_at(historic_request_handle, target_tx_id.seqno);
if (
historical_state == nullptr ||
(!get_service_endorsements(
args, historical_state, state_cache, network_identity_subsystem)))
(!populate_service_endorsements(
args.tx, historical_state, state_cache, network_identity_subsystem)))
{
args.rpc_ctx->set_response_status(HTTP_STATUS_ACCEPTED);
constexpr size_t retry_after_seconds = 3;
Expand Down
147 changes: 147 additions & 0 deletions src/node/historical_queries_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the Apache 2.0 License.

#include "ccf/historical_queries_utils.h"

#include "ccf/rpc_context.h"
#include "ccf/service/tables/service.h"
#include "kv/kv_types.h"
#include "node/tx_receipt_impl.h"

namespace ccf
{
static std::map<crypto::Pem, std::vector<crypto::Pem>>
service_endorsement_cache;

namespace historical
{
std::optional<ServiceInfo> find_previous_service_identity(
kv::ReadOnlyTx& tx,
ccf::historical::StatePtr& state,
AbstractStateCache& state_cache)
{
SeqNo target_seqno = state->transaction_id.seqno;

// We start at the previous write to the latest (current) service info.
auto service = tx.template ro<Service>(Tables::SERVICE);

// Iterate until we find the most recent write to the service info that
// precedes the target seqno.
std::optional<ServiceInfo> hservice_info = service->get();
SeqNo i = -1;
do
{
if (!hservice_info->previous_service_identity_version)
{
// Pre 2.0 we did not record the versions of previous identities in
// the service table.
throw std::runtime_error(
"The service identity that signed the receipt cannot be found "
"because it is in a pre-2.0 part of the ledger.");
}
i = hservice_info->previous_service_identity_version.value_or(i - 1);
LOG_TRACE_FMT("historical service identity search at: {}", i);
auto hstate = state_cache.get_state_at(i, i);
if (!hstate)
{
return std::nullopt; // Not available yet - retry later.
}
auto htx = hstate->store->create_read_only_tx();
auto hservice = htx.ro<Service>(Tables::SERVICE);
hservice_info = hservice->get();
} while (i > target_seqno || (i > 1 && !hservice_info));

if (!hservice_info)
{
throw std::runtime_error("Failed to locate previous service identity");
}

return hservice_info;
}

bool populate_service_endorsements(
kv::ReadOnlyTx& tx,
ccf::historical::StatePtr& state,
AbstractStateCache& state_cache,
std::shared_ptr<NetworkIdentitySubsystemInterface>
network_identity_subsystem)
{
try
{
if (!network_identity_subsystem)
{
throw std::runtime_error(
"The service identity endorsement for this receipt cannot be "
"created "
"because the current network identity is not available.");
}

const auto& network_identity = network_identity_subsystem->get();

if (state && state->receipt && state->receipt->node_cert)
{
auto& receipt = *state->receipt;

if (receipt.node_cert->empty())
{
// Pre 2.0 receipts did not contain node certs.
throw std::runtime_error(
"Node certificate in receipt is empty, likely because the "
"transaction is in a pre-2.0 part of the ledger.");
}

auto v = crypto::make_unique_verifier(*receipt.node_cert);
if (!v->verify_certificate(
{&network_identity->cert}, {}, /* ignore_time */ true))
{
// The current service identity does not endorse the node
// certificate in the receipt, so we search for the the most recent
// write to the service info table before the historical transaction
// ID to get the historical service identity.

auto opt_psi =
find_previous_service_identity(tx, state, state_cache);
if (!opt_psi)
{
return false;
}

auto hpubkey = crypto::public_key_pem_from_cert(
crypto::cert_pem_to_der(opt_psi->cert));

auto eit = service_endorsement_cache.find(hpubkey);
if (eit != service_endorsement_cache.end())
{
// Note: validity period of service certificate may have changed
// since we created the cached endorsements.
receipt.service_endorsements = eit->second;
}
else
{
auto ncv = crypto::make_unique_verifier(network_identity->cert);
auto endorsement = create_endorsed_cert(
hpubkey,
ReplicatedNetworkIdentity::subject_name,
{},
ncv->validity_period(),
network_identity->priv_key,
network_identity->cert,
true);
service_endorsement_cache[hpubkey] = {endorsement};
receipt.service_endorsements = {endorsement};
}
}
}
}
catch (std::exception& ex)
{
LOG_DEBUG_FMT(
"Exception while extracting previous service identities: {}",
ex.what());
// (We keep the incomplete receipt, no further error reporting)
}

return true;
}
}
}
147 changes: 0 additions & 147 deletions src/node/historical_queries_utils.h

This file was deleted.

Loading

0 comments on commit 816454a

Please sign in to comment.