CreateNodeStatus enclave_create_node()

in src/enclave/main.cpp [31:238]


  CreateNodeStatus enclave_create_node(
    void* enclave_config,
    char* ccf_config,
    size_t ccf_config_size,
    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,
    uint8_t* enclave_version,
    size_t enclave_version_size,
    size_t* enclave_version_len,
    StartType start_type,
    size_t num_worker_threads,
    void* time_location)
  {
    std::lock_guard<std::mutex> guard(create_lock);

    if (e != nullptr)
    {
      return CreateNodeStatus::NodeAlreadyCreated;
    }

    EnclaveConfig ec = *static_cast<EnclaveConfig*>(enclave_config);

    // Setup logger to allow enclave logs to reach the host before node is
    // actually created
    auto circuit = std::make_unique<ringbuffer::Circuit>(
      ringbuffer::BufferDef{
        ec.to_enclave_buffer_start,
        ec.to_enclave_buffer_size,
        ec.to_enclave_buffer_offsets},
      ringbuffer::BufferDef{
        ec.from_enclave_buffer_start,
        ec.from_enclave_buffer_size,
        ec.from_enclave_buffer_offsets});
    auto basic_writer_factory =
      std::make_unique<ringbuffer::WriterFactory>(*circuit);
    auto writer_factory = std::make_unique<oversized::WriterFactory>(
      *basic_writer_factory, ec.writer_config);

    auto new_logger = std::make_unique<enclave::RingbufferLogger>(
      writer_factory->create_writer_to_outside());
    auto ringbuffer_logger = new_logger.get();
    logger::config::loggers().push_back(std::move(new_logger));

    {
      // Report enclave version to host
      auto ccf_version_string = std::string(ccf::ccf_version);
      if (ccf_version_string.size() > enclave_version_size)
      {
        LOG_FAIL_FMT(
          "Version mismatch: host {}, enclave {}",
          ccf_version_string,
          enclave_version);
        return CreateNodeStatus::VersionMismatch;
      }

      ::memcpy(
        enclave_version, ccf_version_string.data(), ccf_version_string.size());
      *enclave_version_len = ccf_version_string.size();

      num_pending_threads = (uint16_t)num_worker_threads + 1;

      if (
        num_pending_threads >
        threading::ThreadMessaging::thread_messaging.max_num_threads)
      {
        LOG_FAIL_FMT("Too many threads: {}", num_pending_threads);
        return CreateNodeStatus::TooManyThreads;
      }

      // Check that where we expect arguments to be in host-memory, they really
      // are. lfence after these checks to prevent speculative execution
      if (!oe_is_outside_enclave(time_location, sizeof(enclave::host_time)))
      {
        LOG_FAIL_FMT("Memory outside enclave: time_location");
        return CreateNodeStatus::MemoryNotOutsideEnclave;
      }

      enclave::host_time =
        static_cast<decltype(enclave::host_time)>(time_location);

      if (!oe_is_outside_enclave(enclave_config, sizeof(EnclaveConfig)))
      {
        LOG_FAIL_FMT("Memory outside enclave: enclave_config");
        return CreateNodeStatus::MemoryNotOutsideEnclave;
      }

      // Check that ringbuffer memory ranges are entirely outside of the enclave
      if (!oe_is_outside_enclave(
            ec.to_enclave_buffer_start, ec.to_enclave_buffer_size))
      {
        LOG_FAIL_FMT("Memory outside enclave: to_enclave buffer start");
        return CreateNodeStatus::MemoryNotOutsideEnclave;
      }

      if (!oe_is_outside_enclave(
            ec.from_enclave_buffer_start, ec.from_enclave_buffer_size))
      {
        LOG_FAIL_FMT("Memory outside enclave: from_enclave buffer start");
        return CreateNodeStatus::MemoryNotOutsideEnclave;
      }

      if (!oe_is_outside_enclave(
            ec.to_enclave_buffer_offsets, sizeof(ringbuffer::Offsets)))
      {
        LOG_FAIL_FMT("Memory outside enclave: to_enclave buffer offset");
        return CreateNodeStatus::MemoryNotOutsideEnclave;
      }

      if (!oe_is_outside_enclave(
            ec.from_enclave_buffer_offsets, sizeof(ringbuffer::Offsets)))
      {
        LOG_FAIL_FMT("Memory outside enclave: from_enclave buffer offset");
        return CreateNodeStatus::MemoryNotOutsideEnclave;
      }

      oe_lfence();
    }

    if (!oe_is_outside_enclave(ccf_config, ccf_config_size))
    {
      LOG_FAIL_FMT("Memory outside enclave: ccf_config");
      return CreateNodeStatus::MemoryNotOutsideEnclave;
    }

    oe_lfence();

    StartupConfig cc =
      nlohmann::json::parse(ccf_config, ccf_config + ccf_config_size);

#ifndef ENABLE_BFT
    // As BFT consensus is currently experimental, disable it in release
    // enclaves
    if (cc.consensus.type != ConsensusType::CFT)
    {
      LOG_FAIL_FMT("BFT consensus disabled in release mode");
      return CreateNodeStatus::ConsensusNotAllowed;
    }
#endif

#ifndef ENABLE_2TX_RECONFIG
    // 2-tx reconfiguration is currently experimental, disable it in release
    // enclaves
    if (
      cc.start.service_configuration.reconfiguration_type.has_value() &&
      cc.start.service_configuration.reconfiguration_type.value() !=
        ReconfigurationType::ONE_TRANSACTION)
    {
      LOG_FAIL_FMT(
        "2TX reconfiguration is experimental, disabled in release mode");
      return CreateNodeStatus::ReconfigurationMethodNotSupported;
    }
#endif

    enclave::Enclave* enclave;

    try
    {
      enclave = new enclave::Enclave(
        ec,
        std::move(circuit),
        std::move(basic_writer_factory),
        std::move(writer_factory),
        ringbuffer_logger,
        cc.ledger_signatures.tx_count,
        cc.ledger_signatures.delay.count_ms(),
        cc.consensus,
        cc.node_certificate.curve_id);
    }
    catch (const ccf::ccf_oe_attester_init_error&)
    {
      return CreateNodeStatus::OEAttesterInitFailed;
    }
    catch (const ccf::ccf_oe_verifier_init_error&)
    {
      return CreateNodeStatus::OEVerifierInitFailed;
    }
    catch (const ccf::ccf_openssl_rdrand_init_error&)
    {
      return CreateNodeStatus::OpenSSLRDRANDInitFailed;
    }
    catch (const std::exception&)
    {
      return CreateNodeStatus::EnclaveInitFailed;
    }

    auto status = enclave->create_new_node(
      start_type,
      std::move(cc),
      node_cert,
      node_cert_size,
      node_cert_len,
      service_cert,
      service_cert_size,
      service_cert_len);

    if (status != CreateNodeStatus::OK)
    {
      return status;
    }

    e.store(enclave);

    return CreateNodeStatus::OK;
  }