CCF
Loading...
Searching...
No Matches
enclave.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#include "ccf/app_interface.h"
5#include "ccf/ds/logger.h"
7#include "ccf/node_context.h"
9#include "ccf/pal/enclave.h"
10#include "ccf/pal/mem.h"
11#include "crypto/openssl/hash.h"
12#include "ds/oversized.h"
13#include "enclave_time.h"
16#include "interface.h"
17#include "js/ffi_plugins.h"
21#include "node/network_state.h"
22#include "node/node_state.h"
23#include "node/node_types.h"
26#include "node/rpc/forwarder.h"
34#include "ringbuffer_logger.h"
35#include "rpc_map.h"
36#include "rpc_sessions.h"
37#include "verify.h"
38
39#include <openssl/engine.h>
40
41namespace ccf
42{
43 class Enclave
44 {
45 private:
46 std::unique_ptr<ringbuffer::Circuit> circuit;
47 std::unique_ptr<ringbuffer::WriterFactory> basic_writer_factory;
48 std::unique_ptr<oversized::WriterFactory> writer_factory;
49 RingbufferLogger* ringbuffer_logger = nullptr;
50 ccf::NetworkState network;
51 std::shared_ptr<RPCMap> rpc_map;
52 std::shared_ptr<RPCSessions> rpcsessions;
53 std::unique_ptr<ccf::NodeState> node;
54 ringbuffer::WriterPtr to_host = nullptr;
55 std::chrono::microseconds last_tick_time;
56#if !(defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3)
57 ENGINE* rdrand_engine = nullptr;
58#endif
59
60 StartType start_type;
61
62 struct NodeContext : public ccf::AbstractNodeContext
63 {
64 const ccf::NodeId this_node;
65
66 NodeContext(const ccf::NodeId& id) : this_node(id) {}
67
68 ccf::NodeId get_node_id() const override
69 {
70 return this_node;
71 }
72 };
73
74 std::unique_ptr<NodeContext> context = nullptr;
75
76 std::shared_ptr<ccf::historical::StateCache> historical_state_cache =
77 nullptr;
78 std::shared_ptr<ccf::indexing::Indexer> indexer = nullptr;
79 std::shared_ptr<ccf::indexing::EnclaveLFSAccess> lfs_access = nullptr;
80 std::shared_ptr<ccf::HostProcesses> host_processes = nullptr;
81
82 public:
84 std::unique_ptr<ringbuffer::Circuit> circuit_,
85 std::unique_ptr<ringbuffer::WriterFactory> basic_writer_factory_,
86 std::unique_ptr<oversized::WriterFactory> writer_factory_,
87 RingbufferLogger* ringbuffer_logger_,
88 size_t sig_tx_interval,
89 size_t sig_ms_interval,
90 const ccf::consensus::Configuration& consensus_config,
91 const ccf::crypto::CurveID& curve_id) :
92 circuit(std::move(circuit_)),
93 basic_writer_factory(std::move(basic_writer_factory_)),
94 writer_factory(std::move(writer_factory_)),
95 ringbuffer_logger(ringbuffer_logger_),
96 network(),
97 rpc_map(std::make_shared<RPCMap>()),
98 rpcsessions(std::make_shared<RPCSessions>(*writer_factory, rpc_map))
99 {
100 ccf::pal::initialize_enclave();
103
104 // https://github.com/microsoft/CCF/issues/5569
105 // Open Enclave with OpenSSL 3.x (default for SGX) is built with RDCPU
106 // (https://github.com/openenclave/openenclave/blob/master/docs/OpenSSLSupport.md#how-to-use-rand-apis)
107 // and so does not need to make use of the (deprecated) ENGINE_x API.
108#if !(defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3)
109 // From
110 // https://software.intel.com/content/www/us/en/develop/articles/how-to-use-the-rdrand-engine-in-openssl-for-random-number-generation.html
111 if (
112 ENGINE_load_rdrand() != 1 ||
113 (rdrand_engine = ENGINE_by_id("rdrand")) == nullptr ||
114 ENGINE_init(rdrand_engine) != 1 ||
115 ENGINE_set_default(rdrand_engine, ENGINE_METHOD_RAND) != 1)
116 {
117 LOG_FAIL_FMT("Error creating OpenSSL's RDRAND engine");
118 ENGINE_free(rdrand_engine);
120 "could not initialize RDRAND engine for OpenSSL");
121 }
122#endif
123
124 to_host = writer_factory->create_writer_to_outside();
125
126 LOG_TRACE_FMT("Creating ledger secrets");
127 network.ledger_secrets = std::make_shared<ccf::LedgerSecrets>();
128
129 LOG_TRACE_FMT("Creating node");
130 node = std::make_unique<ccf::NodeState>(
131 *writer_factory, network, rpcsessions, curve_id);
132
133 LOG_TRACE_FMT("Creating context");
134 context = std::make_unique<NodeContext>(node->get_node_id());
135
136 LOG_TRACE_FMT("Creating context subsystems");
137 historical_state_cache = std::make_shared<ccf::historical::StateCache>(
138 *network.tables,
139 network.ledger_secrets,
140 writer_factory->create_writer_to_outside());
141 context->install_subsystem(historical_state_cache);
142
143 indexer = std::make_shared<ccf::indexing::Indexer>(
144 std::make_shared<ccf::indexing::HistoricalTransactionFetcher>(
145 historical_state_cache));
146 context->install_subsystem(indexer);
147
148 lfs_access = std::make_shared<ccf::indexing::EnclaveLFSAccess>(
149 writer_factory->create_writer_to_outside());
150 context->install_subsystem(lfs_access);
151
152 context->install_subsystem(std::make_shared<ccf::HostProcesses>(*node));
153 context->install_subsystem(std::make_shared<ccf::NodeOperation>(*node));
154 context->install_subsystem(
155 std::make_shared<ccf::GovernanceEffects>(*node));
156
157 context->install_subsystem(
158 std::make_shared<ccf::NetworkIdentitySubsystem>(
159 *node, network.identity));
160
161 context->install_subsystem(
162 std::make_shared<ccf::NodeConfigurationSubsystem>(*node));
163
164 context->install_subsystem(std::make_shared<ccf::ACMESubsystem>(*node));
165
166 auto cpss = std::make_shared<ccf::CustomProtocolSubsystem>(*node);
167 context->install_subsystem(cpss);
168 rpcsessions->set_custom_protocol_subsystem(cpss);
169
170 static constexpr size_t max_interpreter_cache_size = 10;
171 auto interpreter_cache =
172 std::make_shared<ccf::js::InterpreterCache>(max_interpreter_cache_size);
173 context->install_subsystem(interpreter_cache);
174
175 LOG_TRACE_FMT("Creating RPC actors / ffi");
176 rpc_map->register_frontend<ccf::ActorsType::members>(
177 std::make_unique<ccf::MemberRpcFrontend>(network, *context));
178
179 rpc_map->register_frontend<ccf::ActorsType::users>(
180 std::make_unique<ccf::UserRpcFrontend>(
181 network, ccf::make_user_endpoints(*context), *context));
182
183 rpc_map->register_frontend<ccf::ActorsType::nodes>(
184 std::make_unique<ccf::NodeRpcFrontend>(network, *context));
185
186 // Note: for ACME challenges, the well-known frontend should really only
187 // listen on the interface specified in the ACMEClientConfig, but we don't
188 // have support for frontends restricted to particular interfaces yet.
189 rpc_map->register_frontend<ccf::ActorsType::acme_challenge>(
190 std::make_unique<ccf::ACMERpcFrontend>(network, *context));
191
192 ccf::js::register_ffi_plugins(ccf::get_js_plugins());
193
194 LOG_TRACE_FMT("Initialize node");
195 node->initialize(
196 consensus_config,
197 rpc_map,
198 rpcsessions,
199 indexer,
200 sig_tx_interval,
201 sig_ms_interval);
202 }
203
205 {
206#if !(defined(OPENSSL_VERSION_MAJOR) && OPENSSL_VERSION_MAJOR >= 3)
207 if (rdrand_engine)
208 {
209 LOG_TRACE_FMT("Finishing RDRAND engine");
210 ENGINE_finish(rdrand_engine);
211 ENGINE_free(rdrand_engine);
212 }
213#endif
214 LOG_TRACE_FMT("Shutting down enclave");
216 ccf::pal::shutdown_enclave();
218 }
219
221 StartType start_type_,
222 StartupConfig&& ccf_config_,
223 std::vector<uint8_t>&& startup_snapshot,
224 uint8_t* node_cert,
225 size_t node_cert_size,
226 size_t* node_cert_len,
227 uint8_t* service_cert,
228 size_t service_cert_size,
229 size_t* service_cert_len)
230 {
231 // node_cert_size and service_cert_size are ignored here, but we pass them
232 // in because it allows us to set EDL an annotation so that node_cert_len
233 // <= node_cert_size is checked by the EDL-generated wrapper
234
235 start_type = start_type_;
236
237 rpcsessions->update_listening_interface_options(ccf_config_.network);
238
239 node->set_n2n_message_limit(ccf_config_.node_to_node_message_limit);
240
241 historical_state_cache->set_soft_cache_limit(
242 ccf_config_.historical_cache_soft_limit);
243
244 // If we haven't heard from a node for multiple elections, then cleanup
245 // their node-to-node channel
246 const auto idle_timeout =
247 std::chrono::milliseconds(ccf_config_.consensus.election_timeout) * 4;
248 node->set_n2n_idle_timeout(idle_timeout);
249
251 try
252 {
254 "Creating node with start_type {}", start_type_to_str(start_type));
255 r = node->create(
256 start_type, std::move(ccf_config_), std::move(startup_snapshot));
257 }
258 catch (const std::exception& e)
259 {
260 LOG_FAIL_FMT("Error starting node: {}", e.what());
262 }
263
264 // Copy node and service certs out
265 if (r.self_signed_node_cert.size() > node_cert_size)
266 {
268 "Insufficient space ({}) to copy node_cert out ({})",
269 node_cert_size,
272 }
273 pal::safe_memcpy(
274 node_cert,
277 *node_cert_len = r.self_signed_node_cert.size();
278
279 if (start_type == StartType::Start || start_type == StartType::Recover)
280 {
281 // When starting a node in start or recover modes, fresh network secrets
282 // are created and the associated certificate can be passed to the host
283 if (r.service_cert.size() > service_cert_size)
284 {
286 "Insufficient space ({}) to copy service_cert out ({})",
287 service_cert_size,
288 r.service_cert.size());
290 }
291 pal::safe_memcpy(
292 service_cert, r.service_cert.data(), r.service_cert.size());
293 *service_cert_len = r.service_cert.size();
294 }
295
297 }
298
299 bool run_main()
300 {
302 LOG_DEBUG_FMT("Running main thread");
303#ifndef VIRTUAL_ENCLAVE
304 try
305#endif
306 {
307 messaging::BufferProcessor bp("Enclave");
308
309 // reconstruct oversized messages sent to the enclave
311
312 lfs_access->register_message_handlers(bp.get_dispatcher());
313
315 bp, AdminMessage::stop, [&bp](const uint8_t*, size_t) {
316 bp.set_finished();
318 });
319
321 bp, AdminMessage::stop_notice, [this](const uint8_t*, size_t) {
322 node->stop_notice();
323 });
324
325 last_tick_time = ccf::get_enclave_time();
326
328 bp,
329 AdminMessage::tick,
330 [this, &disp = bp.get_dispatcher()](const uint8_t*, size_t) {
331 const auto message_counts = disp.retrieve_message_counts();
332 const auto j = disp.convert_message_counts(message_counts);
333 RINGBUFFER_WRITE_MESSAGE(
334 AdminMessage::work_stats, to_host, j.dump());
335
336 const auto time_now = ccf::get_enclave_time();
337 ringbuffer_logger->set_time(time_now);
338
339 const auto elapsed_ms =
340 std::chrono::duration_cast<std::chrono::milliseconds>(
341 time_now - last_tick_time);
342 if (elapsed_ms.count() > 0)
343 {
344 last_tick_time += elapsed_ms;
345
346 node->tick(elapsed_ms);
347 historical_state_cache->tick(elapsed_ms);
348 ::threading::ThreadMessaging::instance().tick(elapsed_ms);
349 // When recovering, no signature should be emitted while the
350 // public ledger is being read
351 if (!node->is_reading_public_ledger())
352 {
353 for (auto& [actor, frontend] : rpc_map->frontends())
354 {
355 frontend->tick(elapsed_ms);
356 }
357 }
358 node->tick_end();
359 }
360 });
361
363 bp, ccf::node_inbound, [this](const uint8_t* data, size_t size) {
364 try
365 {
366 node->recv_node_inbound(data, size);
367 }
368 catch (const std::exception& e)
369 {
371 "Ignoring node_inbound message due to exception: {}", e.what());
372 }
373 });
374
376 bp,
377 ::consensus::ledger_entry_range,
378 [this](const uint8_t* data, size_t size) {
379 const auto [from_seqno, to_seqno, purpose, body] =
380 ringbuffer::read_message<::consensus::ledger_entry_range>(
381 data, size);
382 switch (purpose)
383 {
384 case ::consensus::LedgerRequestPurpose::Recovery:
385 {
386 if (node->is_reading_public_ledger())
387 {
388 node->recover_public_ledger_entries(body);
389 }
390 else if (node->is_reading_private_ledger())
391 {
392 node->recover_private_ledger_entries(body);
393 }
394 else
395 {
396 auto [s, _, __] = node->state();
398 "Cannot recover ledger entry: Unexpected node state {}", s);
399 }
400 break;
401 }
402 case ::consensus::LedgerRequestPurpose::HistoricalQuery:
403 {
404 historical_state_cache->handle_ledger_entries(
405 from_seqno, to_seqno, body);
406 break;
407 }
408 default:
409 {
410 LOG_FAIL_FMT("Unhandled purpose: {}", purpose);
411 }
412 }
413 });
414
416 bp,
417 ::consensus::ledger_no_entry_range,
418 [this](const uint8_t* data, size_t size) {
419 const auto [from_seqno, to_seqno, purpose] =
420 ringbuffer::read_message<::consensus::ledger_no_entry_range>(
421 data, size);
422 switch (purpose)
423 {
424 case ::consensus::LedgerRequestPurpose::Recovery:
425 {
426 node->recover_ledger_end();
427 break;
428 }
429 case ::consensus::LedgerRequestPurpose::HistoricalQuery:
430 {
431 historical_state_cache->handle_no_entry_range(
432 from_seqno, to_seqno);
433 break;
434 }
435 default:
436 {
437 LOG_FAIL_FMT("Unhandled purpose: {}", purpose);
438 }
439 }
440 });
441
443 bp,
444 ::consensus::snapshot_allocated,
445 [this](const uint8_t* data, size_t size) {
446 const auto [snapshot_span, generation_count] =
447 ringbuffer::read_message<::consensus::snapshot_allocated>(
448 data, size);
449
450 node->write_snapshot(snapshot_span, generation_count);
451 });
452
453 rpcsessions->register_message_handlers(bp.get_dispatcher());
454
455 // Maximum number of inbound ringbuffer messages which will be
456 // processed in a single iteration
457 static constexpr size_t max_messages = 256;
458
459 size_t consecutive_idles = 0u;
460 while (!bp.get_finished())
461 {
462 // First, read some messages from the ringbuffer
463 auto read = bp.read_n(max_messages, circuit->read_from_outside());
464
465 // Then, execute some thread messages
466 size_t thread_msg = 0;
467 while (thread_msg < max_messages &&
469 {
470 thread_msg++;
471 }
472
473 // If no messages were read from the ringbuffer and no thread
474 // messages were executed, idle
475 if (read == 0 && thread_msg == 0)
476 {
477 const auto time_now = ccf::get_enclave_time();
478 static std::chrono::microseconds idling_start_time;
479
480 if (consecutive_idles == 0)
481 {
482 idling_start_time = time_now;
483 }
484
485 // Handle initial idles by pausing, eventually sleep (in host)
486 constexpr std::chrono::milliseconds timeout(5);
487 if ((time_now - idling_start_time) > timeout)
488 {
489 std::this_thread::sleep_for(timeout * 10);
490 }
491 else
492 {
493 CCF_PAUSE();
494 }
495
496 consecutive_idles++;
497 }
498 else
499 {
500 // If some messages were read, reset consecutive idles count
501 consecutive_idles = 0;
502 }
503 }
504
505 LOG_INFO_FMT("Enclave stopped successfully. Stopping host...");
506 RINGBUFFER_WRITE_MESSAGE(AdminMessage::stopped, to_host);
507
509
510 return true;
511 }
512#ifndef VIRTUAL_ENCLAVE
513 catch (const std::exception& e)
514 {
515 // It is expected that all enclave modules consuming ring buffer
516 // messages catch any thrown exception they can recover from. Uncaught
517 // exceptions bubble up to here and cause the node to shutdown.
519 AdminMessage::fatal_error_msg, to_host, std::string(e.what()));
521 return false;
522 }
523#endif
524 }
525
526 struct Msg
527 {
528 uint64_t tid;
529 };
530
531 static void init_thread_cb(std::unique_ptr<::threading::Tmsg<Msg>> msg)
532 {
533 LOG_DEBUG_FMT("First thread CB:{}", msg->data.tid);
534 }
535
537 {
539 LOG_DEBUG_FMT("Running worker thread");
540#ifndef VIRTUAL_ENCLAVE
541 try
542#endif
543 {
544 auto msg = std::make_unique<::threading::Tmsg<Msg>>(&init_thread_cb);
547 msg->data.tid, std::move(msg));
548
551 }
552#ifndef VIRTUAL_ENCLAVE
553 catch (const std::exception& e)
554 {
556 AdminMessage::fatal_error_msg, to_host, std::string(e.what()));
558 return false;
559 }
560#endif
561 return true;
562 }
563 };
564}
Definition enclave.h:44
static void init_thread_cb(std::unique_ptr<::threading::Tmsg< Msg > > msg)
Definition enclave.h:531
CreateNodeStatus create_new_node(StartType start_type_, StartupConfig &&ccf_config_, std::vector< uint8_t > &&startup_snapshot, uint8_t *node_cert, size_t node_cert_size, size_t *node_cert_len, uint8_t *service_cert, size_t service_cert_size, size_t *service_cert_len)
Definition enclave.h:220
Enclave(std::unique_ptr< ringbuffer::Circuit > circuit_, std::unique_ptr< ringbuffer::WriterFactory > basic_writer_factory_, std::unique_ptr< oversized::WriterFactory > writer_factory_, RingbufferLogger *ringbuffer_logger_, size_t sig_tx_interval, size_t sig_ms_interval, const ccf::consensus::Configuration &consensus_config, const ccf::crypto::CurveID &curve_id)
Definition enclave.h:83
~Enclave()
Definition enclave.h:204
bool run_main()
Definition enclave.h:299
bool run_worker()
Definition enclave.h:536
Definition rpc_map.h:11
Definition rpc_sessions.h:46
Definition ringbuffer_logger.h:10
Definition ccf_exception.h:52
size_t size() const
Definition pem.h:61
uint8_t * data()
Definition pem.h:51
Definition messaging.h:211
RingbufferDispatcher & get_dispatcher()
Definition messaging.h:219
void set_finished(bool v=true)
Definition messaging.h:230
Definition oversized.h:29
void run()
Definition thread_messaging.h:295
static ThreadMessaging & instance()
Definition thread_messaging.h:278
bool run_one()
Definition thread_messaging.h:305
void add_task(uint16_t tid, std::unique_ptr< Tmsg< Payload > > msg)
Definition thread_messaging.h:312
void set_finished(bool v=true)
Definition thread_messaging.h:290
CreateNodeStatus
Definition enclave_interface_types.h:8
@ OK
Definition enclave_interface_types.h:10
@ InternalError
Definition enclave_interface_types.h:13
StartType
Definition enclave_interface_types.h:113
@ Recover
Definition enclave_interface_types.h:116
@ Start
Definition enclave_interface_types.h:114
constexpr char const * start_type_to_str(StartType type)
Definition enclave_interface_types.h:119
#define LOG_INFO_FMT
Definition logger.h:395
#define LOG_TRACE_FMT
Definition logger.h:378
#define LOG_DEBUG_FMT
Definition logger.h:380
#define LOG_FAIL_FMT
Definition logger.h:396
#define DISPATCHER_SET_MESSAGE_HANDLER(DISP, MSG,...)
Definition messaging.h:316
void openssl_sha256_init()
Definition hash.cpp:43
CurveID
Definition curve.h:18
void openssl_sha256_shutdown()
Definition hash.cpp:69
uint16_t get_current_thread_id()
Definition thread_local.cpp:9
Definition app_interface.h:15
void initialize_verifiers()
Definition verify.h:15
std::vector< ccf::js::FFIPlugin > get_js_plugins()
std::unique_ptr< ccf::endpoints::EndpointRegistry > make_user_endpoints(ccf::AbstractNodeContext &context)
Definition js_generic.cpp:8
void shutdown_verifiers()
Definition verify.h:27
std::shared_ptr< AbstractWriter > WriterPtr
Definition ring_buffer_types.h:150
STL namespace.
#define CCF_PAUSE()
Definition ring_buffer.h:16
#define RINGBUFFER_WRITE_MESSAGE(MSG,...)
Definition ring_buffer_types.h:255
Definition startup_config.h:79
Definition node_context.h:12
Definition enclave.h:527
uint64_t tid
Definition enclave.h:528
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
Definition node_state.h:62
ccf::crypto::Pem service_cert
Definition node_state.h:64
ccf::crypto::Pem self_signed_node_cert
Definition node_state.h:63
Definition consensus_config.h:11
Definition thread_messaging.h:27