in src/node/rpc/node_frontend.h [181:312]
auto add_node(
kv::Tx& tx,
const std::vector<uint8_t>& node_der,
const JoinNetworkNodeToNode::In& in,
NodeStatus node_status,
ServiceStatus service_status,
ReconfigurationType reconfiguration_type)
{
auto nodes = tx.rw(network.nodes);
auto node_endorsed_certificates =
tx.rw(network.node_endorsed_certificates);
auto config = tx.ro(network.config)->get();
auto conflicting_node_id =
check_conflicting_node_network(tx, in.node_info_network);
if (conflicting_node_id.has_value())
{
return make_error(
HTTP_STATUS_BAD_REQUEST,
ccf::errors::NodeAlreadyExists,
fmt::format(
"A node with the same node address {} already exists "
"(node id: {}).",
in.node_info_network.node_to_node_interface.bind_address,
conflicting_node_id.value()));
}
auto pubk_der = crypto::public_key_der_from_cert(node_der);
NodeId joining_node_id = compute_node_id_from_pubk_der(pubk_der);
CodeDigest code_digest;
#ifdef GET_QUOTE
QuoteVerificationResult verify_result =
this->context.get_node_state().verify_quote(
tx, in.quote_info, pubk_der, code_digest);
if (verify_result != QuoteVerificationResult::Verified)
{
const auto [code, message] = quote_verification_error(verify_result);
return make_error(code, ccf::errors::InvalidQuote, message);
}
#else
LOG_INFO_FMT("Skipped joining node quote verification");
#endif
std::optional<kv::Version> ledger_secret_seqno = std::nullopt;
if (
node_status == NodeStatus::TRUSTED ||
node_status == NodeStatus::LEARNER)
{
ledger_secret_seqno =
this->network.ledger_secrets->get_latest(tx).first;
}
// Note: All new nodes should specify a CSR from 2.x
auto client_public_key_pem = crypto::public_key_pem_from_cert(node_der);
if (in.certificate_signing_request.has_value())
{
// Verify that client's public key matches the one specified in the CSR
auto csr_public_key_pem = crypto::public_key_pem_from_csr(
in.certificate_signing_request.value());
if (client_public_key_pem != csr_public_key_pem)
{
return make_error(
HTTP_STATUS_BAD_REQUEST,
ccf::errors::CSRPublicKeyInvalid,
"Public key in CSR does not match TLS client identity.");
}
}
NodeInfo node_info = {
in.node_info_network,
in.quote_info,
in.public_encryption_key,
node_status,
ledger_secret_seqno,
ds::to_hex(code_digest.data),
in.certificate_signing_request,
client_public_key_pem};
// Because the certificate signature scheme is non-deterministic, only
// self-signed node certificate is recorded in the node info table
if (this->network.consensus_type == ConsensusType::BFT)
{
node_info.cert = crypto::cert_der_to_pem(node_der);
}
nodes->put(joining_node_id, node_info);
LOG_INFO_FMT("Node {} added as {}", joining_node_id, node_status);
JoinNetworkNodeToNode::Out rep;
rep.node_status = node_status;
rep.node_id = joining_node_id;
if (
node_status == NodeStatus::TRUSTED ||
node_status == NodeStatus::LEARNER)
{
// Joining node only submit a CSR from 2.x
std::optional<crypto::Pem> endorsed_certificate = std::nullopt;
if (
in.certificate_signing_request.has_value() &&
this->network.consensus_type == ConsensusType::CFT)
{
// For a pre-open service, extract the validity period of self-signed
// node certificate and use it verbatim in endorsed certificate
auto [valid_from, valid_to] =
crypto::make_verifier(node_der)->validity_period();
endorsed_certificate = crypto::create_endorsed_cert(
in.certificate_signing_request.value(),
valid_from,
valid_to,
this->network.identity->priv_key,
this->network.identity->cert);
node_endorsed_certificates->put(
joining_node_id, {endorsed_certificate.value()});
}
rep.network_info = JoinNetworkNodeToNode::Out::NetworkInfo{
context.get_node_state().is_part_of_public_network(),
context.get_node_state().get_last_recovered_signed_idx(),
this->network.consensus_type,
reconfiguration_type,
this->network.ledger_secrets->get(tx),
*this->network.identity.get(),
service_status,
endorsed_certificate};
}
return make_success(rep);
}