Namespaces
Functions
Functions Documentation
function OUTCOME_CPP_DEFINE_CATEGORY_3
OUTCOME_CPP_DEFINE_CATEGORY_3(
sgns::trustedpeer ,
GenesisCeremony::Error ,
e
)
Source code
#include "trustedpeer/genesis_tool/GenesisCeremony.hpp"
#include <algorithm>
#include <cctype>
#include <chrono>
#include <iostream>
#include <stdexcept>
#include <thread>
#include <utility>
#include <openssl/crypto.h>
#include "account/GeniusSigner.hpp"
#include "base/hexutil.hpp"
#include "trustedpeer/genesis_tool/GenesisCeremonyPlatform.hpp"
OUTCOME_CPP_DEFINE_CATEGORY_3( sgns::trustedpeer, GenesisCeremony::Error, e )
{
using Error = sgns::trustedpeer::GenesisCeremony::Error;
switch ( e )
{
case Error::SUCCESS: return "genesis ceremony completed";
case Error::INVALID_MANIFEST: return "genesis manifest is not canonical and valid";
case Error::INVALID_KEY_SOURCE: return "select exactly one protected key source";
case Error::KEY_FILE_IO: return "unable to read the bootstrap key file";
case Error::KEY_FILE_NOT_REGULAR: return "bootstrap key path is not a regular file";
case Error::KEY_FILE_SYMLINK: return "bootstrap key path must not be a symlink";
case Error::KEY_FILE_OWNER: return "bootstrap key file is not owned by this process user";
case Error::KEY_FILE_MODE: return "bootstrap key file mode must be 0600";
case Error::INVALID_PRIVATE_KEY: return "bootstrap private key is invalid";
case Error::BOOTSTRAPPER_MISMATCH: return "derived bootstrapper does not match the manifest";
case Error::CONFIRMATION_MISMATCH: return "typed fingerprint does not match the reviewed manifest";
case Error::NETWORK_START_FAILED: return "unable to start the production CRDT composition";
case Error::SUBMISSION_FAILED: return "unable to submit the reviewed genesis approval";
case Error::CONFIRMATION_FAILED: return "durable genesis confirmation failed";
case Error::CONFIRMATION_TIMEOUT: return "durable genesis confirmation timed out";
case Error::KEY_FILE_UNLINK_FAILED: return "confirmed genesis key file could not be removed";
}
return "unknown GenesisCeremony::Error";
}
namespace sgns::trustedpeer
{
namespace
{
constexpr size_t PRIVATE_KEY_HEX_LENGTH = 64;
bool IsPrivateKeyHex( const std::string &value )
{
return value.size() == PRIVATE_KEY_HEX_LENGTH &&
std::all_of( value.begin(), value.end(), []( unsigned char c ) { return std::isxdigit( c ) != 0; } );
}
void WriteCriticalRetention( const GenesisCeremony::Request &request, std::ostream &errors )
{
if ( request.key_file )
errors << "CRITICAL: genesis is not durably confirmed; retain protected key file at "
<< *request.key_file << " and investigate before retrying.\n";
else
errors << "CRITICAL: genesis is not durably confirmed; securely retain the bootstrap key and investigate.\n";
}
} // namespace
GenesisCeremony::Hooks GenesisCeremony::DefaultHooks()
{
Hooks hooks;
hooks.inspect_key_file = genesis_ceremony_platform::InspectKeyFile;
hooks.read_key_file = genesis_ceremony_platform::ReadKeyFile;
hooks.create_signer = []( std::string_view private_key ) -> outcome::result<Signer>
{
auto local_key = GeniusSigner::FromPrivateKeyHex( private_key );
if ( !local_key )
return outcome::failure( Error::INVALID_PRIVATE_KEY );
std::shared_ptr<GeniusSigner> signer = std::make_shared<GeniusSigner>( std::move( *local_key ) );
return Signer{ signer->GetAddress(),
[signer]( const std::vector<uint8_t> &bytes ) { return signer->Sign( bytes ); } };
};
hooks.cleanse = []( void *data, size_t size ) { OPENSSL_cleanse( data, size ); };
hooks.unlink_file = genesis_ceremony_platform::RemoveKeyFile;
hooks.sleep = []( std::chrono::milliseconds duration ) { std::this_thread::sleep_for( duration ); };
return hooks;
}
GenesisCeremony::GenesisCeremony() : hooks_( DefaultHooks() )
{
}
GenesisCeremony::GenesisCeremony( Hooks hooks ) : hooks_( std::move( hooks ) )
{
}
std::optional<GenesisCeremony::Error> GenesisCeremony::KeyFileStatusProblem(
const outcome::result<KeyFileStatus> &status )
{
if ( status.has_error() || !status.value().exists )
return Error::KEY_FILE_IO;
if ( status.value().symlink )
return Error::KEY_FILE_SYMLINK;
if ( !status.value().regular )
return Error::KEY_FILE_NOT_REGULAR;
if ( !status.value().owner )
return Error::KEY_FILE_OWNER;
if ( status.value().mode != 0600 )
return Error::KEY_FILE_MODE;
return std::nullopt;
}
outcome::result<void> GenesisCeremony::Run( const Request &request,
Network &network,
std::istream &input,
std::ostream &output,
std::ostream &errors ) const
{
const auto canonical = request.manifest.Canonicalized();
const auto manifest_bytes = request.manifest.CanonicalBytes();
const auto fingerprint = request.manifest.Fingerprint();
if ( !canonical || !manifest_bytes || !fingerprint || !( *canonical == request.manifest ) )
return outcome::failure( Error::INVALID_MANIFEST );
if ( request.key_file.has_value() == request.key_stdin )
return outcome::failure( Error::INVALID_KEY_SOURCE );
if ( !network.start || !network.submit || !network.confirmed )
return outcome::failure( Error::NETWORK_START_FAILED );
const auto fail = [&request, &errors]( Error error )
{
WriteCriticalRetention( request, errors );
return outcome::result<void>( outcome::failure( error ) );
};
output << "Trusted-peer genesis review\n"
<< "network: " << canonical->network_id << '\n'
<< "bootstrapper: " << canonical->bootstrapper_public_key << '\n'
<< "policy version: " << canonical->policy_version << '\n'
<< "membership threshold: " << canonical->membership_threshold << '\n'
<< "burn threshold: " << canonical->burn_threshold << '\n'
<< "initial burn basis points: " << canonical->initial_burn_basis_points << '\n'
<< "ordered peers:\n";
for ( const auto &peer : canonical->peers ) output << " " << peer << '\n';
output << "fingerprint: " << *fingerprint << '\n';
std::string private_key;
if ( request.key_file )
{
auto status = hooks_.inspect_key_file( *request.key_file );
if ( auto problem = KeyFileStatusProblem( status ) )
{
return fail( *problem );
}
auto read = hooks_.read_key_file( *request.key_file );
if ( read.has_error() )
{
WriteCriticalRetention( request, errors );
return read.error();
}
private_key = std::move( read.value() );
}
else
{
output << "bootstrap key (protected stdin): " << std::flush;
const auto read = genesis_ceremony_platform::ReadProtectedLine( input, output, private_key );
if ( read == genesis_ceremony_platform::ProtectedInputResult::NOT_A_TERMINAL )
return outcome::failure( Error::INVALID_KEY_SOURCE );
if ( read != genesis_ceremony_platform::ProtectedInputResult::SUCCESS )
return outcome::failure( Error::KEY_FILE_IO );
genesis_ceremony_platform::TrimLineEnding( private_key );
}
bool cleansed = false;
const auto cleanse = [&]
{
if ( !cleansed && !private_key.empty() )
{
hooks_.cleanse( private_key.data(), private_key.size() );
cleansed = true;
}
};
if ( !IsPrivateKeyHex( private_key ) )
{
cleanse();
return fail( Error::INVALID_PRIVATE_KEY );
}
outcome::result<Signer> local_signer = hooks_.create_signer( private_key );
cleanse();
if ( local_signer.has_error() )
{
WriteCriticalRetention( request, errors );
return local_signer.error();
}
if ( local_signer.value().address != canonical->bootstrapper_public_key )
{
return fail( Error::BOOTSTRAPPER_MISMATCH );
}
output << "Type the exact fingerprint to submit: " << std::flush;
std::string typed_fingerprint;
if ( !std::getline( input, typed_fingerprint ) || typed_fingerprint != *fingerprint )
{
return fail( Error::CONFIRMATION_MISMATCH );
}
auto started = network.start();
if ( started.has_error() )
{
return fail( Error::NETWORK_START_FAILED );
}
const std::vector<uint8_t> manifest_signature = local_signer.value().sign( *manifest_bytes );
auto submitted = network.submit( *canonical,
manifest_signature,
local_signer.value().address,
local_signer.value().sign );
if ( submitted.has_error() )
{
return fail( Error::SUBMISSION_FAILED );
}
const auto deadline = std::chrono::steady_clock::now() + request.confirmation_timeout;
do
{
auto confirmed = network.confirmed();
if ( confirmed.has_error() )
{
return fail( Error::CONFIRMATION_FAILED );
}
if ( confirmed.value() && confirmed.value()->genesis_fingerprint == *fingerprint &&
confirmed.value()->genesis == *canonical )
{
if ( request.key_file && hooks_.unlink_file( *request.key_file ) != 0 )
{
errors << "CRITICAL: genesis is confirmed but the protected key file could not be removed; "
"remove it securely before continuing.\n";
return outcome::failure( Error::KEY_FILE_UNLINK_FAILED );
}
output << "Genesis durably confirmed.\n";
// Durable confirmation is a LOCAL statement: this tool's store
// committed. It says nothing about remote propagation - head
// broadcast and the peers' GraphSync fetches are still in flight,
// and this process is the only serving transport for the freshly
// written DAG. Keep it up for the requested window so peers that
// have not fetched yet are not stranded waiting for a genesis
// nobody serves anymore.
if ( network.serve && request.serve_duration > std::chrono::milliseconds::zero() )
{
output << "Serving genesis to peers for "
<< std::chrono::duration_cast<std::chrono::seconds>( request.serve_duration ).count()
<< "s before exit.\n";
network.serve( request.serve_duration );
output << "Genesis serving window complete.\n";
}
return outcome::success();
}
if ( request.confirmation_timeout.count() > 0 ) hooks_.sleep( request.poll_interval );
} while ( std::chrono::steady_clock::now() < deadline );
return fail( Error::CONFIRMATION_TIMEOUT );
}
} // namespace sgns::trustedpeer
Updated on 2026-09-25 at 15:46:12 +0000