CCF
Loading...
Searching...
No Matches
node_state.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
6#include "ccf/crypto/pem.h"
9#include "ccf/ds/json.h"
10#include "ccf/js/core/context.h"
13#include "ccf/pal/locking.h"
14#include "ccf/pal/platform.h"
15#include "ccf/pal/snp_ioctl.h"
20#include "ccf/tx.h"
21#include "consensus/aft/raft.h"
23#include "crypto/certs.h"
24#include "ds/ccf_assert.h"
25#include "ds/files.h"
26#include "ds/internal_logger.h"
27#include "ds/state_machine.h"
29#include "encryptor.h"
30#include "history.h"
31#include "http/http_parser.h"
32#include "indexing/indexer.h"
33#include "js/global_class_ids.h"
34#include "network_state.h"
35#include "node/hooks.h"
38#include "node/ledger_secret.h"
39#include "node/ledger_secrets.h"
40#include "node/local_sealing.h"
42#include "node/snapshotter.h"
43#include "node_to_node.h"
46#include "rpc/frontend.h"
47#include "rpc/serialization.h"
48#include "secret_broadcast.h"
51#include "share_manager.h"
52#include "uvm_endorsements.h"
53
54#include <optional>
55
56#ifdef USE_NULL_ENCRYPTOR
57# include "kv/test/null_encryptor.h"
58#endif
59
60#include <algorithm>
61#include <atomic>
62#include <chrono>
63#define FMT_HEADER_ONLY
64#include <fmt/format.h>
65#include <nlohmann/json.hpp>
66#include <stdexcept>
67#include <unordered_set>
68#include <vector>
69
70namespace ccf
71{
73
79
80 inline void reset_data(std::vector<uint8_t>& data)
81 {
82 data.clear();
83 data.shrink_to_fit();
84 }
85
87 {
88 private:
89 //
90 // this node's core state
91 //
93 pal::Mutex lock;
94 StartType start_type = StartType::Start;
95
96 ccf::crypto::CurveID curve_id;
97 std::vector<ccf::crypto::SubjectAltName> subject_alt_names;
98
99 std::shared_ptr<ccf::crypto::ECKeyPair_OpenSSL> node_sign_kp;
100 NodeId self;
101 std::shared_ptr<ccf::crypto::RSAKeyPair> node_encrypt_kp;
102 ccf::crypto::Pem self_signed_node_cert;
103 std::optional<ccf::crypto::Pem> endorsed_node_cert = std::nullopt;
104 QuoteInfo quote_info;
106 std::optional<pal::snp::TcbVersionRaw> snp_tcb_version = std::nullopt;
107 ccf::StartupConfig config;
108 std::optional<pal::UVMEndorsements> snp_uvm_endorsements = std::nullopt;
109 std::vector<uint8_t> startup_snapshot;
110 std::shared_ptr<QuoteEndorsementsClient> quote_endorsements_client =
111 nullptr;
112
113 std::atomic<bool> stop_noticed = false;
114
115 //
116 // kv store, replication, and I/O
117 //
118 ringbuffer::AbstractWriterFactory& writer_factory;
119 ringbuffer::WriterPtr to_host;
120 ccf::consensus::Configuration consensus_config;
121 size_t sig_tx_interval = 0;
122 size_t sig_ms_interval = 0;
123
124 NetworkState& network;
125
126 std::shared_ptr<ccf::kv::Consensus> consensus;
127 std::shared_ptr<RPCMap> rpc_map;
128 std::shared_ptr<indexing::Indexer> indexer;
129 std::shared_ptr<NodeToNode> n2n_channels;
130 std::shared_ptr<Forwarder<NodeToNode>> cmd_forwarder;
131 std::shared_ptr<RPCSessions> rpcsessions;
132
133 std::shared_ptr<ccf::kv::TxHistory> history;
134 std::shared_ptr<ccf::kv::AbstractTxEncryptor> encryptor;
135
136 ShareManager share_manager;
137 std::shared_ptr<Snapshotter> snapshotter;
138
139 //
140 // recovery
141 //
142 std::shared_ptr<ccf::kv::Store> recovery_store;
143
144 ccf::kv::Version recovery_v = 0;
145 ccf::crypto::Sha256Hash recovery_root;
146 std::vector<ccf::kv::Version> view_history;
147 ::consensus::Index last_recovered_signed_idx = 0;
148 RecoveredEncryptedLedgerSecrets recovered_encrypted_ledger_secrets;
149 ::consensus::Index last_recovered_idx = 0;
150 static const size_t recovery_batch_size = 100;
151
152 //
153 // JWT key auto-refresh
154 //
155 std::shared_ptr<JwtKeyAutoRefresh> jwt_key_auto_refresh;
156
157 std::unique_ptr<StartupSnapshotInfo> startup_snapshot_info = nullptr;
158 // Set to the snapshot seqno when a node starts from one and remembered for
159 // the lifetime of the node
160 ccf::kv::Version startup_seqno = 0;
161
162 ccf::tasks::Task join_periodic_task;
163
164 std::shared_ptr<ccf::kv::AbstractTxEncryptor> make_encryptor()
165 {
166#ifdef USE_NULL_ENCRYPTOR
167 return std::make_shared<ccf::kv::NullTxEncryptor>();
168#else
169 return std::make_shared<NodeEncryptor>(network.ledger_secrets);
170#endif
171 }
172
173 // Returns true if the snapshot is already verified (via embedded receipt)
174 void initialise_startup_snapshot(bool recovery = false)
175 {
176 std::shared_ptr<ccf::kv::Store> snapshot_store;
177 if (!recovery)
178 {
179 // Create a new store to verify the snapshot only
180 snapshot_store = make_store();
181 auto snapshot_history = std::make_shared<MerkleTxHistory>(
182 *snapshot_store,
183 self,
184 *node_sign_kp,
185 sig_tx_interval,
186 sig_ms_interval,
187 false /* No signature timer on snapshot_history */);
188
189 auto snapshot_encryptor = make_encryptor();
190
191 snapshot_store->set_history(snapshot_history);
192 snapshot_store->set_encryptor(snapshot_encryptor);
193 }
194 else
195 {
196 snapshot_store = network.tables;
197 }
198
200 startup_snapshot_info = initialise_from_snapshot(
201 snapshot_store,
202 std::move(startup_snapshot),
203 hooks,
204 &view_history,
205 true,
207
208 startup_seqno = startup_snapshot_info->seqno;
209 last_recovered_idx = startup_seqno;
210 last_recovered_signed_idx = last_recovered_idx;
211 }
212
213 public:
215 ringbuffer::AbstractWriterFactory& writer_factory,
216 NetworkState& network,
217 std::shared_ptr<RPCSessions> rpcsessions,
218 ccf::crypto::CurveID curve_id_) :
219 sm("NodeState", NodeStartupState::uninitialized),
220 curve_id(curve_id_),
221 node_sign_kp(std::make_shared<ccf::crypto::ECKeyPair_OpenSSL>(curve_id_)),
222 self(compute_node_id_from_kp(node_sign_kp)),
223 node_encrypt_kp(ccf::crypto::make_rsa_key_pair()),
224 writer_factory(writer_factory),
225 to_host(writer_factory.create_writer_to_outside()),
226 network(network),
227 rpcsessions(std::move(rpcsessions)),
228 share_manager(network.ledger_secrets)
229 {}
230
233 const QuoteInfo& quote_info_,
234 const std::vector<uint8_t>& expected_node_public_key_der,
235 pal::PlatformAttestationMeasurement& measurement) override
236 {
238 tx, quote_info_, expected_node_public_key_der, measurement);
239 }
240
241 //
242 // funcs in state "uninitialized"
243 //
245 const ccf::consensus::Configuration& consensus_config_,
246 std::shared_ptr<RPCMap> rpc_map_,
247 std::shared_ptr<AbstractRPCResponder> rpc_sessions_,
248 std::shared_ptr<indexing::Indexer> indexer_,
249 size_t sig_tx_interval_,
250 size_t sig_ms_interval_)
251 {
252 std::lock_guard<pal::Mutex> guard(lock);
254
255 consensus_config = consensus_config_;
256 rpc_map = rpc_map_;
257 indexer = indexer_;
258 sig_tx_interval = sig_tx_interval_;
259 sig_ms_interval = sig_ms_interval_;
260
261 n2n_channels = std::make_shared<NodeToNodeChannelManager>(writer_factory);
262
263 cmd_forwarder = std::make_shared<Forwarder<NodeToNode>>(
264 rpc_sessions_, n2n_channels, rpc_map);
265
267
268 for (auto& [actor, fe] : rpc_map->frontends())
269 {
270 fe->set_sig_intervals(sig_tx_interval, sig_ms_interval);
271 fe->set_cmd_forwarder(cmd_forwarder);
272 }
273 }
274
275 //
276 // funcs in state "initialized"
277 //
279 {
280 auto measurement = AttestationProvider::get_measurement(quote_info);
281 if (measurement.has_value())
282 {
283 node_measurement = measurement.value();
284 }
285 else
286 {
287 throw std::logic_error("Failed to extract code id from quote");
288 }
289
290 auto snp_attestation =
292 if (snp_attestation.has_value())
293 {
294 snp_tcb_version = snp_attestation.value().reported_tcb;
295 }
296
297 // Verify that the security policy matches the quoted digest of the policy
298 if (!config.attestation.environment.security_policy.has_value())
299 {
301 "Security policy not set, skipping check against attestation host "
302 "data");
303 }
304 else
305 {
306 auto quoted_digest = AttestationProvider::get_host_data(quote_info);
307 if (!quoted_digest.has_value())
308 {
309 throw std::logic_error("Unable to find host data in attestation");
310 }
311
312 auto const& security_policy =
314
315 auto security_policy_digest =
316 quote_info.format == QuoteFormat::amd_sev_snp_v1 ?
318 ccf::crypto::Sha256Hash(security_policy);
319 if (security_policy_digest != quoted_digest.value())
320 {
321 throw std::logic_error(fmt::format(
322 "Digest of decoded security policy \"{}\" {} does not match "
323 "attestation host data {}",
324 security_policy,
325 security_policy_digest.hex_str(),
326 quoted_digest.value().hex_str()));
327 }
329 "Successfully verified attested security policy {}",
330 security_policy_digest);
331 }
332
333 if (quote_info.format == QuoteFormat::amd_sev_snp_v1)
334 {
335 if (!config.attestation.environment.uvm_endorsements.has_value())
336 {
338 "UVM endorsements not set, skipping check against attestation "
339 "measurement");
340 }
341 else
342 {
343 try
344 {
345 auto uvm_endorsements_raw = ccf::crypto::raw_from_b64(
347 // A node at this stage does not have a notion of what UVM
348 // descriptor is acceptable. That is decided either by the Joinee,
349 // or by Consortium endorsing the Start or Recovery node. For that
350 // reason, we extract an endorsement descriptor from the UVM
351 // endorsements and make it available in the ledger's initial or
352 // recovery transaction.
353 snp_uvm_endorsements = pal::verify_uvm_endorsements_descriptor(
354 uvm_endorsements_raw, node_measurement);
355 quote_info.uvm_endorsements = uvm_endorsements_raw;
357 "Successfully verified attested UVM endorsements: {}",
358 snp_uvm_endorsements->to_str());
359 }
360 catch (const std::exception& e)
361 {
362 throw std::logic_error(
363 fmt::format("Error verifying UVM endorsements: {}", e.what()));
364 }
365 }
366 }
367
368 switch (start_type)
369 {
370 case StartType::Start:
371 {
372 create_and_send_boot_request(
373 aft::starting_view_change, true /* Create new consortium */);
374 return;
375 }
376 case StartType::Join:
377 {
378 if (!startup_snapshot.empty())
379 {
380 initialise_startup_snapshot();
381 }
382
385 return;
386 }
388 {
389 setup_recovery_hook();
390 if (!startup_snapshot.empty())
391 {
392 initialise_startup_snapshot(true);
393 snapshotter->set_last_snapshot_idx(last_recovered_idx);
394 }
395
398 return;
399 }
400 default:
401 {
402 throw std::logic_error(
403 fmt::format("Node was launched in unknown mode {}", start_type));
404 }
405 }
406 }
407
409 {
410 auto fetch_endorsements = [this](
411 const QuoteInfo& qi,
412 const pal::snp::
413 EndorsementEndpointsConfiguration&
414 endpoint_config) {
415 // Note: Node lock is already taken here as this is called back
416 // synchronously with the call to pal::generate_quote
417 this->quote_info = qi;
418
419 auto b64encoded_quote = ccf::crypto::b64url_from_raw(quote_info.quote);
420 nlohmann::json jq;
421 to_json(jq, quote_info.format);
423 "Initial node attestation ({}): {}", jq.dump(), b64encoded_quote);
424
425 if (quote_info.format == QuoteFormat::amd_sev_snp_v1)
426 {
427 // Use endorsements retrieved from file, if available
428 if (config.attestation.environment.snp_endorsements.has_value())
429 {
430 try
431 {
432 const auto raw_data = ccf::crypto::raw_from_b64(
434
435 const auto j = nlohmann::json::parse(raw_data);
436 const auto aci_endorsements =
438
439 // Check that tcbm in endorsement matches reported TCB in our
440 // retrieved attestation
441 const auto* quote =
442 reinterpret_cast<const ccf::pal::snp::Attestation*>(
443 quote_info.quote.data());
444 const auto reported_tcb = quote->reported_tcb;
445
446 // tcbm is a single hex value, like DB18000000000004. To match
447 // that with a TcbVersion, reverse the bytes.
448 const auto* tcb_begin =
449 reinterpret_cast<const uint8_t*>(&reported_tcb);
450 const std::span<const uint8_t> tcb_bytes{
451 tcb_begin, tcb_begin + sizeof(reported_tcb)};
452 auto tcb_as_hex = fmt::format(
453 "{:02x}", fmt::join(tcb_bytes.rbegin(), tcb_bytes.rend(), ""));
454 ccf::nonstd::to_upper(tcb_as_hex);
455
456 if (tcb_as_hex == aci_endorsements.tcbm)
457 {
459 "Using SNP endorsements loaded from file, endorsing TCB {}",
460 tcb_as_hex);
461
462 auto& endorsements_pem = quote_info.endorsements;
463 endorsements_pem.insert(
464 endorsements_pem.end(),
465 aci_endorsements.vcek_cert.begin(),
466 aci_endorsements.vcek_cert.end());
467 endorsements_pem.insert(
468 endorsements_pem.end(),
469 aci_endorsements.certificate_chain.begin(),
470 aci_endorsements.certificate_chain.end());
471
472 try
473 {
474 launch_node();
475 return;
476 }
477 catch (const std::exception& e)
478 {
479 LOG_FAIL_FMT("Failed to launch node: {}", e.what());
480 throw;
481 }
482 }
483 else
484 {
486 "SNP endorsements loaded from disk ({}) contained tcbm {}, "
487 "which does not match reported TCB of current attestation "
488 "{}. "
489 "Falling back to fetching fresh endorsements from server.",
490 config.attestation.snp_endorsements_file.value(),
491 aci_endorsements.tcbm,
492 tcb_as_hex);
493 }
494 }
495 catch (const std::exception& e)
496 {
498 "Error attempting to use SNP endorsements from file: {}",
499 e.what());
500 }
501 }
502
503 if (config.attestation.snp_endorsements_servers.empty())
504 {
505 throw std::runtime_error(
506 "One or more SNP endorsements servers must be specified to fetch "
507 "the collateral for the attestation");
508 }
509 // On SEV-SNP, fetch endorsements from servers if specified
510 quote_endorsements_client = std::make_shared<QuoteEndorsementsClient>(
511 endpoint_config, [this](std::vector<uint8_t>&& endorsements) {
512 std::lock_guard<pal::Mutex> guard(lock);
513 quote_info.endorsements = std::move(endorsements);
514 try
515 {
516 launch_node();
517 }
518 catch (const std::exception& e)
519 {
520 LOG_FAIL_FMT("{}", e.what());
521 throw;
522 }
523 quote_endorsements_client.reset();
524 });
525
526 quote_endorsements_client->fetch_endorsements();
527 return;
528 }
529
530 if (quote_info.format != QuoteFormat::insecure_virtual)
531 {
532 throw std::runtime_error(fmt::format(
533 "Unsupported quote format: {}",
534 static_cast<int>(quote_info.format)));
535 }
536
537 launch_node();
538 };
539
541 ccf::crypto::Sha256Hash((node_sign_kp->public_key_der()));
542
543 pal::generate_quote(
544 report_data,
545 fetch_endorsements,
547 }
548
550 StartType start_type_,
551 const ccf::StartupConfig& config_,
552 std::vector<uint8_t>&& startup_snapshot_)
553 {
554 std::lock_guard<pal::Mutex> guard(lock);
556 start_type = start_type_;
557
558 config = config_;
559 startup_snapshot = std::move(startup_snapshot_);
560 subject_alt_names = get_subject_alternative_names();
561
563 self_signed_node_cert = create_self_signed_cert(
564 node_sign_kp,
566 subject_alt_names,
567 config.startup_host_time,
569
570 accept_node_tls_connections();
571 open_frontend(ActorsType::nodes);
572
573 // Signatures are only emitted on a timer once the public ledger has been
574 // recovered
575 setup_history();
576 setup_snapshotter();
577 setup_encryptor();
578
580
581 switch (start_type)
582 {
583 case StartType::Start:
584 {
585 network.identity = std::make_unique<ccf::NetworkIdentity>(
587 curve_id,
588 config.startup_host_time,
590
591 network.ledger_secrets->init();
592 // Safe as initiate_quote_generation has previously set the
593 // snp_tcb_version
594 seal_ledger_secret(network.ledger_secrets->get_first());
595
596 history->set_service_signing_identity(
597 network.identity->get_key_pair(), config.cose_signatures);
598
599 setup_consensus(false, endorsed_node_cert);
600
601 // Become the primary and force replication
602 consensus->force_become_primary();
603
604 LOG_INFO_FMT("Created new node {}", self);
605 return {self_signed_node_cert, network.identity->cert};
606 }
607 case StartType::Join:
608 {
609 LOG_INFO_FMT("Created join node {}", self);
610 return {self_signed_node_cert, {}};
611 }
613 {
615 {
616 throw std::logic_error(
617 "Recovery requires the certificate of the previous service "
618 "identity");
619 }
620
621 ccf::crypto::Pem previous_service_identity_cert(
622 config.recover.previous_service_identity.value());
623
624 network.identity = std::make_unique<ccf::NetworkIdentity>(
625 ccf::crypto::get_subject_name(previous_service_identity_cert),
626 curve_id,
627 config.startup_host_time,
629
630 LOG_INFO_FMT("Created recovery node {}", self);
631 return {self_signed_node_cert, network.identity->cert};
632 }
633 default:
634 {
635 throw std::logic_error(
636 fmt::format("Node was started in unknown mode {}", start_type));
637 }
638 }
639 }
640
641 //
642 // funcs in state "pending"
643 //
644
646 {
648
649 auto network_ca = std::make_shared<::tls::CA>(std::string(
650 config.join.service_cert.begin(), config.join.service_cert.end()));
651
652 auto join_client_cert = std::make_unique<::tls::Cert>(
653 network_ca,
654 self_signed_node_cert,
655 node_sign_kp->private_key_pem(),
656 config.join.target_rpc_address);
657
658 // Create RPC client and connect to remote node
659 // Note: For now, assume that target node accepts same application
660 // protocol as this node's main RPC interface
661 auto join_client = rpcsessions->create_client(
662 std::move(join_client_cert),
663 rpcsessions->get_app_protocol_main_interface());
664
665 auto [target_host, target_port] =
666 split_net_address(config.join.target_rpc_address);
667
668 join_client->connect(
669 target_host,
670 target_port,
671 [this](
672 ccf::http_status status,
673 http::HeaderMap&& headers,
674 std::vector<uint8_t>&& data) {
675 std::lock_guard<pal::Mutex> guard(lock);
677 {
678 return;
679 }
680
681 if (is_http_status_client_error(status))
682 {
683 auto error_msg = fmt::format(
684 "Join request to {} returned {} Bad Request: {}. Shutting "
685 "down node gracefully.",
687 status,
688 std::string(data.begin(), data.end()));
689 LOG_FAIL_FMT("{}", error_msg);
691 AdminMessage::fatal_error_msg, to_host, error_msg);
692 }
693 else if (status != HTTP_STATUS_OK)
694 {
695 const auto& location = headers.find(http::headers::LOCATION);
696 if (
697 config.join.follow_redirect &&
698 (status == HTTP_STATUS_PERMANENT_REDIRECT ||
699 status == HTTP_STATUS_TEMPORARY_REDIRECT) &&
700 location != headers.end())
701 {
702 const auto& url = ::http::parse_url_full(location->second);
703 config.join.target_rpc_address =
704 make_net_address(url.host, url.port);
705 LOG_INFO_FMT("Target node redirected to {}", location->second);
706 }
707 else
708 {
710 "An error occurred while joining the network: {} {}{}",
711 status,
712 ccf::http_status_str(status),
713 data.empty() ?
714 "" :
715 fmt::format(" '{}'", std::string(data.begin(), data.end())));
716 }
717 return;
718 }
719
720 auto j = nlohmann::json::parse(data);
721
723 try
724 {
725 resp = j.get<JoinNetworkNodeToNode::Out>();
726 }
727 catch (const std::exception& e)
728 {
730 "An error occurred while parsing the join network response");
732 "An error occurred while parsing the join network response: {}",
733 j.dump());
734 return;
735 }
736
737 // Set network secrets, node id and become part of network.
739 {
740 if (!resp.network_info.has_value())
741 {
742 throw std::logic_error("Expected network info in join response");
743 }
744
745 network.identity = std::make_unique<ccf::NetworkIdentity>(
746 resp.network_info->identity);
747 seal_ledger_secret(*resp.network_info->ledger_secrets.rbegin());
748 network.ledger_secrets->init_from_map(
749 std::move(resp.network_info->ledger_secrets));
750
751 history->set_service_signing_identity(
752 network.identity->get_key_pair(),
753 resp.network_info->cose_signatures_config.value_or(
755
756 ccf::crypto::Pem n2n_channels_cert;
757 if (!resp.network_info->endorsed_certificate.has_value())
758 {
759 // Endorsed certificate was added to join response in 2.x
760 throw std::logic_error(
761 "Expected endorsed certificate in join response");
762 }
763 n2n_channels_cert = resp.network_info->endorsed_certificate.value();
764
765 setup_consensus(resp.network_info->public_only, n2n_channels_cert);
767
768 if (resp.network_info->public_only)
769 {
770 last_recovered_signed_idx =
771 resp.network_info->last_recovered_signed_idx;
772 setup_recovery_hook();
773 snapshotter->set_snapshot_generation(false);
774 }
775
777 std::vector<ccf::kv::Version> view_history_ = {};
778 if (startup_snapshot_info)
779 {
780 // It is only possible to deserialise the entire snapshot then,
781 // once the ledger secrets have been passed in by the network
783 deserialise_snapshot(
784 network.tables,
785 startup_snapshot_info->raw,
786 hooks,
787 &view_history_,
788 resp.network_info->public_only,
790
791 for (auto& hook : hooks)
792 {
793 hook->call(consensus.get());
794 }
795
796 auto tx = network.tables->create_read_only_tx();
797 auto* signatures = tx.ro(network.signatures);
798 auto sig = signatures->get();
799 if (!sig.has_value())
800 {
801 throw std::logic_error(
802 fmt::format("No signatures found after applying snapshot"));
803 }
804 view = sig->view;
805
806 if (!resp.network_info->public_only)
807 {
808 // Only clear snapshot if not recovering. When joining the
809 // public network the snapshot is used later to initialise the
810 // recovery store
811 startup_snapshot_info.reset();
812 }
813
815 "Joiner successfully resumed from snapshot at seqno {} and "
816 "view {}",
817 network.tables->current_version(),
818 view);
819 }
820
821 consensus->init_as_backup(
822 network.tables->current_version(),
823 view,
824 view_history_,
825 last_recovered_signed_idx);
826
827 snapshotter->set_last_snapshot_idx(
828 network.tables->current_version());
829 history->start_signature_emit_timer();
830
831 if (resp.network_info->public_only)
832 {
834 }
835 else
836 {
837 reset_data(quote_info.quote);
838 reset_data(quote_info.endorsements);
840 }
841
842 if (join_periodic_task != nullptr)
843 {
844 join_periodic_task->cancel_task();
845 join_periodic_task = nullptr;
846 }
847
849 "Node has now joined the network as node {}: {}",
850 self,
851 (resp.network_info->public_only ? "public only" : "all domains"));
852 }
853 else if (resp.node_status == NodeStatus::PENDING)
854 {
856 "Node {} is waiting for votes of members to be trusted", self);
857 }
858 },
859 [this](const std::string& error_msg) {
860 std::lock_guard<pal::Mutex> guard(lock);
861 auto long_error_msg = fmt::format(
862 "Early error when joining existing network at {}: {}. Shutting "
863 "down node gracefully...",
865 error_msg);
866 LOG_FAIL_FMT("{}", long_error_msg);
868 AdminMessage::fatal_error_msg, to_host, long_error_msg);
869 });
870
871 // Send RPC request to remote node to join the network.
872 JoinNetworkNodeToNode::In join_params;
873
874 join_params.node_info_network = config.network;
875 join_params.public_encryption_key = node_encrypt_kp->public_key_pem();
876 join_params.quote_info = quote_info;
877 join_params.startup_seqno = startup_seqno;
878 join_params.certificate_signing_request = node_sign_kp->create_csr(
879 config.node_certificate.subject_name, subject_alt_names);
880 join_params.node_data = config.node_data;
881
883 "Sending join request to {}", config.join.target_rpc_address);
884
885 const auto body = nlohmann::json(join_params).dump();
886
887 LOG_DEBUG_FMT("Sending join request body: {}", body);
888
890 fmt::format("/{}/{}", get_actor_prefix(ActorsType::nodes), "join"));
891 r.set_header(
892 http::headers::CONTENT_TYPE, http::headervalues::contenttype::JSON);
893 r.set_body(body);
894
895 join_client->send_request(std::move(r));
896 }
897
899 {
900 std::lock_guard<pal::Mutex> guard(lock);
902 }
903
905 {
907
908 join_periodic_task = ccf::tasks::make_basic_task([this]() {
909 std::lock_guard<pal::Mutex> guard(this->lock);
910 if (this->sm.check(NodeStartupState::pending))
911 {
912 this->initiate_join_unsafe();
913 }
914 });
915
917 join_periodic_task,
918 config.join.retry_timeout,
919 config.join.retry_timeout);
920 }
921
923 {
924 if (!consensus)
925 {
927 "JWT key auto-refresh: consensus not initialized, not starting "
928 "auto-refresh");
929 return;
930 }
931 jwt_key_auto_refresh = std::make_shared<JwtKeyAutoRefresh>(
933 network,
934 consensus,
935 rpcsessions,
936 rpc_map,
937 node_sign_kp,
938 self_signed_node_cert);
939 jwt_key_auto_refresh->start();
940
941 network.tables->set_map_hook(
942 network.jwt_issuers.get_name(),
945 jwt_key_auto_refresh->schedule_once();
946 return {nullptr};
947 });
948 }
949
950 size_t get_jwt_attempts() override
951 {
952 return jwt_key_auto_refresh->get_attempts();
953 }
954
955 //
956 // funcs in state "readingPublicLedger"
957 //
959 {
961 {
962 throw std::logic_error(fmt::format(
963 "Node should be in state {} to start reading ledger",
965 }
966
967 LOG_INFO_FMT("Starting to read public ledger");
968
969 read_ledger_entries(
970 last_recovered_idx + 1, last_recovered_idx + recovery_batch_size);
971 }
972
973 void recover_public_ledger_entries(const std::vector<uint8_t>& entries)
974 {
975 std::lock_guard<pal::Mutex> guard(lock);
976
978
979 const auto* data = entries.data();
980 auto size = entries.size();
981
982 if (size == 0)
983 {
984 recover_public_ledger_end_unsafe();
985 return;
986 }
987
988 while (size > 0)
989 {
990 auto entry = ::consensus::LedgerEnclave::get_entry(data, size);
991
993 "Deserialising public ledger entry #{} [{} bytes]",
994 last_recovered_idx,
995 entry.size());
996
997 // When reading the private ledger, deserialise in the recovery store
998
1000 try
1001 {
1002 auto r = network.tables->deserialize(entry, true);
1003 result = r->apply();
1004 if (result == ccf::kv::ApplyResult::FAIL)
1005 {
1007 "Failed to deserialise public ledger entry: {}", result);
1008 recover_public_ledger_end_unsafe();
1009 return;
1010 }
1011 ++last_recovered_idx;
1012
1013 // Not synchronised because consensus isn't effectively running then
1014 for (auto& hook : r->get_hooks())
1015 {
1016 hook->call(consensus.get());
1017 }
1018 }
1019 catch (const std::exception& e)
1020 {
1022 "Failed to deserialise public ledger entry: {}", e.what());
1023 recover_public_ledger_end_unsafe();
1024 return;
1025 }
1026
1027 // If the ledger entry is a signature, it is safe to compact the store
1029 {
1030 // If the ledger entry is a signature, it is safe to compact the store
1031 network.tables->compact(last_recovered_idx);
1032 auto tx = network.tables->create_read_only_tx();
1033 auto last_sig = tx.ro(network.signatures)->get();
1034
1035 if (!last_sig.has_value())
1036 {
1037 throw std::logic_error("Signature missing");
1038 }
1039
1041 "Read signature at {} for view {}",
1042 last_recovered_idx,
1043 last_sig->view);
1044 // Initial transactions, before the first signature, must have
1045 // happened in the first signature's view (eg - if the first
1046 // signature is at seqno 20 in view 4, then transactions 1->19 must
1047 // also have been in view 4). The brief justification is that while
1048 // the first node may start in an arbitrarily high view (it does not
1049 // necessarily start in view 1), it cannot _change_ view before a
1050 // valid signature.
1051 const auto view_start_idx =
1052 view_history.empty() ? 1 : last_recovered_signed_idx + 1;
1054 last_sig->view >= 0,
1055 "last_sig->view is invalid, {}",
1056 last_sig->view);
1057 for (auto i = view_history.size();
1058 i < static_cast<size_t>(last_sig->view);
1059 ++i)
1060 {
1061 view_history.push_back(view_start_idx);
1062 }
1063 last_recovered_signed_idx = last_recovered_idx;
1064 }
1065 }
1066
1067 read_ledger_entries(
1068 last_recovered_idx + 1, last_recovered_idx + recovery_batch_size);
1069 }
1070
1072 {
1073 std::lock_guard<pal::Mutex> guard(lock);
1075 history->start_signature_emit_timer();
1077 }
1078
1080 {
1081 std::lock_guard<pal::Mutex> guard(lock);
1083 history->start_signature_emit_timer();
1084 auto_refresh_jwt_keys();
1085 reset_data(quote_info.quote);
1086 reset_data(quote_info.endorsements);
1088 }
1089
1091 {
1093
1094 // When reaching the end of the public ledger, truncate to last signed
1095 // index
1096 const auto last_recovered_term = view_history.size();
1097 auto new_term = last_recovered_term + aft::starting_view_change;
1098 LOG_INFO_FMT("Setting term on public recovery store to {}", new_term);
1099
1100 // Note: KV term must be set before the first Tx is committed
1101 network.tables->rollback(
1102 {last_recovered_term, last_recovered_signed_idx}, new_term);
1103 ledger_truncate(last_recovered_signed_idx, true);
1104 snapshotter->rollback(last_recovered_signed_idx);
1105
1107 "End of public ledger recovery - Truncating ledger to last signed "
1108 "TxID: {}.{}",
1109 last_recovered_term,
1110 last_recovered_signed_idx);
1111
1112 auto tx = network.tables->create_read_only_tx();
1113 network.ledger_secrets->init(last_recovered_signed_idx + 1);
1114
1115 // Initialise snapshotter after public recovery
1116 snapshotter->init_after_public_recovery();
1117 snapshotter->set_snapshot_generation(false);
1118
1119 ccf::kv::Version index = 0;
1120 ccf::kv::Term view = 0;
1121
1122 auto ls = tx.ro(network.signatures)->get();
1123 if (ls.has_value())
1124 {
1125 auto s = ls.value();
1126 index = s.seqno;
1127 view = s.view;
1128 }
1129 else
1130 {
1131 throw std::logic_error("No signature found after recovery");
1132 }
1133
1135 auto lcs = tx.ro(network.cose_signatures)->get();
1136 if (lcs.has_value())
1137 {
1138 CoseSignature cs = lcs.value();
1139 LOG_INFO_FMT("COSE signature found after recovery");
1140 try
1141 {
1142 auto receipt =
1143 cose::decode_ccf_receipt(cs, /* recompute_root */ false);
1144 auto issuer = receipt.phdr.cwt.iss;
1145 auto subject = receipt.phdr.cwt.sub;
1147 "COSE signature issuer: {}, subject: {}", issuer, subject);
1148 cs_cfg = ccf::COSESignaturesConfig{issuer, subject};
1149 }
1150 catch (const cose::COSEDecodeError& e)
1151 {
1152 LOG_FAIL_FMT("COSE signature decode error: {}", e.what());
1153 throw;
1154 }
1155 }
1156 else
1157 {
1158 LOG_INFO_FMT("No COSE signature found after recovery");
1159 }
1160
1161 history->set_service_signing_identity(
1162 network.identity->get_key_pair(), cs_cfg);
1163
1164 auto* h = dynamic_cast<MerkleTxHistory*>(history.get());
1165 if (h != nullptr)
1166 {
1167 h->set_node_id(self);
1168 }
1169
1170 setup_consensus(true);
1171 auto_refresh_jwt_keys();
1172
1173 LOG_DEBUG_FMT("Restarting consensus at view: {} seqno: {}", view, index);
1174
1175 consensus->force_become_primary(index, view, view_history, index);
1176
1177 create_and_send_boot_request(
1178 new_term, false /* Restore consortium from ledger */);
1179 }
1180
1181 //
1182 // funcs in state "readingPrivateLedger"
1183 //
1184 void recover_private_ledger_entries(const std::vector<uint8_t>& entries)
1185 {
1186 std::lock_guard<pal::Mutex> guard(lock);
1188 {
1190 "Node in state {} cannot recover private ledger entries", sm.value());
1191 return;
1192 }
1193
1194 const auto* data = entries.data();
1195 auto size = entries.size();
1196
1197 if (size == 0)
1198 {
1199 recover_private_ledger_end_unsafe();
1200 return;
1201 }
1202
1203 while (size > 0)
1204 {
1205 auto entry = ::consensus::LedgerEnclave::get_entry(data, size);
1206
1208 "Deserialising private ledger entry {} [{}]",
1209 last_recovered_idx + 1,
1210 entry.size());
1211
1212 // When reading the private ledger, deserialise in the recovery store
1214 try
1215 {
1216 result = recovery_store->deserialize(entry)->apply();
1217 if (result == ccf::kv::ApplyResult::FAIL)
1218 {
1220 "Failed to deserialise private ledger entry: {}", result);
1221 // Note: rollback terms do not matter here as recovery store is
1222 // about to be discarded
1223 recovery_store->rollback({0, last_recovered_idx}, 0);
1224 recover_private_ledger_end_unsafe();
1225 return;
1226 }
1227 ++last_recovered_idx;
1228 }
1229 catch (const std::exception& e)
1230 {
1232 "Failed to deserialise private ledger entry: {}", e.what());
1233 recover_private_ledger_end_unsafe();
1234 return;
1235 }
1236
1238 {
1239 recovery_store->compact(last_recovered_idx);
1240 }
1241 }
1242
1243 if (recovery_store->current_version() == recovery_v)
1244 {
1245 LOG_INFO_FMT("Reached recovery final version at {}", recovery_v);
1246 recover_private_ledger_end_unsafe();
1247 }
1248 else
1249 {
1250 read_ledger_entries(
1251 last_recovered_idx + 1,
1252 std::min(last_recovered_idx + recovery_batch_size, recovery_v));
1253 }
1254 }
1255
1257 {
1258 // When reaching the end of the private ledger, make sure the same
1259 // ledger has been read and swap in private state
1260
1262
1264 "Try end private recovery at {}. Is primary: {}",
1265 recovery_v,
1266 consensus->is_primary());
1267
1268 if (recovery_v != recovery_store->current_version())
1269 {
1270 throw std::logic_error(fmt::format(
1271 "Private recovery did not reach public ledger seqno: {}/{}",
1272 recovery_store->current_version(),
1273 recovery_v));
1274 }
1275
1276 auto* h =
1277 dynamic_cast<MerkleTxHistory*>(recovery_store->get_history().get());
1278 if (h->get_replicated_state_root() != recovery_root)
1279 {
1280 throw std::logic_error(fmt::format(
1281 "Root of public store does not match root of private store at {}",
1282 recovery_v));
1283 }
1284
1285 network.tables->swap_private_maps(*recovery_store);
1286 recovery_store.reset();
1287
1288 // Raft should deserialise all security domains when network is opened
1289 consensus->enable_all_domains();
1290
1291 // Snapshots are only generated after recovery is complete
1292 snapshotter->set_snapshot_generation(true);
1293
1294 // Open the service
1295 if (consensus->can_replicate())
1296 {
1298 "Try end private recovery at {}. Trigger service opening",
1299 recovery_v);
1300
1301 auto tx = network.tables->create_tx();
1302
1303 {
1304 // Ensure this transition happens at-most-once, by checking that no
1305 // other node has already advanced the state
1306 auto* service = tx.ro<ccf::Service>(Tables::SERVICE);
1307 auto active_service = service->get();
1308
1309 if (!active_service.has_value())
1310 {
1311 throw std::logic_error(fmt::format(
1312 "Error in {}: no value in {}", __func__, Tables::SERVICE));
1313 }
1314
1315 if (
1316 active_service->status !=
1318 {
1319 throw std::logic_error(fmt::format(
1320 "Error in {}: current service status is {}",
1321 __func__,
1322 active_service->status));
1323 }
1324 }
1325
1326 // Clear recovery shares that were submitted to initiate the recovery
1327 // procedure
1329
1330 // Shares for the new ledger secret can only be issued now, once the
1331 // previous ledger secrets have been recovered
1332 share_manager.issue_recovery_shares(tx);
1333
1334 if (
1337 tx, *network.identity->get_key_pair()))
1338 {
1339 throw std::logic_error("Service could not be opened");
1340 }
1341
1342 // Trigger a snapshot (at next signature) to ensure we have a working
1343 // snapshot signed by the current (now new) service identity, in case
1344 // we need to recover soon again.
1345 trigger_snapshot(tx);
1346
1347 if (tx.commit() != ccf::kv::CommitResult::SUCCESS)
1348 {
1349 throw std::logic_error(
1350 "Could not commit transaction when finishing network recovery");
1351 }
1352 }
1353 recovered_encrypted_ledger_secrets.clear();
1354 reset_data(quote_info.quote);
1355 reset_data(quote_info.endorsements);
1357 }
1358
1360 {
1361 // This hook is necessary to adjust the version at which the last ledger
1362 // secret before recovery is recorded in the store. This can only be
1363 // fired once, after the recovery shares for the post-recovery ledger
1364 // secret are issued.
1365 network.tables->set_map_hook(
1366 network.encrypted_ledger_secrets.get_name(),
1368 [this](
1369 ccf::kv::Version version,
1372 if (!w.has_value())
1373 {
1374 throw std::logic_error(fmt::format(
1375 "Unexpected removal from {} table",
1376 network.encrypted_ledger_secrets.get_name()));
1377 }
1378
1379 network.ledger_secrets->adjust_previous_secret_stored_version(
1380 version);
1381
1382 network.tables->unset_map_hook(
1383 network.encrypted_ledger_secrets.get_name());
1384
1385 return {nullptr};
1386 }));
1387 }
1388
1389 //
1390 // funcs in state "readingPublicLedger" or "readingPrivateLedger"
1391 //
1393 {
1394 std::lock_guard<pal::Mutex> guard(lock);
1395
1396 if (is_reading_public_ledger())
1397 {
1398 recover_public_ledger_end_unsafe();
1399 }
1400 else if (is_reading_private_ledger())
1401 {
1402 recover_private_ledger_end_unsafe();
1403 }
1404 else
1405 {
1407 "Node in state {} cannot finalise ledger recovery", sm.value());
1408 return;
1409 }
1410 }
1411
1412 //
1413 // funcs in state "partOfPublicNetwork"
1414 //
1416 {
1417 recovery_store = std::make_shared<ccf::kv::Store>(
1418 true /* Check transactions in order */,
1419 true /* Make use of historical secrets */);
1420 auto recovery_history = std::make_shared<MerkleTxHistory>(
1421 *recovery_store,
1422 self,
1423 *node_sign_kp,
1424 sig_tx_interval,
1425 sig_ms_interval,
1426 false /* No signature timer on recovery_history */);
1427
1428 auto recovery_encryptor = make_encryptor();
1429
1430 recovery_store->set_history(recovery_history);
1431 recovery_store->set_encryptor(recovery_encryptor);
1432
1433 // Record real store version and root
1434 recovery_v = network.tables->current_version();
1435 auto* h = dynamic_cast<MerkleTxHistory*>(history.get());
1436 recovery_root = h->get_replicated_state_root();
1437
1438 if (startup_snapshot_info)
1439 {
1440 std::vector<ccf::kv::Version> view_history_;
1442 deserialise_snapshot(
1443 recovery_store,
1444 startup_snapshot_info->raw,
1445 hooks,
1446 &view_history_,
1447 false,
1448 config.recover.previous_service_identity);
1449 startup_snapshot_info.reset();
1450 }
1451
1453 "Recovery store successfully setup at {}. Target recovery seqno: {}",
1454 recovery_store->current_version(),
1455 recovery_v);
1456 }
1457
1459 {
1460 share_manager.shuffle_recovery_shares(tx);
1461 }
1462
1464 {
1465 auto* tx_ = dynamic_cast<ccf::kv::CommittableTx*>(&tx);
1466 if (tx_ == nullptr)
1467 {
1468 throw std::logic_error("Could not cast tx to CommittableTx");
1469 }
1470 tx_->set_tx_flag(
1472 }
1473
1475 {
1476 auto* committable_tx = dynamic_cast<ccf::kv::CommittableTx*>(&tx);
1477 if (committable_tx == nullptr)
1478 {
1479 throw std::logic_error("Could not cast tx to CommittableTx");
1480 }
1481 committable_tx->set_tx_flag(
1483 }
1484
1486 ccf::kv::Tx& tx,
1488 {
1489 std::lock_guard<pal::Mutex> guard(lock);
1490
1491 auto* service = tx.rw<Service>(Tables::SERVICE);
1492 auto service_info = service->get();
1493 if (!service_info.has_value())
1494 {
1495 throw std::logic_error(
1496 "Service information cannot be found to transition service to "
1497 "open");
1498 }
1499
1500 // Idempotence: if the service is already open or waiting for recovery
1501 // shares, this function should succeed with no effect
1502 if (
1503 service_info->status == ServiceStatus::WAITING_FOR_RECOVERY_SHARES ||
1504 service_info->status == ServiceStatus::OPEN)
1505 {
1507 "Service in state {} is already open", service_info->status);
1508 return;
1509 }
1510
1511 if (service_info->status == ServiceStatus::RECOVERING)
1512 {
1513 const auto prev_ident =
1514 tx.ro<PreviousServiceIdentity>(Tables::PREVIOUS_SERVICE_IDENTITY)
1515 ->get();
1516 if (!prev_ident.has_value() || !identities.previous.has_value())
1517 {
1518 throw std::logic_error(
1519 "Recovery with service certificates requires both, a previous "
1520 "service identity written to the KV during recovery genesis and a "
1521 "transition_service_to_open proposal that contains previous and "
1522 "next service certificates");
1523 }
1524
1525 const ccf::crypto::Pem from_proposal(
1526 identities.previous->data(), identities.previous->size());
1527 if (prev_ident.value() != from_proposal)
1528 {
1529 throw std::logic_error(fmt::format(
1530 "Previous service identity does not match.\nActual:\n{}\nIn "
1531 "proposal:\n{}",
1532 prev_ident->str(),
1533 from_proposal.str()));
1534 }
1535 }
1536
1537 if (identities.next != service_info->cert)
1538 {
1539 throw std::logic_error(fmt::format(
1540 "Service identity mismatch: the next service identity in the "
1541 "transition_service_to_open proposal does not match the current "
1542 "service identity:\nNext:\n{}\nCurrent:\n{}",
1543 identities.next.str(),
1544 service_info->cert.str()));
1545 }
1546
1547 if (is_part_of_public_network())
1548 {
1549 // If the node is in public mode, start accepting member recovery
1550 // shares
1551 ShareManager::clear_submitted_recovery_shares(tx);
1552 service_info->status = ServiceStatus::WAITING_FOR_RECOVERY_SHARES;
1553 service->put(service_info.value());
1554 if (config.recover.previous_sealed_ledger_secret_location.has_value())
1555 {
1556 tx.wo<LastRecoveryType>(Tables::LAST_RECOVERY_TYPE)
1557 ->put(RecoveryType::LOCAL_UNSEALING);
1558 auto unsealed_ls = unseal_ledger_secret();
1559 LOG_INFO_FMT("Unsealed ledger secret, initiating private recovery");
1560 initiate_private_recovery_unsafe(tx, unsealed_ls);
1561 }
1562 else
1563 {
1564 tx.wo<LastRecoveryType>(Tables::LAST_RECOVERY_TYPE)
1565 ->put(RecoveryType::RECOVERY_SHARES);
1566 }
1567 return;
1568 }
1569
1570 if (is_part_of_network())
1571 {
1572 // Otherwise, if the node is part of the network. Open the network
1573 // straight away. Recovery shares are allocated to each recovery
1574 // member.
1575 try
1576 {
1577 share_manager.issue_recovery_shares(tx);
1578 }
1579 catch (const std::logic_error& e)
1580 {
1581 throw std::logic_error(
1582 fmt::format("Failed to issue recovery shares: {}", e.what()));
1583 }
1584
1585 InternalTablesAccess::open_service(tx);
1586 InternalTablesAccess::endorse_previous_identity(
1587 tx, *network.identity->get_key_pair());
1588 trigger_snapshot(tx);
1589 return;
1590 }
1591
1592 throw std::logic_error(
1593 fmt::format("Node in state {} cannot open service", sm.value()));
1594 }
1595
1596 // Decrypts chain of ledger secrets, and writes those to the ledger
1597 // encrypted for each node. On a commit hook for this write, each node
1598 // (including this one!) will begin_private_recovery().
1600 ccf::kv::Tx& tx,
1601 const std::optional<LedgerSecretPtr>& unsealed_ledger_secret =
1602 std::nullopt)
1603 {
1604 sm.expect(NodeStartupState::partOfPublicNetwork);
1605 LedgerSecretsMap recovered_ledger_secrets =
1606 share_manager.restore_recovery_shares_info(
1607 tx, recovered_encrypted_ledger_secrets, unsealed_ledger_secret);
1608
1609 // Broadcast decrypted ledger secrets to other nodes for them to
1610 // initiate private recovery too
1611 LedgerSecretsBroadcast::broadcast_some(
1612 InternalTablesAccess::get_trusted_nodes(tx),
1613 tx.wo(network.secrets),
1614 recovered_ledger_secrets);
1615 }
1616
1618 ccf::kv::Tx& tx,
1619 const std::optional<LedgerSecretPtr>& unsealed_ledger_secret =
1620 std::nullopt) override
1621 {
1622 std::lock_guard<pal::Mutex> guard(lock);
1623 initiate_private_recovery_unsafe(tx, unsealed_ledger_secret);
1624 }
1625
1626 //
1627 // funcs in state "partOfNetwork" or "partOfPublicNetwork"
1628 //
1629 void tick(std::chrono::milliseconds elapsed)
1630 {
1631 if (
1632 !sm.check(NodeStartupState::partOfNetwork) &&
1633 !sm.check(NodeStartupState::partOfPublicNetwork) &&
1634 !sm.check(NodeStartupState::readingPrivateLedger))
1635 {
1636 return;
1637 }
1638
1639 consensus->periodic(elapsed);
1640
1641 if (sm.check(NodeStartupState::partOfNetwork))
1642 {
1643 const auto tx_id = consensus->get_committed_txid();
1644 indexer->update_strategies(elapsed, {tx_id.first, tx_id.second});
1645 }
1646
1647 n2n_channels->tick(elapsed);
1648 }
1649
1651 {
1652 if (
1653 !sm.check(NodeStartupState::partOfNetwork) &&
1654 !sm.check(NodeStartupState::partOfPublicNetwork) &&
1655 !sm.check(NodeStartupState::readingPrivateLedger))
1656 {
1657 return;
1658 }
1659
1660 consensus->periodic_end();
1661 }
1662
1663 void stop_notice() override
1664 {
1665 consensus->nominate_successor();
1666 stop_noticed = true;
1667 }
1668
1670 {
1671 return stop_noticed;
1672 }
1673
1674 void recv_node_inbound(const uint8_t* data, size_t size)
1675 {
1676 auto [msg_type, from, payload] =
1677 ringbuffer::read_message<node_inbound>(data, size);
1678
1679 const auto* payload_data = payload.data;
1680 auto payload_size = payload.size;
1681
1682 if (msg_type == NodeMsgType::forwarded_msg)
1683 {
1684 cmd_forwarder->recv_message(from, payload_data, payload_size);
1685 }
1686 else
1687 {
1688 // Only process messages once part of network
1689 if (
1690 !sm.check(NodeStartupState::partOfNetwork) &&
1691 !sm.check(NodeStartupState::partOfPublicNetwork) &&
1692 !sm.check(NodeStartupState::readingPrivateLedger))
1693 {
1695 "Ignoring node msg received too early - current state is {}",
1696 sm.value());
1697 return;
1698 }
1699
1700 switch (msg_type)
1701 {
1702 case channel_msg:
1703 {
1704 n2n_channels->recv_channel_message(
1705 from, payload_data, payload_size);
1706 break;
1707 }
1708
1709 case consensus_msg:
1710 {
1711 consensus->recv_message(from, payload_data, payload_size);
1712 break;
1713 }
1714
1715 default:
1716 {
1717 LOG_FAIL_FMT("Unknown node message type: {}", msg_type);
1718 return;
1719 }
1720 }
1721 }
1722 }
1723
1724 //
1725 // always available
1726 //
1727 [[nodiscard]] bool is_primary() const override
1728 {
1729 return (
1730 (sm.check(NodeStartupState::partOfNetwork) ||
1731 sm.check(NodeStartupState::partOfPublicNetwork) ||
1732 sm.check(NodeStartupState::readingPrivateLedger)) &&
1733 consensus->is_primary());
1734 }
1735
1736 bool can_replicate() override
1737 {
1738 return (
1739 (sm.check(NodeStartupState::partOfNetwork) ||
1740 sm.check(NodeStartupState::partOfPublicNetwork) ||
1741 sm.check(NodeStartupState::readingPrivateLedger)) &&
1742 consensus->can_replicate());
1743 }
1744
1745 std::optional<ccf::NodeId> get_primary() override
1746 {
1747 return consensus->primary();
1748 }
1749
1750 [[nodiscard]] bool is_in_initialised_state() const override
1751 {
1752 return sm.check(NodeStartupState::initialized);
1753 }
1754
1755 [[nodiscard]] bool is_part_of_network() const override
1756 {
1757 return sm.check(NodeStartupState::partOfNetwork);
1758 }
1759
1760 [[nodiscard]] bool is_reading_public_ledger() const override
1761 {
1762 return sm.check(NodeStartupState::readingPublicLedger);
1763 }
1764
1765 [[nodiscard]] bool is_reading_private_ledger() const override
1766 {
1767 return sm.check(NodeStartupState::readingPrivateLedger);
1768 }
1769
1770 [[nodiscard]] bool is_part_of_public_network() const override
1771 {
1772 return sm.check(NodeStartupState::partOfPublicNetwork);
1773 }
1774
1775 [[nodiscard]] bool is_accessible_to_members() const override
1776 {
1777 const auto val = sm.value();
1778 return val == NodeStartupState::partOfNetwork ||
1779 val == NodeStartupState::partOfPublicNetwork ||
1780 val == NodeStartupState::readingPrivateLedger;
1781 }
1782
1784 {
1785 std::lock_guard<pal::Mutex> guard(lock);
1786 auto s = sm.value();
1787 if (s == NodeStartupState::readingPrivateLedger)
1788 {
1789 return {s, recovery_v, recovery_store->current_version()};
1790 }
1791
1792 return {s, std::nullopt, std::nullopt};
1793 }
1794
1795 bool rekey_ledger(ccf::kv::Tx& tx) override
1796 {
1797 std::lock_guard<pal::Mutex> guard(lock);
1798 sm.expect(NodeStartupState::partOfNetwork);
1799
1800 // The ledger should not be re-keyed when the service is not open
1801 // because:
1802 // - While waiting for recovery shares, the submitted shares are stored
1803 // in a public table, encrypted with the ledger secret generated at
1804 // startup of the first recovery node
1805 // - On recovery, historical ledger secrets can only be looked up in the
1806 // ledger once all ledger secrets have been restored
1807 const auto service_status = InternalTablesAccess::get_service_status(tx);
1808 if (
1809 !service_status.has_value() ||
1810 service_status.value() != ServiceStatus::OPEN)
1811 {
1812 LOG_FAIL_FMT("Cannot rekey ledger while the service is not open");
1813 return false;
1814 }
1815
1816 // Effects of ledger rekey are only observed from the next transaction,
1817 // once the local hook on the secrets table has been triggered.
1818
1819 auto new_ledger_secret = make_ledger_secret();
1820 share_manager.issue_recovery_shares(tx, new_ledger_secret);
1821 LedgerSecretsBroadcast::broadcast_new(
1822 InternalTablesAccess::get_trusted_nodes(tx),
1823 tx.wo(network.secrets),
1824 std::move(new_ledger_secret));
1825
1826 return true;
1827 }
1828
1829 [[nodiscard]] NodeId get_node_id() const
1830 {
1831 return self;
1832 }
1833
1835 {
1836 std::lock_guard<pal::Mutex> guard(lock);
1837 return startup_seqno;
1838 }
1839
1841 {
1842 return rpcsessions->get_session_metrics();
1843 }
1844
1846 {
1847 std::lock_guard<pal::Mutex> guard(lock);
1848 return self_signed_node_cert;
1849 }
1850
1852 {
1853 if (history == nullptr)
1854 {
1855 throw std::logic_error(
1856 "Attempting to access COSE signatures config before history has been "
1857 "constructed");
1858 }
1859
1860 return history->get_cose_signatures_config();
1861 }
1862
1863 private:
1864 bool is_ip(const std::string_view& hostname)
1865 {
1866 // IP address components are purely numeric. DNS names may be largely
1867 // numeric, but at least the final component (TLD) must not be
1868 // all-numeric. So this distinguishes "1.2.3.4" (an IP address) from
1869 // "1.2.3.c4m" (a DNS name). "1.2.3." is invalid for either, and will
1870 // throw. Attempts to handle IPv6 by also splitting on ':', but this is
1871 // untested.
1872 const auto final_component =
1873 ccf::nonstd::split(ccf::nonstd::split(hostname, ".").back(), ":")
1874 .back();
1875 if (final_component.empty())
1876 {
1877 throw std::runtime_error(fmt::format(
1878 "{} has a trailing period, is not a valid hostname", hostname));
1879 }
1880
1881 return std::ranges::all_of(
1882 final_component, [](char c) { return c >= '0' && c <= '9'; });
1883 }
1884
1885 std::vector<ccf::crypto::SubjectAltName> get_subject_alternative_names()
1886 {
1887 // If no Subject Alternative Name (SAN) is passed in at node creation,
1888 // default to using node's RPC address as single SAN. Otherwise, use
1889 // specified SANs.
1890 if (!config.node_certificate.subject_alt_names.empty())
1891 {
1892 return ccf::crypto::sans_from_string_list(
1893 config.node_certificate.subject_alt_names);
1894 }
1895
1896 // Construct SANs from RPC interfaces, manually detecting whether each
1897 // is a domain name or IP
1898 std::vector<ccf::crypto::SubjectAltName> sans;
1899 for (const auto& [_, interface] : config.network.rpc_interfaces)
1900 {
1901 auto host = split_net_address(interface.published_address).first;
1902 sans.push_back({host, is_ip(host)});
1903 }
1904 return sans;
1905 }
1906
1907 void accept_node_tls_connections()
1908 {
1909 // Accept TLS connections, presenting self-signed (i.e. non-endorsed)
1910 // node certificate.
1911 rpcsessions->set_node_cert(
1912 self_signed_node_cert, node_sign_kp->private_key_pem());
1913 LOG_INFO_FMT("Node TLS connections now accepted");
1914 }
1915
1916 void accept_network_tls_connections()
1917 {
1918 // Accept TLS connections, presenting node certificate signed by network
1919 // certificate
1921 endorsed_node_cert.has_value(),
1922 "Node certificate should be endorsed before accepting endorsed "
1923 "client "
1924 "connections");
1925 if (auto cert_opt = endorsed_node_cert; cert_opt.has_value())
1926 {
1927 const auto& endorsed_cert = cert_opt.value();
1928 rpcsessions->set_network_cert(
1929 endorsed_cert, node_sign_kp->private_key_pem());
1930 }
1931 LOG_INFO_FMT("Network TLS connections now accepted");
1932 }
1933
1934 auto find_frontend(ActorsType actor)
1935 {
1936 auto fe = rpc_map->find(actor);
1937 if (!fe.has_value())
1938 {
1939 throw std::logic_error(
1940 fmt::format("Cannot find {} frontend", (int)actor));
1941 }
1942 return fe.value();
1943 }
1944
1945 void open_frontend(ActorsType actor)
1946 {
1947 find_frontend(actor)->open();
1948 }
1949
1950 void open_user_frontend()
1951 {
1952 open_frontend(ActorsType::users);
1953 }
1954
1955 bool is_member_frontend_open_unsafe()
1956 {
1957 return find_frontend(ActorsType::members)->is_open();
1958 }
1959
1960 bool is_member_frontend_open() override
1961 {
1962 std::lock_guard<pal::Mutex> guard(lock);
1963 return is_member_frontend_open_unsafe();
1964 }
1965
1966 bool is_user_frontend_open() override
1967 {
1968 std::lock_guard<pal::Mutex> guard(lock);
1969 return find_frontend(ActorsType::users)->is_open();
1970 }
1971
1972 std::vector<uint8_t> serialize_create_request(
1973 View create_view, bool create_consortium = true)
1974 {
1975 CreateNetworkNodeToNode::In create_params;
1976
1977 // False on recovery where the consortium is read from the existing
1978 // ledger
1979 if (create_consortium)
1980 {
1981 create_params.genesis_info = config.start;
1982 }
1983
1984 create_params.node_id = self;
1985 create_params.certificate_signing_request = node_sign_kp->create_csr(
1986 config.node_certificate.subject_name, subject_alt_names);
1987 create_params.node_endorsed_certificate =
1988 ccf::crypto::create_endorsed_cert(
1989 create_params.certificate_signing_request,
1990 config.startup_host_time,
1991 config.node_certificate.initial_validity_days,
1992 network.identity->priv_key,
1993 network.identity->cert);
1994
1995 // Even though endorsed certificate is updated on (global) hook, history
1996 // requires it to generate signatures
1997 history->set_endorsed_certificate(
1998 create_params.node_endorsed_certificate);
1999
2000 create_params.public_key = node_sign_kp->public_key_pem();
2001 create_params.service_cert = network.identity->cert;
2002 create_params.quote_info = quote_info;
2003 create_params.public_encryption_key = node_encrypt_kp->public_key_pem();
2004 create_params.measurement = node_measurement;
2005 create_params.snp_uvm_endorsements = snp_uvm_endorsements;
2006 create_params.snp_security_policy =
2007 config.attestation.environment.security_policy;
2008
2009 create_params.node_info_network = config.network;
2010 create_params.node_data = config.node_data;
2011 create_params.service_data = config.service_data;
2012 create_params.create_txid = {create_view, last_recovered_signed_idx + 1};
2013
2014 const auto body = nlohmann::json(create_params).dump();
2015
2016 ::http::Request request(
2017 fmt::format("/{}/{}", get_actor_prefix(ActorsType::nodes), "create"));
2018 request.set_header(
2019 ccf::http::headers::CONTENT_TYPE,
2020 ccf::http::headervalues::contenttype::JSON);
2021
2022 request.set_body(body);
2023
2024 return request.build_request();
2025 }
2026
2027 bool extract_create_result(const std::shared_ptr<RpcContext>& ctx)
2028 {
2029 if (ctx == nullptr)
2030 {
2031 LOG_FAIL_FMT("Expected non-null context");
2032 return false;
2033 }
2034
2035 const auto status = ctx->get_response_status();
2036 const auto& raw_body = ctx->get_response_body();
2037 if (status != HTTP_STATUS_OK)
2038 {
2040 "Create response is error: {} {}\n{}",
2041 status,
2042 ccf::http_status_str((ccf::http_status)status),
2043 std::string(raw_body.begin(), raw_body.end()));
2044 return false;
2045 }
2046
2047 const auto body = nlohmann::json::parse(raw_body);
2048 if (!body.is_boolean())
2049 {
2050 LOG_FAIL_FMT("Expected boolean body in create response");
2052 "Expected boolean body in create response: {}", body.dump());
2053 return false;
2054 }
2055
2056 return body;
2057 }
2058
2059 bool send_create_request(const std::vector<uint8_t>& packed)
2060 {
2061 auto node_session = std::make_shared<SessionContext>(
2062 InvalidSessionId, self_signed_node_cert.raw());
2063 auto ctx = make_rpc_context(node_session, packed);
2064
2065 std::shared_ptr<ccf::RpcHandler> search =
2066 ::http::fetch_rpc_handler(ctx, this->rpc_map);
2067
2068 search->process(ctx);
2069
2070 return extract_create_result(ctx);
2071 }
2072
2073 void create_and_send_boot_request(
2074 View create_view, bool create_consortium = true)
2075 {
2076 // Service creation transaction is asynchronous to avoid deadlocks
2077 // (e.g. https://github.com/microsoft/CCF/issues/3788)
2079 ccf::tasks::make_basic_task([this, create_view, create_consortium]() {
2080 if (!this->send_create_request(
2081 this->serialize_create_request(create_view, create_consortium)))
2082 {
2083 throw std::runtime_error(
2084 "Service creation request could not be committed");
2085 }
2086 if (create_consortium)
2087 {
2088 this->advance_part_of_network();
2089 }
2090 else
2091 {
2092 this->advance_part_of_public_network();
2093 }
2094 }));
2095 }
2096
2097 void begin_private_recovery()
2098 {
2099 sm.expect(NodeStartupState::partOfPublicNetwork);
2100
2101 LOG_INFO_FMT("Beginning private recovery");
2102
2103 setup_private_recovery_store();
2104
2105 reset_recovery_hook();
2106 setup_one_off_secret_hook();
2107
2108 // Start reading private security domain of ledger
2109 sm.advance(NodeStartupState::readingPrivateLedger);
2110 last_recovered_idx = recovery_store->current_version();
2111 read_ledger_entries(
2112 last_recovered_idx + 1, last_recovered_idx + recovery_batch_size);
2113 }
2114
2115 void setup_basic_hooks()
2116 {
2117 network.tables->set_map_hook(
2118 network.secrets.get_name(),
2120 [this](ccf::kv::Version hook_version, const Secrets::Write& w)
2122 // Used to rekey the ledger on a live service
2123 if (!is_part_of_network())
2124 {
2125 // Ledger rekey is not allowed during recovery
2126 return {nullptr};
2127 }
2128
2129 const auto& ledger_secrets_for_nodes = w;
2130 if (!ledger_secrets_for_nodes.has_value())
2131 {
2132 throw std::logic_error(fmt::format(
2133 "Unexpected removal from {} table",
2134 network.secrets.get_name()));
2135 }
2136
2137 for (const auto& [node_id, encrypted_ledger_secrets] :
2138 ledger_secrets_for_nodes.value())
2139 {
2140 if (node_id != self)
2141 {
2142 // Only consider ledger secrets for this node
2143 continue;
2144 }
2145
2146 for (const auto& encrypted_ledger_secret :
2147 encrypted_ledger_secrets)
2148 {
2149 auto plain_ledger_secret = LedgerSecretsBroadcast::decrypt(
2150 node_encrypt_kp, encrypted_ledger_secret.encrypted_secret);
2151
2152 // When rekeying, set the encryption key for the next version
2153 // onward (backups deserialise this transaction with the
2154 // previous ledger secret)
2155 auto ledger_secret = std::make_shared<LedgerSecret>(
2156 std::move(plain_ledger_secret), hook_version);
2157 seal_ledger_secret(hook_version + 1, ledger_secret);
2158 network.ledger_secrets->set_secret(
2159 hook_version + 1, std::move(ledger_secret));
2160 }
2161 }
2162
2163 return {nullptr};
2164 }));
2165
2166 network.tables->set_global_hook(
2167 network.secrets.get_name(),
2169 ccf::kv::Version hook_version,
2170 const Secrets::Write& w) {
2171 // Used on recovery to initiate private recovery on backup nodes.
2172 if (!is_part_of_public_network())
2173 {
2174 return;
2175 }
2176
2177 const auto& ledger_secrets_for_nodes = w;
2178 if (!ledger_secrets_for_nodes.has_value())
2179 {
2180 throw std::logic_error(fmt::format(
2181 "Unexpected removal from {} table", network.secrets.get_name()));
2182 }
2183
2184 for (const auto& [node_id, encrypted_ledger_secrets] :
2185 ledger_secrets_for_nodes.value())
2186 {
2187 if (node_id != self)
2188 {
2189 // Only consider ledger secrets for this node
2190 continue;
2191 }
2192
2193 LedgerSecretsMap restored_ledger_secrets = {};
2194 for (const auto& encrypted_ledger_secret : encrypted_ledger_secrets)
2195 {
2196 // On rekey, the version is inferred from the version at which
2197 // the hook is executed. Otherwise, on recovery, use the
2198 // version read from the write set.
2199 if (!encrypted_ledger_secret.version.has_value())
2200 {
2201 throw std::logic_error(fmt::format(
2202 "Commit hook at seqno {} for table {}: no version for "
2203 "encrypted ledger secret",
2204 hook_version,
2205 network.secrets.get_name()));
2206 }
2207
2208 auto plain_ledger_secret = LedgerSecretsBroadcast::decrypt(
2209 node_encrypt_kp, encrypted_ledger_secret.encrypted_secret);
2210
2211 restored_ledger_secrets.emplace(
2212 encrypted_ledger_secret.version.value(),
2213 std::make_shared<LedgerSecret>(
2214 std::move(plain_ledger_secret),
2215 encrypted_ledger_secret.previous_secret_stored_version));
2216 }
2217
2218 if (!restored_ledger_secrets.empty())
2219 {
2220 // When recovering, restore ledger secrets and trigger end of
2221 // recovery protocol (backup only)
2222 network.ledger_secrets->restore_historical(
2223 std::move(restored_ledger_secrets));
2224 begin_private_recovery();
2225 return;
2226 }
2227 }
2228
2230 "Found no ledger secrets for this node ({}) in global commit hook "
2231 "for {} @ {}",
2232 self,
2233 network.secrets.get_name(),
2234 hook_version);
2235 }));
2236
2237 network.tables->set_global_hook(
2238 network.nodes.get_name(),
2240 [this](ccf::kv::Version hook_version, const Nodes::Write& w) {
2241 std::vector<NodeId> retired_committed_nodes;
2242 for (const auto& [node_id, node_info] : w)
2243 {
2244 if (node_info.has_value() && node_info->retired_committed)
2245 {
2246 retired_committed_nodes.push_back(node_id);
2247 }
2248 }
2249 consensus->set_retired_committed(
2250 hook_version, retired_committed_nodes);
2251 }));
2252
2253 // Service-endorsed certificate is passed to history as early as _local_
2254 // commit since a new node may become primary (and thus, e.g. generate
2255 // signatures) before the transaction that added it is _globally_
2256 // committed (see https://github.com/microsoft/CCF/issues/4063). It is OK
2257 // if this transaction is rolled back as the node will no longer be part
2258 // of the service.
2259 network.tables->set_map_hook(
2260 network.node_endorsed_certificates.get_name(),
2262 [this](
2263 ccf::kv::Version hook_version,
2267 "[local] node_endorsed_certificates local hook at version {}, "
2268 "with {} writes",
2269 hook_version,
2270 w.size());
2271 for (auto const& [node_id, endorsed_certificate] : w)
2272 {
2273 if (node_id != self)
2274 {
2276 "[local] Ignoring endorsed certificate for other node {}",
2277 node_id);
2278 continue;
2279 }
2280
2281 if (!endorsed_certificate.has_value())
2282 {
2284 "[local] Endorsed cert for self ({}) has been deleted", self);
2285 throw std::logic_error(fmt::format(
2286 "Could not find endorsed node certificate for {}", self));
2287 }
2288
2289 std::lock_guard<pal::Mutex> guard(lock);
2290
2291 if (endorsed_node_cert.has_value())
2292 {
2294 "[local] Previous endorsed node cert was:\n{}",
2295 endorsed_node_cert->str());
2296 }
2297
2298 endorsed_node_cert = endorsed_certificate.value();
2300 "[local] Under lock, setting endorsed node cert to:\n{}",
2301 endorsed_node_cert->str());
2302 history->set_endorsed_certificate(endorsed_node_cert.value());
2303 n2n_channels->set_endorsed_node_cert(endorsed_node_cert.value());
2304 }
2305
2306 return {nullptr};
2307 }));
2308
2309 network.tables->set_global_hook(
2310 network.node_endorsed_certificates.get_name(),
2312 [this](
2313 ccf::kv::Version hook_version,
2316 "[global] node_endorsed_certificates global hook at version {}, "
2317 "with {} writes",
2318 hook_version,
2319 w.size());
2320 for (auto const& [node_id, endorsed_certificate] : w)
2321 {
2322 if (node_id != self)
2323 {
2325 "[global] Ignoring endorsed certificate for other node {}",
2326 node_id);
2327 continue;
2328 }
2329
2330 if (!endorsed_certificate.has_value())
2331 {
2333 "[global] Endorsed cert for self ({}) has been deleted",
2334 self);
2335 throw std::logic_error(fmt::format(
2336 "Could not find endorsed node certificate for {}", self));
2337 }
2338
2339 std::lock_guard<pal::Mutex> guard(lock);
2340
2341 LOG_INFO_FMT("[global] Accepting network connections");
2342 accept_network_tls_connections();
2343
2344 if (is_member_frontend_open_unsafe())
2345 {
2346 // Also, automatically refresh self-signed node certificate,
2347 // using the same validity period as the endorsed certificate.
2348 // Note that this is only done when the certificate is renewed
2349 // via proposal (i.e. when the member frontend is open), and not
2350 // for the initial addition of the node (the self-signed
2351 // certificate is output to disk then).
2352 auto [valid_from, valid_to] =
2353 ccf::crypto::make_verifier(endorsed_node_cert.value())
2354 ->validity_period();
2356 "[global] Member frontend is open, so refreshing self-signed "
2357 "node cert");
2359 "[global] Previously:\n{}", self_signed_node_cert.str());
2360 self_signed_node_cert = create_self_signed_cert(
2361 node_sign_kp,
2362 config.node_certificate.subject_name,
2363 subject_alt_names,
2364 valid_from,
2365 valid_to);
2366 LOG_INFO_FMT("[global] Now:\n{}", self_signed_node_cert.str());
2367
2368 LOG_INFO_FMT("[global] Accepting node connections");
2369 accept_node_tls_connections();
2370 }
2371 else
2372 {
2373 LOG_INFO_FMT("[global] Member frontend is NOT open");
2375 "[global] Self-signed node cert remains:\n{}",
2376 self_signed_node_cert.str());
2377 }
2378
2379 LOG_INFO_FMT("[global] Opening members frontend");
2380 open_frontend(ActorsType::members);
2381 }
2382 }));
2383
2384 network.tables->set_global_hook(
2385 network.service.get_name(),
2387 [this](ccf::kv::Version hook_version, const Service::Write& w) {
2388 if (!w.has_value())
2389 {
2390 throw std::logic_error("Unexpected deletion in service value");
2391 }
2392
2393 // Service open on historical service has no effect
2394 auto hook_pubk_pem = ccf::crypto::public_key_pem_from_cert(
2396 auto current_pubk_pem =
2397 ccf::crypto::make_ec_key_pair(network.identity->priv_key)
2398 ->public_key_pem();
2399 if (hook_pubk_pem != current_pubk_pem)
2400 {
2402 "Ignoring historical service open at seqno {} for {}",
2403 hook_version,
2404 w->cert.str());
2405 return;
2406 }
2407
2409 "Executing global hook for service table at {}, to service "
2410 "status {}. Cert is:\n{}",
2411 hook_version,
2412 w->status,
2413 w->cert.str());
2414
2415 network.identity->set_certificate(w->cert);
2416 if (w->status == ServiceStatus::OPEN)
2417 {
2418 open_user_frontend();
2419
2420 RINGBUFFER_WRITE_MESSAGE(::consensus::ledger_open, to_host);
2421 LOG_INFO_FMT("Service open at seqno {}", hook_version);
2422 }
2423 }));
2424 }
2425
2426 ccf::kv::Version get_last_recovered_signed_idx() override
2427 {
2428 // On recovery, only one node recovers the public ledger and is thus
2429 // aware of the version at which the new ledger secret is applicable
2430 // from. If the primary changes while the network is public-only, the
2431 // new primary should also know at which version the new ledger secret
2432 // is applicable from.
2433 std::lock_guard<pal::Mutex> guard(lock);
2434 return last_recovered_signed_idx;
2435 }
2436
2437 void setup_recovery_hook()
2438 {
2439 network.tables->set_map_hook(
2440 network.encrypted_ledger_secrets.get_name(),
2442 [this](
2443 ccf::kv::Version version,
2446 auto encrypted_ledger_secret_info = w;
2447 if (!encrypted_ledger_secret_info.has_value())
2448 {
2449 throw std::logic_error(fmt::format(
2450 "Unexpected removal from {} table",
2451 network.encrypted_ledger_secrets.get_name()));
2452 }
2453
2454 // If the version of the next ledger secret is not set, deduce it
2455 // from the hook version (i.e. ledger rekey)
2456 if (!encrypted_ledger_secret_info->next_version.has_value())
2457 {
2458 encrypted_ledger_secret_info->next_version = version + 1;
2459 }
2460
2461 if (encrypted_ledger_secret_info->previous_ledger_secret
2462 .has_value())
2463 {
2464 LOG_DEBUG_FMT(
2465 "Recovering encrypted ledger secret valid at seqno {}",
2466 encrypted_ledger_secret_info->previous_ledger_secret->version);
2467 }
2468
2469 recovered_encrypted_ledger_secrets.emplace_back(
2470 std::move(encrypted_ledger_secret_info.value()));
2471
2472 return {nullptr};
2473 }));
2474 }
2475
2476 void reset_recovery_hook()
2477 {
2478 network.tables->unset_map_hook(
2479 network.encrypted_ledger_secrets.get_name());
2480 }
2481
2482 void setup_n2n_channels(
2483 const std::optional<ccf::crypto::Pem>& endorsed_node_certificate_ =
2484 std::nullopt)
2485 {
2486 // If the endorsed node certificate is available at the time the
2487 // consensus/node-to-node channels are initialised, use it (i.e. join).
2488 // Otherwise, specify it later, on endorsed certificate table hook (i.e.
2489 // start or recover).
2490 n2n_channels->initialize(
2491 self, network.identity->cert, node_sign_kp, endorsed_node_certificate_);
2492 }
2493
2494 void setup_cmd_forwarder()
2495 {
2496 cmd_forwarder->initialize(self);
2497 }
2498
2499 void setup_history()
2500 {
2501 if (history)
2502 {
2503 throw std::logic_error("History already initialised");
2504 }
2505
2506 history = std::make_shared<MerkleTxHistory>(
2507 *network.tables,
2508 self,
2509 *node_sign_kp,
2510 sig_tx_interval,
2511 sig_ms_interval,
2512 false /* start timed signatures after first tx */);
2513 network.tables->set_history(history);
2514 }
2515
2516 void setup_encryptor()
2517 {
2518 if (encryptor)
2519 {
2520 throw std::logic_error("Encryptor already initialised");
2521 }
2522
2523 encryptor = make_encryptor();
2524 network.tables->set_encryptor(encryptor);
2525 }
2526
2527 void setup_consensus(
2528 bool public_only = false,
2529 const std::optional<ccf::crypto::Pem>& endorsed_node_certificate_ =
2530 std::nullopt)
2531 {
2532 setup_n2n_channels(endorsed_node_certificate_);
2533 setup_cmd_forwarder();
2534
2535 auto shared_state = std::make_shared<aft::State>(self);
2536
2537 auto node_client = std::make_shared<HTTPNodeClient>(
2538 rpc_map, node_sign_kp, self_signed_node_cert, endorsed_node_cert);
2539
2540 consensus = std::make_shared<RaftType>(
2541 consensus_config,
2542 std::make_unique<aft::Adaptor<ccf::kv::Store>>(network.tables),
2543 std::make_unique<::consensus::LedgerEnclave>(writer_factory),
2544 n2n_channels,
2545 shared_state,
2546 node_client,
2547 public_only);
2548
2549 network.tables->set_consensus(consensus);
2550 network.tables->set_snapshotter(snapshotter);
2551
2552 // When a node is added, even locally, inform consensus so that it
2553 // can add a new active configuration.
2554 network.tables->set_map_hook(
2555 network.nodes.get_name(),
2557 [](ccf::kv::Version version, const Nodes::Write& w)
2559 return std::make_unique<ConfigurationChangeHook>(version, w);
2560 }));
2561
2562 // Note: The Signatures hook and SerialisedMerkleTree hook are separate
2563 // because the signature and the Merkle tree are recorded in distinct
2564 // tables (for serialisation performance reasons). However here, they are
2565 // expected to always be called together and for the same version as they
2566 // are always written by each signature transaction.
2567
2568 network.tables->set_map_hook(
2569 network.signatures.get_name(),
2571 [s = this->snapshotter](
2572 ccf::kv::Version version,
2574 assert(w.has_value());
2575 auto sig = w.value();
2576 s->record_signature(version, sig.sig, sig.node, sig.cert);
2577 return {nullptr};
2578 }));
2579
2580 network.tables->set_map_hook(
2581 network.serialise_tree.get_name(),
2583 [s = this->snapshotter](
2584 ccf::kv::Version version,
2586 assert(w.has_value());
2587 const auto& tree = w.value();
2588 s->record_serialised_tree(version, tree);
2589 return {nullptr};
2590 }));
2591
2592 network.tables->set_map_hook(
2593 network.snapshot_evidence.get_name(),
2595 [s = this->snapshotter](
2596 ccf::kv::Version version,
2598 assert(w.has_value());
2599 auto snapshot_evidence = w.value();
2600 s->record_snapshot_evidence_idx(version, snapshot_evidence);
2601 return {nullptr};
2602 }));
2603
2604 setup_basic_hooks();
2605 }
2606
2607 void setup_snapshotter()
2608 {
2609 if (snapshotter)
2610 {
2611 throw std::logic_error("Snapshotter already initialised");
2612 }
2613 snapshotter = std::make_shared<Snapshotter>(
2614 writer_factory, network.tables, config.snapshots.tx_count);
2615 }
2616
2617 void read_ledger_entries(::consensus::Index from, ::consensus::Index to)
2618 {
2620 ::consensus::ledger_get_range,
2621 to_host,
2622 from,
2623 to,
2625 }
2626
2627 void ledger_truncate(::consensus::Index idx, bool recovery_mode = false)
2628 {
2630 ::consensus::ledger_truncate, to_host, idx, recovery_mode);
2631 }
2632
2633 void seal_ledger_secret(const VersionedLedgerSecret& ledger_secret)
2634 {
2635 seal_ledger_secret(ledger_secret.first, ledger_secret.second);
2636 }
2637
2638 void seal_ledger_secret(
2639 const kv::Version& version, const LedgerSecretPtr& ledger_secret)
2640 {
2641 if (!config.sealed_ledger_secret_location.has_value())
2642 {
2643 return;
2644 }
2645
2646 CCF_ASSERT(
2647 snp_tcb_version.has_value(), "TCB version must be set before sealing");
2648
2649 if (auto loc_opt = config.sealed_ledger_secret_location;
2650 loc_opt.has_value())
2651 {
2652 if (auto tcb_opt = snp_tcb_version; tcb_opt.has_value())
2653 {
2654 const auto& sealed_location = loc_opt.value();
2655 const auto& tcb_version = tcb_opt.value();
2657 sealed_location, tcb_version, version, ledger_secret);
2658 }
2659 }
2660 }
2661
2662 LedgerSecretPtr unseal_ledger_secret()
2663 {
2664 CCF_ASSERT(
2665 snp_tcb_version.has_value(),
2666 "TCB version must be set when unsealing ledger secret");
2667
2668 CCF_ASSERT(
2669 config.recover.previous_sealed_ledger_secret_location.has_value(),
2670 "Previous sealed ledger secret location must be set");
2671
2672 if (auto path_opt = config.recover.previous_sealed_ledger_secret_location;
2673 path_opt.has_value())
2674 {
2675 const auto& ledger_secret_path = path_opt.value();
2676 auto max_version = network.tables->current_version();
2678 ledger_secret_path, max_version);
2679 }
2680
2681 return nullptr;
2682 }
2683
2684 public:
2685 void set_n2n_message_limit(size_t message_limit)
2686 {
2687 n2n_channels->set_message_limit(message_limit);
2688 }
2689
2690 void set_n2n_idle_timeout(std::chrono::milliseconds idle_timeout)
2691 {
2692 n2n_channels->set_idle_timeout(idle_timeout);
2693 }
2694
2695 [[nodiscard]] const ccf::StartupConfig& get_node_config() const override
2696 {
2697 return config;
2698 }
2699
2701 {
2702 return network.identity->cert;
2703 }
2704
2705 // Stop-gap until it becomes easier to use other HTTP clients
2707 const ::http::URL& url,
2708 ::http::Request&& req,
2709 std::function<bool(
2710 ccf::http_status status, http::HeaderMap&&, std::vector<uint8_t>&&)>
2711 callback,
2712 const std::vector<std::string>& ca_certs = {},
2713 const std::string& app_protocol = "HTTP1",
2714 bool authenticate_as_node_client_certificate = false) override
2715 {
2716 std::optional<ccf::crypto::Pem> client_cert = std::nullopt;
2717 std::optional<ccf::crypto::Pem> client_cert_key = std::nullopt;
2718 if (authenticate_as_node_client_certificate)
2719 {
2720 client_cert =
2721 endorsed_node_cert ? *endorsed_node_cert : self_signed_node_cert;
2722 client_cert_key = node_sign_kp->private_key_pem();
2723 }
2724
2725 auto ca = std::make_shared<::tls::CA>(ca_certs, true);
2726 std::shared_ptr<::tls::Cert> ca_cert =
2727 std::make_shared<::tls::Cert>(ca, client_cert, client_cert_key);
2728 auto client = rpcsessions->create_client(ca_cert, app_protocol);
2729 client->connect(
2730 url.host,
2731 url.port,
2732 [callback](
2733 ccf::http_status status,
2734 http::HeaderMap&& headers,
2735 std::vector<uint8_t>&& data) {
2736 return callback(status, std::move(headers), std::move(data));
2737 });
2738 client->send_request(std::move(req));
2739 }
2740
2741 void write_snapshot(std::span<uint8_t> snapshot_buf, size_t request_id)
2742 {
2743 snapshotter->write_snapshot(snapshot_buf, request_id);
2744 }
2745
2746 std::shared_ptr<ccf::kv::Store> get_store() override
2747 {
2748 return network.tables;
2749 }
2750
2752 {
2753 return writer_factory;
2754 }
2755 };
2756}
#define CCF_ASSERT_FMT(expr,...)
Definition ccf_assert.h:10
#define CCF_ASSERT(expr, msg)
Definition ccf_assert.h:14
Definition raft_types.h:41
Definition raft.h:98
Definition node_interface.h:22
static std::optional< pal::snp::Attestation > get_snp_attestation(const QuoteInfo &quote_info)
Definition quote.cpp:146
static std::optional< HostData > get_host_data(const QuoteInfo &quote_info)
Definition quote.cpp:169
static QuoteVerificationResult verify_quote_against_store(ccf::kv::ReadOnlyTx &tx, const QuoteInfo &quote_info, const std::vector< uint8_t > &expected_node_public_key_der, pal::PlatformAttestationMeasurement &measurement)
Definition quote.cpp:311
static std::optional< pal::PlatformAttestationMeasurement > get_measurement(const QuoteInfo &quote_info)
Definition quote.cpp:129
Definition history.h:542
void set_node_id(const NodeId &id_)
Definition history.h:676
ccf::crypto::Sha256Hash get_replicated_state_root() override
Definition history.h:716
static bool open_service(ccf::kv::Tx &tx)
Definition internal_tables_access.h:683
static bool endorse_previous_identity(ccf::kv::Tx &tx, const ccf::crypto::ECKeyPair_OpenSSL &service_key)
Definition internal_tables_access.h:540
Definition node_state.h:87
void write_snapshot(std::span< uint8_t > snapshot_buf, size_t request_id)
Definition node_state.h:2741
void start_ledger_recovery_unsafe()
Definition node_state.h:958
bool rekey_ledger(ccf::kv::Tx &tx) override
Definition node_state.h:1795
bool is_part_of_public_network() const override
Definition node_state.h:1770
NodeState(ringbuffer::AbstractWriterFactory &writer_factory, NetworkState &network, std::shared_ptr< RPCSessions > rpcsessions, ccf::crypto::CurveID curve_id_)
Definition node_state.h:214
void setup_one_off_secret_hook()
Definition node_state.h:1359
void tick_end()
Definition node_state.h:1650
void recover_private_ledger_entries(const std::vector< uint8_t > &entries)
Definition node_state.h:1184
void recv_node_inbound(const uint8_t *data, size_t size)
Definition node_state.h:1674
ccf::crypto::Pem get_network_cert() override
Definition node_state.h:2700
bool is_reading_private_ledger() const override
Definition node_state.h:1765
std::optional< ccf::NodeId > get_primary() override
Definition node_state.h:1745
void trigger_ledger_chunk(ccf::kv::Tx &tx) override
Definition node_state.h:1463
bool is_primary() const override
Definition node_state.h:1727
void recover_public_ledger_end_unsafe()
Definition node_state.h:1090
SessionMetrics get_session_metrics() override
Definition node_state.h:1840
std::shared_ptr< ccf::kv::Store > get_store() override
Definition node_state.h:2746
void initiate_join()
Definition node_state.h:898
void advance_part_of_network()
Definition node_state.h:1079
void advance_part_of_public_network()
Definition node_state.h:1071
ccf::kv::Version get_startup_snapshot_seqno() override
Definition node_state.h:1834
const ccf::COSESignaturesConfig & get_cose_signatures_config() override
Definition node_state.h:1851
ringbuffer::AbstractWriterFactory & get_writer_factory() override
Definition node_state.h:2751
void auto_refresh_jwt_keys()
Definition node_state.h:922
void recover_ledger_end()
Definition node_state.h:1392
void initiate_private_recovery(ccf::kv::Tx &tx, const std::optional< LedgerSecretPtr > &unsealed_ledger_secret=std::nullopt) override
Definition node_state.h:1617
void initiate_join_unsafe()
Definition node_state.h:645
void stop_notice() override
Definition node_state.h:1663
void set_n2n_message_limit(size_t message_limit)
Definition node_state.h:2685
void make_http_request(const ::http::URL &url, ::http::Request &&req, std::function< bool(ccf::http_status status, http::HeaderMap &&, std::vector< uint8_t > &&)> callback, const std::vector< std::string > &ca_certs={}, const std::string &app_protocol="HTTP1", bool authenticate_as_node_client_certificate=false) override
Definition node_state.h:2706
bool can_replicate() override
Definition node_state.h:1736
void transition_service_to_open(ccf::kv::Tx &tx, AbstractGovernanceEffects::ServiceIdentities identities) override
Definition node_state.h:1485
QuoteVerificationResult verify_quote(ccf::kv::ReadOnlyTx &tx, const QuoteInfo &quote_info_, const std::vector< uint8_t > &expected_node_public_key_der, pal::PlatformAttestationMeasurement &measurement) override
Definition node_state.h:231
bool is_reading_public_ledger() const override
Definition node_state.h:1760
void launch_node()
Definition node_state.h:278
void initiate_quote_generation()
Definition node_state.h:408
void initialize(const ccf::consensus::Configuration &consensus_config_, std::shared_ptr< RPCMap > rpc_map_, std::shared_ptr< AbstractRPCResponder > rpc_sessions_, std::shared_ptr< indexing::Indexer > indexer_, size_t sig_tx_interval_, size_t sig_ms_interval_)
Definition node_state.h:244
void setup_private_recovery_store()
Definition node_state.h:1415
const ccf::StartupConfig & get_node_config() const override
Definition node_state.h:2695
void trigger_snapshot(ccf::kv::Tx &tx) override
Definition node_state.h:1474
bool is_in_initialised_state() const override
Definition node_state.h:1750
bool has_received_stop_notice() override
Definition node_state.h:1669
bool is_part_of_network() const override
Definition node_state.h:1755
void start_join_timer()
Definition node_state.h:904
ExtendedState state() override
Definition node_state.h:1783
void trigger_recovery_shares_refresh(ccf::kv::Tx &tx) override
Definition node_state.h:1458
void set_n2n_idle_timeout(std::chrono::milliseconds idle_timeout)
Definition node_state.h:2690
NodeId get_node_id() const
Definition node_state.h:1829
void tick(std::chrono::milliseconds elapsed)
Definition node_state.h:1629
NodeCreateInfo create(StartType start_type_, const ccf::StartupConfig &config_, std::vector< uint8_t > &&startup_snapshot_)
Definition node_state.h:549
void recover_public_ledger_entries(const std::vector< uint8_t > &entries)
Definition node_state.h:973
void recover_private_ledger_end_unsafe()
Definition node_state.h:1256
void initiate_private_recovery_unsafe(ccf::kv::Tx &tx, const std::optional< LedgerSecretPtr > &unsealed_ledger_secret=std::nullopt)
Definition node_state.h:1599
ccf::crypto::Pem get_self_signed_certificate() override
Definition node_state.h:1845
size_t get_jwt_attempts() override
Definition node_state.h:950
bool is_accessible_to_members() const override
Definition node_state.h:1775
Definition share_manager.h:168
static void clear_submitted_recovery_shares(ccf::kv::Tx &tx)
Definition share_manager.h:651
Definition ec_key_pair.h:16
Definition pem.h:18
const std::string & str() const
Definition pem.h:46
Definition sha256_hash.h:16
Definition committable_tx.h:19
Definition tx.h:159
M::ReadOnlyHandle * ro(M &m)
Definition tx.h:168
Definition tx.h:200
M::Handle * rw(M &m)
Definition tx.h:211
M::WriteOnlyHandle * wo(M &m)
Definition tx.h:232
std::map< K, std::optional< V > > Write
Definition map.h:40
static ccf::kv::untyped::CommitHook wrap_commit_hook(const CommitHook &hook)
Definition map.h:73
static ccf::kv::untyped::MapHook wrap_map_hook(const MapHook &hook)
Definition map.h:80
Definition value.h:32
static ccf::kv::untyped::CommitHook wrap_commit_hook(const CommitHook &hook)
Definition value.h:65
static ccf::kv::untyped::MapHook wrap_map_hook(const MapHook &hook)
Definition value.h:72
std::optional< V > Write
Definition value.h:38
static std::vector< uint8_t > get_entry(const uint8_t *&data, size_t &size)
Definition ledger_enclave.h:26
Definition state_machine.h:15
void expect(T state_) const
Definition state_machine.h:25
void advance(T state_)
Definition state_machine.h:45
bool check(T state_) const
Definition state_machine.h:35
void set_header(std::string k, const std::string &v)
Definition http_builder.h:45
void set_body(const std::vector< uint8_t > *b, bool overwrite_content_length=true)
Definition http_builder.h:72
Definition http_builder.h:117
Definition ring_buffer_types.h:157
StartType
Definition enclave_interface_types.h:92
@ Join
Definition enclave_interface_types.h:94
@ Recover
Definition enclave_interface_types.h:95
@ Start
Definition enclave_interface_types.h:93
#define LOG_INFO_FMT
Definition internal_logger.h:15
#define LOG_TRACE_FMT
Definition internal_logger.h:13
#define LOG_DEBUG_FMT
Definition internal_logger.h:14
#define LOG_FAIL_FMT
Definition internal_logger.h:16
CurveID
Definition curve.h:18
std::vector< uint8_t > raw_from_b64(const std::string_view &b64_string)
Definition base64.cpp:12
ccf::crypto::Pem public_key_pem_from_cert(const std::vector< uint8_t > &der)
Definition verifier.cpp:48
std::string b64url_from_raw(const uint8_t *data, size_t size, bool with_padding=true)
Definition base64.cpp:51
ECKeyPairPtr make_ec_key_pair(CurveID curve_id=service_identity_curve_choice)
Definition ec_key_pair.cpp:530
VerifierPtr make_verifier(const std::vector< uint8_t > &cert)
Definition verifier.cpp:18
std::string get_subject_name(const Pem &cert)
Definition verifier.cpp:53
std::vector< uint8_t > cert_pem_to_der(const Pem &pem)
Definition verifier.cpp:38
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
if(argc !=1)
Definition kv_helpers.h:73
void register_class_ids()
Definition global_class_ids.cpp:17
std::map< ccf::kv::serialisers::SerialisedEntry, std::optional< ccf::kv::serialisers::SerialisedEntry > > Write
Definition untyped.h:16
uint64_t Term
Definition kv_types.h:43
std::unique_ptr< ConsensusHook > ConsensusHookPtr
Definition hooks.h:21
@ SUCCESS
Definition kv_types.h:210
uint64_t Version
Definition version.h:8
ApplyResult
Definition kv_types.h:302
@ PASS_SIGNATURE
Definition kv_types.h:304
@ FAIL
Definition kv_types.h:311
std::vector< ConsensusHookPtr > ConsensusHookPtrs
Definition hooks.h:22
UVMEndorsements verify_uvm_endorsements_descriptor(const std::vector< uint8_t > &uvm_endorsements_raw, const pal::PlatformAttestationMeasurement &uvm_measurement)
Definition uvm_endorsements.cpp:648
std::mutex Mutex
Definition locking.h:12
Task make_basic_task(Ts &&... ts)
Definition basic_task.h:33
void add_periodic_task(Task task, std::chrono::milliseconds initial_delay, std::chrono::milliseconds repeat_period)
Definition task_system.cpp:75
void add_task(Task task)
Definition task_system.cpp:65
std::shared_ptr< BaseTask > Task
Definition task.h:36
Definition app_interface.h:14
LedgerSecretPtr find_and_unseal_ledger_secret_from_disk(const std::string &sealed_secret_dir, kv::Version max_version)
Definition local_sealing.h:191
NodeId compute_node_id_from_kp(const ccf::crypto::ECKeyPairPtr &node_sign_kp)
Definition nodes.h:43
constexpr auto get_actor_prefix(ActorsType at)
Definition actors.h:24
std::shared_ptr< ccf::kv::Store > make_store()
Definition network_tables.h:39
std::vector< uint8_t > CoseSignature
Definition signatures.h:64
std::tuple< NodeStartupState, std::optional< ccf::kv::Version >, std::optional< ccf::kv::Version > > ExtendedState
Definition node_operation_interface.h:20
std::list< EncryptedLedgerSecretInfo > RecoveredEncryptedLedgerSecrets
Definition share_manager.h:158
view
Definition signatures.h:54
std::map< ccf::kv::Version, LedgerSecretPtr > LedgerSecretsMap
Definition ledger_secrets.h:21
void reset_data(std::vector< uint8_t > &data)
Definition node_state.h:80
LedgerSecretPtr make_ledger_secret()
Definition ledger_secret.h:81
NodeStartupState
Definition node_startup_state.h:10
llhttp_status http_status
Definition http_status.h:9
QuoteVerificationResult
Definition quote.h:18
std::shared_ptr< LedgerSecret > LedgerSecretPtr
Definition ledger_secret.h:79
constexpr View VIEW_UNKNOWN
Definition tx_id.h:26
uint64_t View
Definition tx_id.h:23
void seal_ledger_secret_to_disk(const std::string &sealed_secret_dir, const ccf::pal::snp::TcbVersionRaw &tcb_version, const kv::Version &version, const LedgerSecretPtr &ledger_secret)
Definition local_sealing.h:107
@ channel_msg
Definition node_types.h:22
@ consensus_msg
Definition node_types.h:23
void to_json(nlohmann::json &j, const ClaimsDigest &hash)
Definition claims_digest.h:49
std::shared_ptr<::http::HttpRpcContext > make_rpc_context(std::shared_ptr< ccf::SessionContext > s, const std::vector< uint8_t > &packed)
Definition http_rpc_context.h:374
Definition perf_client.h:12
Definition consensus_types.h:23
uint64_t Index
Definition ledger_enclave_types.h:11
@ Recovery
Definition ledger_enclave_types.h:15
Definition configuration.h:14
URL parse_url_full(const std::string &url)
Definition http_parser.h:151
std::shared_ptr< AbstractWriter > WriterPtr
Definition ring_buffer_types.h:154
STL namespace.
#define RINGBUFFER_WRITE_MESSAGE(MSG,...)
Definition ring_buffer_types.h:259
Definition gov_effects_interface.h:22
std::optional< ccf::crypto::Pem > previous
Definition gov_effects_interface.h:23
ccf::crypto::Pem next
Definition gov_effects_interface.h:24
std::optional< std::string > security_policy
Definition startup_config.h:82
std::optional< std::string > uvm_endorsements
Definition startup_config.h:83
std::optional< std::string > snp_endorsements
Definition startup_config.h:84
ccf::pal::snp::EndorsementsServers snp_endorsements_servers
Definition startup_config.h:73
std::optional< std::string > snp_endorsements_file
Definition startup_config.h:76
Environment environment
Definition startup_config.h:88
ccf::ds::TimeString key_refresh_interval
Definition startup_config.h:65
std::string subject_name
Definition startup_config.h:35
size_t initial_validity_days
Definition startup_config.h:38
ccf::NodeInfoNetwork network
Definition startup_config.h:31
JWT jwt
Definition startup_config.h:69
Attestation attestation
Definition startup_config.h:92
NodeCertificateInfo node_certificate
Definition startup_config.h:42
Definition cose_signatures_config.h:12
Definition node_call_types.h:83
QuoteInfo quote_info
Definition node_call_types.h:85
std::optional< ccf::crypto::Pem > certificate_signing_request
Definition node_call_types.h:88
std::optional< ccf::kv::Version > startup_seqno
Definition node_call_types.h:87
NodeInfoNetwork node_info_network
Definition node_call_types.h:84
nlohmann::json node_data
Definition node_call_types.h:90
ccf::crypto::Pem public_encryption_key
Definition node_call_types.h:86
Definition node_call_types.h:94
std::optional< NetworkInfo > network_info
Definition node_call_types.h:149
NodeStatus node_status
Definition node_call_types.h:95
Definition network_state.h:12
std::shared_ptr< LedgerSecrets > ledger_secrets
Definition network_state.h:14
std::unique_ptr< NetworkIdentity > identity
Definition network_state.h:13
std::shared_ptr< ccf::kv::Store > tables
Definition network_tables.h:46
const JwtIssuers jwt_issuers
Definition network_tables.h:164
const Signatures signatures
Definition network_tables.h:222
Definition node_state.h:75
ccf::crypto::Pem service_cert
Definition node_state.h:77
ccf::crypto::Pem self_signed_node_cert
Definition node_state.h:76
Describes a quote (attestation) from trusted hardware.
Definition quote_info.h:26
QuoteFormat format
Quote format.
Definition quote_info.h:28
std::optional< std::vector< uint8_t > > uvm_endorsements
UVM endorsements (SNP-only)
Definition quote_info.h:34
std::vector< uint8_t > quote
Enclave quote.
Definition quote_info.h:30
std::vector< uint8_t > endorsements
Quote endorsements.
Definition quote_info.h:32
Definition session_metrics.h:13
std::vector< uint8_t > service_cert
Definition startup_config.h:138
bool follow_redirect
Definition startup_config.h:139
ccf::NodeInfoNetwork::NetAddress target_rpc_address
Definition startup_config.h:136
ccf::ds::TimeString retry_timeout
Definition startup_config.h:137
std::optional< std::vector< uint8_t > > previous_service_identity
Definition startup_config.h:145
Definition startup_config.h:106
size_t initial_service_certificate_validity_days
Definition startup_config.h:114
ccf::COSESignaturesConfig cose_signatures
Definition startup_config.h:116
Join join
Definition startup_config.h:141
nlohmann::json node_data
Definition startup_config.h:122
Recover recover
Definition startup_config.h:150
std::string startup_host_time
Definition startup_config.h:110
std::string service_subject_name
Definition startup_config.h:115
Definition consensus_config.h:11
Definition cose_common.h:57
size_t count_s() const
Definition unit_strings.h:197
const std::string & get_name() const
Definition get_name.h:18
Definition measurement.h:123
Definition report_data.h:56
Definition attestation_sev_snp_endorsements.h:20
Definition attestation_sev_snp.h:387