# Conflicts: # nano/lib/stats_enums.hpp # nano/node/bootstrap/account_sets.hpp # nano/node/node.hpp
1042 lines
No EOL
40 KiB
C++
1042 lines
No EOL
40 KiB
C++
#include <nano/lib/block_type.hpp>
|
|
#include <nano/lib/blocks.hpp>
|
|
#include <nano/lib/files.hpp>
|
|
#include <nano/lib/stream.hpp>
|
|
#include <nano/lib/thread_pool.hpp>
|
|
#include <nano/lib/thread_runner.hpp>
|
|
#include <nano/lib/tomlconfig.hpp>
|
|
#include <nano/lib/utility.hpp>
|
|
#include <nano/lib/work_version.hpp>
|
|
#include <nano/node/active_elections.hpp>
|
|
#include <nano/node/backlog_scan.hpp>
|
|
#include <nano/node/bandwidth_limiter.hpp>
|
|
#include <nano/node/bootstrap/bootstrap_server.hpp>
|
|
#include <nano/node/bootstrap/bootstrap_service.hpp>
|
|
#include <nano/node/bootstrap_weights_beta.hpp>
|
|
#include <nano/node/bootstrap_weights_live.hpp>
|
|
#include <nano/node/bounded_backlog.hpp>
|
|
#include <nano/node/bucketing.hpp>
|
|
#include <nano/node/confirming_set.hpp>
|
|
#include <nano/node/daemonconfig.hpp>
|
|
#include <nano/node/election_status.hpp>
|
|
#include <nano/node/endpoint.hpp>
|
|
#include <nano/node/ledger_notifications.hpp>
|
|
#include <nano/node/local_block_broadcaster.hpp>
|
|
#include <nano/node/local_vote_history.hpp>
|
|
#include <nano/node/make_store.hpp>
|
|
#include <nano/node/message_processor.hpp>
|
|
#include <nano/node/monitor.hpp>
|
|
#include <nano/node/node.hpp>
|
|
#include <nano/node/online_reps.hpp>
|
|
#include <nano/node/peer_history.hpp>
|
|
#include <nano/node/portmapping.hpp>
|
|
#include <nano/node/pruning.hpp>
|
|
#include <nano/node/request_aggregator.hpp>
|
|
#include <nano/node/rpc_callbacks.hpp>
|
|
#include <nano/node/scheduler/component.hpp>
|
|
#include <nano/node/scheduler/hinted.hpp>
|
|
#include <nano/node/scheduler/manual.hpp>
|
|
#include <nano/node/scheduler/optimistic.hpp>
|
|
#include <nano/node/scheduler/priority.hpp>
|
|
#include <nano/node/telemetry.hpp>
|
|
#include <nano/node/transport/loopback.hpp>
|
|
#include <nano/node/transport/tcp_listener.hpp>
|
|
#include <nano/node/vote_generator.hpp>
|
|
#include <nano/node/vote_processor.hpp>
|
|
#include <nano/node/vote_rebroadcaster.hpp>
|
|
#include <nano/node/vote_router.hpp>
|
|
#include <nano/node/websocket.hpp>
|
|
#include <nano/secure/ledger.hpp>
|
|
#include <nano/secure/ledger_set_any.hpp>
|
|
#include <nano/secure/ledger_set_confirmed.hpp>
|
|
#include <nano/secure/vote.hpp>
|
|
#include <nano/store/component.hpp>
|
|
#include <nano/store/rocksdb/rocksdb.hpp>
|
|
|
|
#include <boost/format.hpp>
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
#include <fstream>
|
|
#include <future>
|
|
#include <sstream>
|
|
|
|
double constexpr nano::node::price_max;
|
|
double constexpr nano::node::free_cutoff;
|
|
|
|
namespace nano::weights
|
|
{
|
|
extern std::vector<std::pair<std::string, std::string>> preconfigured_weights_live;
|
|
extern uint64_t max_blocks_live;
|
|
extern std::vector<std::pair<std::string, std::string>> preconfigured_weights_beta;
|
|
extern uint64_t max_blocks_beta;
|
|
}
|
|
|
|
/*
|
|
* node
|
|
*/
|
|
|
|
nano::node::node (std::shared_ptr<boost::asio::io_context> io_ctx_a, uint16_t peering_port_a, std::filesystem::path const & application_path_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) :
|
|
node (io_ctx_a, application_path_a, nano::node_config (peering_port_a), work_a, flags_a, seq)
|
|
{
|
|
}
|
|
|
|
nano::node::node (std::shared_ptr<boost::asio::io_context> io_ctx_a, std::filesystem::path const & application_path_a, nano::node_config const & config_a, nano::work_pool & work_a, nano::node_flags flags_a, unsigned seq) :
|
|
application_path{ application_path_a },
|
|
node_id{ load_or_create_node_id (application_path_a) },
|
|
node_initialized_latch{ 1 },
|
|
config{ config_a },
|
|
flags{ flags_a },
|
|
network_params{ config.network_params },
|
|
io_ctx_shared{ std::make_shared<boost::asio::io_context> () },
|
|
io_ctx{ *io_ctx_shared },
|
|
logger_impl{ std::make_unique<nano::logger> (make_logger_identifier (node_id)) },
|
|
logger{ *logger_impl },
|
|
stats_impl{ std::make_unique<nano::stats> (logger, config.stats_config) },
|
|
stats{ *stats_impl },
|
|
runner_impl{ std::make_unique<nano::thread_runner> (io_ctx_shared, logger, config.io_threads) },
|
|
runner{ *runner_impl },
|
|
observers_impl{ std::make_unique<nano::node_observers> () },
|
|
observers{ *observers_impl },
|
|
workers_impl{ std::make_unique<nano::thread_pool> (config.background_threads, nano::thread_role::name::worker, /* start immediately */ true) },
|
|
workers{ *workers_impl },
|
|
bootstrap_workers_impl{ std::make_unique<nano::thread_pool> (config.bootstrap_serving_threads, nano::thread_role::name::bootstrap_worker, /* start immediately */ true) },
|
|
bootstrap_workers{ *bootstrap_workers_impl },
|
|
wallet_workers_impl{ std::make_unique<nano::thread_pool> (1, nano::thread_role::name::wallet_worker, /* start immediately */ true) },
|
|
wallet_workers{ *wallet_workers_impl },
|
|
election_workers_impl{ std::make_unique<nano::thread_pool> (1, nano::thread_role::name::election_worker, /* start immediately */ true) },
|
|
election_workers{ *election_workers_impl },
|
|
work{ work_a },
|
|
distributed_work_impl{ std::make_unique<nano::distributed_work_factory> (*this) },
|
|
distributed_work{ *distributed_work_impl },
|
|
store_impl{ nano::make_store (logger, application_path_a, network_params.ledger, flags.read_only, true, config_a) },
|
|
store{ *store_impl },
|
|
unchecked_impl{ std::make_unique<nano::unchecked_map> (config.max_unchecked_blocks, stats, flags.disable_block_processor_unchecked_deletion) },
|
|
unchecked{ *unchecked_impl },
|
|
wallets_store_impl{ std::make_unique<nano::mdb_wallets_store> (application_path_a / "wallets.ldb", config_a.lmdb_config) },
|
|
wallets_store{ *wallets_store_impl },
|
|
wallets_impl{ std::make_unique<nano::wallets> (wallets_store.init_error (), *this) },
|
|
wallets{ *wallets_impl },
|
|
ledger_impl{ std::make_unique<nano::ledger> (store, network_params.ledger, stats, logger, flags_a.generate_cache, config_a.representative_vote_weight_minimum.number ()) },
|
|
ledger{ *ledger_impl },
|
|
ledger_notifications_impl{ std::make_unique<nano::ledger_notifications> (config, stats, logger) },
|
|
ledger_notifications{ *ledger_notifications_impl },
|
|
outbound_limiter_impl{ std::make_unique<nano::bandwidth_limiter> (config) },
|
|
outbound_limiter{ *outbound_limiter_impl },
|
|
message_processor_impl{ std::make_unique<nano::message_processor> (config.message_processor, *this) },
|
|
message_processor{ *message_processor_impl },
|
|
// empty `config.peering_port` means the user made no port choice at all;
|
|
// otherwise, any value is considered, with `0` having the special meaning of 'let the OS pick a port instead'
|
|
//
|
|
network_impl{ std::make_unique<nano::network> (*this, config.peering_port.has_value () ? *config.peering_port : 0) },
|
|
network{ *network_impl },
|
|
loopback_channel{ std::make_shared<nano::transport::loopback_channel> (*this) },
|
|
telemetry_impl{ std::make_unique<nano::telemetry> (flags, *this, network, observers, network_params, stats) },
|
|
telemetry{ *telemetry_impl },
|
|
// BEWARE: `bootstrap` takes `network.port` instead of `config.peering_port` because when the user doesn't specify
|
|
// a peering port and wants the OS to pick one, the picking happens when `network` gets initialized
|
|
// (if UDP is active, otherwise it happens when `bootstrap` gets initialized), so then for TCP traffic
|
|
// we want to tell `bootstrap` to use the already picked port instead of itself picking a different one.
|
|
// Thus, be very careful if you change the order: if `bootstrap` gets constructed before `network`,
|
|
// the latter would inherit the port from the former (if TCP is active, otherwise `network` picks first)
|
|
//
|
|
tcp_listener_impl{ std::make_unique<nano::transport::tcp_listener> (network.port, config.tcp, *this) },
|
|
tcp_listener{ *tcp_listener_impl },
|
|
port_mapping_impl{ std::make_unique<nano::port_mapping> (*this) },
|
|
port_mapping{ *port_mapping_impl },
|
|
block_processor_impl{ std::make_unique<nano::block_processor> (config, ledger, ledger_notifications, unchecked, stats, logger) },
|
|
block_processor{ *block_processor_impl },
|
|
confirming_set_impl{ std::make_unique<nano::confirming_set> (config.confirming_set, ledger, ledger_notifications, stats, logger) },
|
|
confirming_set{ *confirming_set_impl },
|
|
bucketing_impl{ std::make_unique<nano::bucketing> () },
|
|
bucketing{ *bucketing_impl },
|
|
active_impl{ std::make_unique<nano::active_elections> (*this, ledger_notifications, confirming_set) },
|
|
active{ *active_impl },
|
|
online_reps_impl{ std::make_unique<nano::online_reps> (config, ledger, stats, logger) },
|
|
online_reps{ *online_reps_impl },
|
|
rep_crawler_impl{ std::make_unique<nano::rep_crawler> (config.rep_crawler, *this) },
|
|
rep_crawler{ *rep_crawler_impl },
|
|
rep_tiers_impl{ std::make_unique<nano::rep_tiers> (ledger, network_params, online_reps, stats, logger) },
|
|
rep_tiers{ *rep_tiers_impl },
|
|
history_impl{ std::make_unique<nano::local_vote_history> (config.network_params.voting) },
|
|
history{ *history_impl },
|
|
block_uniquer_impl{ std::make_unique<nano::block_uniquer> () },
|
|
block_uniquer{ *block_uniquer_impl },
|
|
vote_uniquer_impl{ std::make_unique<nano::vote_uniquer> () },
|
|
vote_uniquer{ *vote_uniquer_impl },
|
|
vote_cache_impl{ std::make_unique<nano::vote_cache> (config.vote_cache, stats) },
|
|
vote_cache{ *vote_cache_impl },
|
|
vote_router_impl{ std::make_unique<nano::vote_router> (vote_cache, active.recently_confirmed) },
|
|
vote_router{ *vote_router_impl },
|
|
vote_processor_impl{ std::make_unique<nano::vote_processor> (config.vote_processor, vote_router, observers, stats, flags, logger, online_reps, rep_crawler, ledger, network_params, rep_tiers) },
|
|
vote_processor{ *vote_processor_impl },
|
|
vote_cache_processor_impl{ std::make_unique<nano::vote_cache_processor> (config.vote_processor, vote_router, vote_cache, stats, logger) },
|
|
vote_cache_processor{ *vote_cache_processor_impl },
|
|
generator_impl{ std::make_unique<nano::vote_generator> (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false, loopback_channel) },
|
|
generator{ *generator_impl },
|
|
final_generator_impl{ std::make_unique<nano::vote_generator> (config, *this, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true, loopback_channel) },
|
|
final_generator{ *final_generator_impl },
|
|
scheduler_impl{ std::make_unique<nano::scheduler::component> (config, *this, ledger, ledger_notifications, bucketing, active, online_reps, vote_cache, confirming_set, stats, logger) },
|
|
scheduler{ *scheduler_impl },
|
|
aggregator_impl{ std::make_unique<nano::request_aggregator> (config.request_aggregator, *this, generator, final_generator, history, ledger, wallets, vote_router) },
|
|
aggregator{ *aggregator_impl },
|
|
backlog_scan_impl{ std::make_unique<nano::backlog_scan> (config.backlog_scan, ledger, stats) },
|
|
backlog_scan{ *backlog_scan_impl },
|
|
backlog_impl{ std::make_unique<nano::bounded_backlog> (config, *this, ledger, ledger_notifications, bucketing, backlog_scan, block_processor, confirming_set, stats, logger) },
|
|
backlog{ *backlog_impl },
|
|
bootstrap_server_impl{ std::make_unique<nano::bootstrap_server> (config.bootstrap_server, store, ledger, network_params.network, stats) },
|
|
bootstrap_server{ *bootstrap_server_impl },
|
|
bootstrap_impl{ std::make_unique<nano::bootstrap_service> (config, ledger, ledger_notifications, block_processor, network, stats, logger) },
|
|
bootstrap{ *bootstrap_impl },
|
|
websocket_impl{ std::make_unique<nano::websocket_server> (config.websocket_config, *this, observers, wallets, ledger, io_ctx, logger) },
|
|
websocket{ *websocket_impl },
|
|
epoch_upgrader_impl{ std::make_unique<nano::epoch_upgrader> (*this, ledger, store, network_params, logger) },
|
|
epoch_upgrader{ *epoch_upgrader_impl },
|
|
local_block_broadcaster_impl{ std::make_unique<nano::local_block_broadcaster> (config.local_block_broadcaster, *this, ledger_notifications, network, confirming_set, stats, logger, !flags.disable_block_processor_republishing) },
|
|
local_block_broadcaster{ *local_block_broadcaster_impl },
|
|
peer_history_impl{ std::make_unique<nano::peer_history> (config.peer_history, store, network, logger, stats) },
|
|
peer_history{ *peer_history_impl },
|
|
monitor_impl{ std::make_unique<nano::monitor> (config.monitor, *this) },
|
|
monitor{ *monitor_impl },
|
|
http_callbacks_impl{ std::make_unique<nano::http_callbacks> (*this) },
|
|
http_callbacks{ *http_callbacks_impl },
|
|
pruning_impl{ std::make_unique<nano::pruning> (config, flags, ledger, stats, logger) },
|
|
pruning{ *pruning_impl },
|
|
vote_rebroadcaster_impl{ std::make_unique<nano::vote_rebroadcaster> (vote_router, network, wallets, stats, logger) },
|
|
vote_rebroadcaster{ *vote_rebroadcaster_impl },
|
|
startup_time{ std::chrono::steady_clock::now () },
|
|
node_seq{ seq }
|
|
{
|
|
logger.debug (nano::log::type::node, "Constructing node...");
|
|
|
|
vote_cache.rep_weight_query = [this] (nano::account const & rep) {
|
|
return ledger.weight (rep);
|
|
};
|
|
|
|
// TODO: Hook this direclty in the schedulers
|
|
backlog_scan.batch_activated.add ([this] (auto const & batch) {
|
|
auto transaction = ledger.tx_begin_read ();
|
|
for (auto const & info : batch)
|
|
{
|
|
scheduler.optimistic.activate (info.account, info.account_info, info.conf_info);
|
|
scheduler.priority.activate (transaction, info.account, info.account_info, info.conf_info);
|
|
}
|
|
});
|
|
|
|
// Do some cleanup due to this block never being processed by confirmation height processor
|
|
confirming_set.cementing_failed.add ([this] (auto const & hash) {
|
|
active.recently_confirmed.erase (hash);
|
|
});
|
|
|
|
// Announce new blocks via websocket
|
|
ledger_notifications.blocks_processed.add ([this] (auto const & batch) {
|
|
auto const transaction = ledger.tx_begin_read ();
|
|
for (auto const & [result, context] : batch)
|
|
{
|
|
if (result == nano::block_status::progress)
|
|
{
|
|
if (websocket.server && websocket.server->any_subscriber (nano::websocket::topic::new_unconfirmed_block))
|
|
{
|
|
websocket.server->broadcast (nano::websocket::message_builder (ledger).new_block_arrived (*context.block));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Do some cleanup of rolled back blocks
|
|
ledger_notifications.blocks_rolled_back.add ([this] (auto const & blocks, auto const & rollback_root) {
|
|
for (auto const & block : blocks)
|
|
{
|
|
history.erase (block->root ());
|
|
}
|
|
});
|
|
|
|
// Representative is defined as online if replying to live votes or rep crawler queries
|
|
observers.vote.add ([this] (std::shared_ptr<nano::vote> vote, std::shared_ptr<nano::transport::channel> const & channel, nano::vote_source source, nano::vote_code code) {
|
|
release_assert (vote != nullptr);
|
|
release_assert (channel != nullptr);
|
|
debug_assert (code != nano::vote_code::invalid);
|
|
|
|
// Track rep weight voting on live elections
|
|
bool should_observe = (code != nano::vote_code::indeterminate);
|
|
|
|
// Ignore republished votes when rep crawling
|
|
if (source == nano::vote_source::live)
|
|
{
|
|
should_observe |= rep_crawler.process (vote, channel);
|
|
}
|
|
|
|
if (should_observe)
|
|
{
|
|
online_reps.observe (vote->account);
|
|
}
|
|
});
|
|
|
|
if (!init_error ())
|
|
{
|
|
wallets.observer = [this] (bool active) {
|
|
observers.wallet.notify (active);
|
|
};
|
|
network.disconnect_observer = [this] () {
|
|
observers.disconnect.notify ();
|
|
};
|
|
|
|
observers.channel_connected.add ([this] (std::shared_ptr<nano::transport::channel> const & channel) {
|
|
network.send_keepalive_self (channel);
|
|
});
|
|
|
|
// Cancelling local work generation
|
|
observers.work_cancel.add ([this] (nano::root const & root_a) {
|
|
this->work.cancel (root_a);
|
|
this->distributed_work.cancel (root_a);
|
|
});
|
|
|
|
auto const network_label = network_params.network.get_current_network_as_string ();
|
|
|
|
logger.info (nano::log::type::node, "Version: {}", NANO_VERSION_STRING);
|
|
logger.info (nano::log::type::node, "Build information: {}", BUILD_INFO);
|
|
logger.info (nano::log::type::node, "Active network: {}", network_label);
|
|
logger.info (nano::log::type::node, "Database backend: {}", store.vendor_get ());
|
|
logger.info (nano::log::type::node, "Data path: {}", application_path.string ());
|
|
logger.info (nano::log::type::node, "Ledger path: {}", store.get_database_path ().string ());
|
|
logger.info (nano::log::type::node, "Work pool threads: {} ({})", work.threads.size (), (work.opencl ? "OpenCL" : "CPU"));
|
|
logger.info (nano::log::type::node, "Work peers: {}", config.work_peers.size ());
|
|
logger.info (nano::log::type::node, "Node ID: {}", node_id.pub.to_node_id ());
|
|
logger.info (nano::log::type::node, "Number of buckets: {}", bucketing.size ());
|
|
logger.info (nano::log::type::node, "Genesis block: {}", config.network_params.ledger.genesis->hash ().to_string ());
|
|
logger.info (nano::log::type::node, "Genesis account: {}", config.network_params.ledger.genesis->account ().to_account ());
|
|
|
|
if (!work_generation_enabled ())
|
|
{
|
|
logger.warn (nano::log::type::node, "Work generation is disabled");
|
|
}
|
|
|
|
logger.info (nano::log::type::node, "Outbound bandwidth limit: {} bytes/s, burst ratio: {}",
|
|
config.bandwidth_limit,
|
|
config.bandwidth_limit_burst_ratio);
|
|
|
|
// First do a pass with a read to see if any writing needs doing, this saves needing to open a write lock (and potentially blocking)
|
|
auto is_initialized (false);
|
|
{
|
|
auto const transaction (store.tx_begin_read ());
|
|
is_initialized = (store.account.begin (transaction) != store.account.end (transaction));
|
|
}
|
|
|
|
if (!is_initialized && !flags.read_only)
|
|
{
|
|
auto const transaction = store.tx_begin_write ();
|
|
// Store was empty meaning we just created it, add the genesis block
|
|
store.initialize (transaction, ledger.cache, ledger.constants);
|
|
}
|
|
|
|
if (!block_or_pruned_exists (config.network_params.ledger.genesis->hash ()))
|
|
{
|
|
logger.critical (nano::log::type::node, "Genesis block not found. This commonly indicates a configuration issue, check that the --network or --data_path command line arguments are correct, and also the ledger backend node config option. If using a read-only CLI command a ledger must already exist, start the node with --daemon first.");
|
|
|
|
if (network_params.network.is_beta_network ())
|
|
{
|
|
logger.critical (nano::log::type::node, "Beta network may have reset, try clearing database files");
|
|
}
|
|
|
|
std::exit (1);
|
|
}
|
|
|
|
if (config.enable_voting)
|
|
{
|
|
auto reps = wallets.reps ();
|
|
logger.info (nano::log::type::node, "Voting is enabled, more system resources will be used, local representatives: {}", reps.accounts.size ());
|
|
for (auto const & account : reps.accounts)
|
|
{
|
|
logger.info (nano::log::type::node, "Local representative: {}", account.to_account ());
|
|
}
|
|
if (reps.accounts.size () > 1)
|
|
{
|
|
logger.warn (nano::log::type::node, "Voting with more than one representative can limit performance");
|
|
}
|
|
}
|
|
|
|
if ((network_params.network.is_live_network () || network_params.network.is_beta_network ()) && !flags.inactive_node)
|
|
{
|
|
auto const bootstrap_weights = get_bootstrap_weights ();
|
|
ledger.bootstrap_weight_max_blocks = bootstrap_weights.first;
|
|
ledger.bootstrap_weights = bootstrap_weights.second;
|
|
|
|
logger.info (nano::log::type::node, "Initial bootstrap height: {:>10}", ledger.bootstrap_weight_max_blocks);
|
|
logger.info (nano::log::type::node, "Current ledger height: {:>10}", ledger.block_count ());
|
|
|
|
// Use bootstrap weights if initial bootstrap is not completed
|
|
const bool use_bootstrap_weight = !ledger.bootstrap_height_reached ();
|
|
if (use_bootstrap_weight)
|
|
{
|
|
logger.info (nano::log::type::node, "Using predefined representative weights, since block count is less than bootstrap threshold");
|
|
logger.info (nano::log::type::node, "******************************************** Bootstrap weights ********************************************");
|
|
|
|
// Sort the weights
|
|
std::vector<std::pair<nano::account, nano::uint128_t>> sorted_weights (ledger.bootstrap_weights.begin (), ledger.bootstrap_weights.end ());
|
|
std::sort (sorted_weights.begin (), sorted_weights.end (), [] (auto const & entry1, auto const & entry2) {
|
|
return entry1.second > entry2.second;
|
|
});
|
|
|
|
for (auto const & rep : sorted_weights)
|
|
{
|
|
logger.info (nano::log::type::node, "Using bootstrap rep weight: {} -> {}",
|
|
rep.first.to_account (),
|
|
nano::uint128_union (rep.second).format_balance (nano_ratio, 0, true));
|
|
}
|
|
|
|
logger.info (nano::log::type::node, "******************************************** ================= ********************************************");
|
|
}
|
|
}
|
|
|
|
ledger.pruning = flags.enable_pruning || store.pruned.count (store.tx_begin_read ()) > 0;
|
|
|
|
if (ledger.pruning)
|
|
{
|
|
if (config.enable_voting && !flags.inactive_node)
|
|
{
|
|
logger.critical (nano::log::type::node, "Incompatibility detected between config node.enable_voting and existing pruned blocks");
|
|
std::exit (1);
|
|
}
|
|
else if (!flags.enable_pruning && !flags.inactive_node)
|
|
{
|
|
logger.critical (nano::log::type::node, "To start node with existing pruned blocks use launch flag --enable_pruning");
|
|
std::exit (1);
|
|
}
|
|
}
|
|
confirming_set.cemented_observers.add ([this] (auto const & block) {
|
|
// TODO: Is it neccessary to call this for all blocks?
|
|
if (block->is_send ())
|
|
{
|
|
wallet_workers.post ([this, hash = block->hash (), destination = block->destination ()] () {
|
|
wallets.receive_confirmed (hash, destination);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
logger.error (nano::log::type::node, "Failed to initialize node");
|
|
}
|
|
|
|
node_initialized_latch.count_down ();
|
|
}
|
|
|
|
nano::node::~node ()
|
|
{
|
|
logger.debug (nano::log::type::node, "Destructing node...");
|
|
stop ();
|
|
}
|
|
|
|
bool nano::node::copy_with_compaction (std::filesystem::path const & destination)
|
|
{
|
|
return store.copy_db (destination);
|
|
}
|
|
|
|
void nano::node::keepalive (std::string const & address_a, uint16_t port_a)
|
|
{
|
|
auto node_l (shared_from_this ());
|
|
network.resolver.async_resolve (boost::asio::ip::tcp::resolver::query (address_a, std::to_string (port_a)), [node_l, address_a, port_a] (boost::system::error_code const & ec, boost::asio::ip::tcp::resolver::iterator i_a) {
|
|
if (!ec)
|
|
{
|
|
for (auto i (i_a), n (boost::asio::ip::tcp::resolver::iterator{}); i != n; ++i)
|
|
{
|
|
auto endpoint (nano::transport::map_endpoint_to_v6 (i->endpoint ()));
|
|
std::weak_ptr<nano::node> node_w (node_l);
|
|
auto channel (node_l->network.find_channel (endpoint));
|
|
if (!channel)
|
|
{
|
|
node_l->network.tcp_channels.start_tcp (endpoint);
|
|
}
|
|
else
|
|
{
|
|
node_l->network.send_keepalive (channel);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
node_l->logger.error (nano::log::type::node, "Error resolving address for keepalive: {}:{} ({})", address_a, port_a, ec.message ());
|
|
}
|
|
});
|
|
}
|
|
|
|
void nano::node::inbound (const nano::message & message, const std::shared_ptr<nano::transport::channel> & channel)
|
|
{
|
|
debug_assert (channel->owner () == shared_from_this ()); // This node should be the channel owner
|
|
|
|
debug_assert (message.header.network == network_params.network.current_network);
|
|
debug_assert (message.header.version_using >= network_params.network.protocol_version_min);
|
|
|
|
message_processor.process (message, channel);
|
|
}
|
|
|
|
void nano::node::process_active (std::shared_ptr<nano::block> const & incoming)
|
|
{
|
|
block_processor.add (incoming);
|
|
}
|
|
|
|
[[nodiscard]] nano::block_status nano::node::process (secure::write_transaction const & transaction, std::shared_ptr<nano::block> block)
|
|
{
|
|
auto status = ledger.process (transaction, block);
|
|
logger.debug (nano::log::type::node, "Directly processed block: {} (status: {})", block->hash (), to_string (status));
|
|
return status;
|
|
}
|
|
|
|
nano::block_status nano::node::process (std::shared_ptr<nano::block> block)
|
|
{
|
|
auto const transaction = ledger.tx_begin_write (nano::store::writer::node);
|
|
return process (transaction, block);
|
|
}
|
|
|
|
std::optional<nano::block_status> nano::node::process_local (std::shared_ptr<nano::block> const & block_a)
|
|
{
|
|
return block_processor.add_blocking (block_a, nano::block_source::local);
|
|
}
|
|
|
|
void nano::node::process_local_async (std::shared_ptr<nano::block> const & block_a)
|
|
{
|
|
block_processor.add (block_a, nano::block_source::local);
|
|
}
|
|
|
|
void nano::node::start ()
|
|
{
|
|
network.start ();
|
|
message_processor.start ();
|
|
|
|
if (!flags.disable_rep_crawler)
|
|
{
|
|
rep_crawler.start ();
|
|
}
|
|
|
|
bool tcp_enabled = false;
|
|
if (!(flags.disable_bootstrap_listener && flags.disable_tcp_realtime))
|
|
{
|
|
tcp_listener.start ();
|
|
tcp_enabled = true;
|
|
|
|
if (network.port != tcp_listener.endpoint ().port ())
|
|
{
|
|
network.port = tcp_listener.endpoint ().port ();
|
|
}
|
|
|
|
logger.info (nano::log::type::node, "Peering port: {}", network.port.load ());
|
|
}
|
|
else
|
|
{
|
|
logger.warn (nano::log::type::node, "Peering is disabled");
|
|
}
|
|
|
|
if (!flags.disable_backup)
|
|
{
|
|
backup_wallet ();
|
|
}
|
|
if (!flags.disable_search_pending)
|
|
{
|
|
search_receivable_all ();
|
|
}
|
|
// Start port mapping if external address is not defined and TCP ports are enabled
|
|
if (config.external_address == boost::asio::ip::address_v6::any ().to_string () && tcp_enabled)
|
|
{
|
|
port_mapping.start ();
|
|
}
|
|
unchecked.start ();
|
|
wallets.start ();
|
|
rep_tiers.start ();
|
|
vote_processor.start ();
|
|
vote_cache_processor.start ();
|
|
ledger_notifications.start ();
|
|
block_processor.start ();
|
|
active.start ();
|
|
generator.start ();
|
|
final_generator.start ();
|
|
confirming_set.start ();
|
|
scheduler.start ();
|
|
aggregator.start ();
|
|
backlog_scan.start ();
|
|
backlog.start ();
|
|
bootstrap_server.start ();
|
|
bootstrap.start ();
|
|
websocket.start ();
|
|
telemetry.start ();
|
|
stats.start ();
|
|
local_block_broadcaster.start ();
|
|
peer_history.start ();
|
|
vote_router.start ();
|
|
online_reps.start ();
|
|
monitor.start ();
|
|
http_callbacks.start ();
|
|
pruning.start ();
|
|
vote_rebroadcaster.start ();
|
|
|
|
add_initial_peers ();
|
|
}
|
|
|
|
void nano::node::stop ()
|
|
{
|
|
// Ensure stop can only be called once
|
|
if (stopped.exchange (true))
|
|
{
|
|
return;
|
|
}
|
|
|
|
logger.info (nano::log::type::node, "Node stopping...");
|
|
|
|
tcp_listener.stop ();
|
|
online_reps.stop ();
|
|
vote_router.stop ();
|
|
peer_history.stop ();
|
|
// Cancels ongoing work generation tasks, which may be blocking other threads
|
|
// No tasks may wait for work generation in I/O threads, or termination signal capturing will be unable to call node::stop()
|
|
distributed_work.stop ();
|
|
backlog_scan.stop ();
|
|
bootstrap.stop ();
|
|
backlog.stop ();
|
|
rep_crawler.stop ();
|
|
unchecked.stop ();
|
|
block_processor.stop ();
|
|
aggregator.stop ();
|
|
vote_cache_processor.stop ();
|
|
vote_processor.stop ();
|
|
rep_tiers.stop ();
|
|
scheduler.stop ();
|
|
active.stop ();
|
|
generator.stop ();
|
|
final_generator.stop ();
|
|
confirming_set.stop ();
|
|
ledger_notifications.stop ();
|
|
telemetry.stop ();
|
|
websocket.stop ();
|
|
bootstrap_server.stop ();
|
|
port_mapping.stop ();
|
|
wallets.stop ();
|
|
stats.stop ();
|
|
epoch_upgrader.stop ();
|
|
local_block_broadcaster.stop ();
|
|
message_processor.stop ();
|
|
network.stop ();
|
|
monitor.stop ();
|
|
http_callbacks.stop ();
|
|
pruning.stop ();
|
|
vote_rebroadcaster.stop ();
|
|
|
|
bootstrap_workers.stop ();
|
|
wallet_workers.stop ();
|
|
election_workers.stop ();
|
|
workers.stop ();
|
|
|
|
// work pool is not stopped on purpose due to testing setup
|
|
|
|
// Stop the IO runner last
|
|
runner.abort ();
|
|
runner.join ();
|
|
debug_assert (io_ctx_shared.use_count () == 1); // Node should be the last user of the io_context
|
|
}
|
|
|
|
void nano::node::keepalive_preconfigured ()
|
|
{
|
|
for (auto const & peer : config.preconfigured_peers)
|
|
{
|
|
// can't use `network.port` here because preconfigured peers are referenced
|
|
// just by their address, so we rely on them listening on the default port
|
|
//
|
|
keepalive (peer, network_params.network.default_node_port);
|
|
}
|
|
}
|
|
|
|
nano::block_hash nano::node::latest (nano::account const & account_a)
|
|
{
|
|
return ledger.any.account_head (ledger.tx_begin_read (), account_a);
|
|
}
|
|
|
|
nano::uint128_t nano::node::balance (nano::account const & account_a)
|
|
{
|
|
return ledger.any.account_balance (ledger.tx_begin_read (), account_a).value_or (0).number ();
|
|
}
|
|
|
|
std::shared_ptr<nano::block> nano::node::block (nano::block_hash const & hash_a)
|
|
{
|
|
return ledger.any.block_get (ledger.tx_begin_read (), hash_a);
|
|
}
|
|
|
|
bool nano::node::block_or_pruned_exists (nano::block_hash const & hash_a) const
|
|
{
|
|
return ledger.any.block_exists_or_pruned (ledger.tx_begin_read (), hash_a);
|
|
}
|
|
|
|
std::pair<nano::uint128_t, nano::uint128_t> nano::node::balance_pending (nano::account const & account_a, bool only_confirmed_a)
|
|
{
|
|
std::pair<nano::uint128_t, nano::uint128_t> result;
|
|
auto const transaction = ledger.tx_begin_read ();
|
|
result.first = only_confirmed_a ? ledger.confirmed.account_balance (transaction, account_a).value_or (0).number () : ledger.any.account_balance (transaction, account_a).value_or (0).number ();
|
|
result.second = ledger.account_receivable (transaction, account_a, only_confirmed_a);
|
|
return result;
|
|
}
|
|
|
|
nano::uint128_t nano::node::weight (nano::account const & account_a)
|
|
{
|
|
auto txn = ledger.tx_begin_read ();
|
|
return ledger.weight_exact (txn, account_a);
|
|
}
|
|
|
|
nano::uint128_t nano::node::minimum_principal_weight ()
|
|
{
|
|
return online_reps.trended () / network_params.network.principal_weight_factor;
|
|
}
|
|
|
|
void nano::node::backup_wallet ()
|
|
{
|
|
auto transaction (wallets.tx_begin_read ());
|
|
for (auto i (wallets.items.begin ()), n (wallets.items.end ()); i != n; ++i)
|
|
{
|
|
boost::system::error_code error_chmod;
|
|
auto backup_path (application_path / "backup");
|
|
|
|
std::filesystem::create_directories (backup_path);
|
|
nano::set_secure_perm_directory (backup_path, error_chmod);
|
|
i->second->store.write_backup (transaction, backup_path / (i->first.to_string () + ".json"));
|
|
}
|
|
auto this_l (shared ());
|
|
workers.post_delayed (network_params.node.backup_interval, [this_l] () {
|
|
this_l->backup_wallet ();
|
|
});
|
|
}
|
|
|
|
void nano::node::search_receivable_all ()
|
|
{
|
|
// Reload wallets from disk
|
|
wallets.reload ();
|
|
// Search pending
|
|
wallets.search_receivable_all ();
|
|
auto this_l (shared ());
|
|
workers.post_delayed (network_params.node.search_pending_interval, [this_l] () {
|
|
this_l->search_receivable_all ();
|
|
});
|
|
}
|
|
|
|
uint64_t nano::node::default_difficulty (nano::work_version const version_a) const
|
|
{
|
|
uint64_t result{ std::numeric_limits<uint64_t>::max () };
|
|
switch (version_a)
|
|
{
|
|
case nano::work_version::work_1:
|
|
result = network_params.work.threshold_base (version_a);
|
|
break;
|
|
default:
|
|
debug_assert (false && "Invalid version specified to default_difficulty");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint64_t nano::node::default_receive_difficulty (nano::work_version const version_a) const
|
|
{
|
|
uint64_t result{ std::numeric_limits<uint64_t>::max () };
|
|
switch (version_a)
|
|
{
|
|
case nano::work_version::work_1:
|
|
result = network_params.work.epoch_2_receive;
|
|
break;
|
|
default:
|
|
debug_assert (false && "Invalid version specified to default_receive_difficulty");
|
|
}
|
|
return result;
|
|
}
|
|
|
|
uint64_t nano::node::max_work_generate_difficulty (nano::work_version const version_a) const
|
|
{
|
|
return nano::difficulty::from_multiplier (config.max_work_generate_multiplier, default_difficulty (version_a));
|
|
}
|
|
|
|
bool nano::node::local_work_generation_enabled () const
|
|
{
|
|
return config.work_threads > 0 || work.opencl;
|
|
}
|
|
|
|
bool nano::node::work_generation_enabled () const
|
|
{
|
|
return work_generation_enabled (config.work_peers);
|
|
}
|
|
|
|
bool nano::node::work_generation_enabled (std::vector<std::pair<std::string, uint16_t>> const & peers_a) const
|
|
{
|
|
return !peers_a.empty () || local_work_generation_enabled ();
|
|
}
|
|
|
|
std::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a, uint64_t difficulty_a)
|
|
{
|
|
auto opt_work_l (work_generate_blocking (block_a.work_version (), block_a.root (), difficulty_a, block_a.account_field ()));
|
|
if (opt_work_l.has_value ())
|
|
{
|
|
block_a.block_work_set (opt_work_l.value ());
|
|
}
|
|
return opt_work_l;
|
|
}
|
|
|
|
void nano::node::work_generate (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::function<void (std::optional<uint64_t>)> callback_a, std::optional<nano::account> const & account_a, bool secondary_work_peers_a)
|
|
{
|
|
auto const & peers_l (secondary_work_peers_a ? config.secondary_work_peers : config.work_peers);
|
|
if (distributed_work.make (version_a, root_a, peers_l, difficulty_a, callback_a, account_a))
|
|
{
|
|
// Error in creating the job (either stopped or work generation is not possible)
|
|
callback_a (std::nullopt);
|
|
}
|
|
}
|
|
|
|
std::optional<uint64_t> nano::node::work_generate_blocking (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::optional<nano::account> const & account_a)
|
|
{
|
|
std::promise<std::optional<uint64_t>> promise;
|
|
work_generate (
|
|
version_a, root_a, difficulty_a, [&promise] (std::optional<uint64_t> opt_work_a) {
|
|
promise.set_value (opt_work_a);
|
|
},
|
|
account_a);
|
|
return promise.get_future ().get ();
|
|
}
|
|
|
|
std::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a)
|
|
{
|
|
debug_assert (network_params.network.is_dev_network ());
|
|
return work_generate_blocking (block_a, default_difficulty (nano::work_version::work_1));
|
|
}
|
|
|
|
std::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a)
|
|
{
|
|
debug_assert (network_params.network.is_dev_network ());
|
|
return work_generate_blocking (root_a, default_difficulty (nano::work_version::work_1));
|
|
}
|
|
|
|
std::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a, uint64_t difficulty_a)
|
|
{
|
|
debug_assert (network_params.network.is_dev_network ());
|
|
return work_generate_blocking (nano::work_version::work_1, root_a, difficulty_a);
|
|
}
|
|
|
|
void nano::node::add_initial_peers ()
|
|
{
|
|
if (flags.disable_add_initial_peers)
|
|
{
|
|
logger.warn (nano::log::type::node, "Not adding initial peers because `disable_add_initial_peers` flag is set");
|
|
return;
|
|
}
|
|
|
|
auto initial_peers = peer_history.peers ();
|
|
|
|
logger.info (nano::log::type::node, "Adding cached initial peers: {}", initial_peers.size ());
|
|
|
|
for (auto const & peer : initial_peers)
|
|
{
|
|
network.merge_peer (peer);
|
|
}
|
|
}
|
|
|
|
void nano::node::start_election (std::shared_ptr<nano::block> const & block)
|
|
{
|
|
scheduler.manual.push (block);
|
|
}
|
|
|
|
bool nano::node::block_confirmed (nano::block_hash const & hash)
|
|
{
|
|
return ledger.confirmed.block_exists_or_pruned (ledger.tx_begin_read (), hash);
|
|
}
|
|
|
|
bool nano::node::block_confirmed_or_being_confirmed (nano::secure::transaction const & transaction, nano::block_hash const & hash)
|
|
{
|
|
return confirming_set.contains (hash) || ledger.confirmed.block_exists_or_pruned (transaction, hash);
|
|
}
|
|
|
|
bool nano::node::block_confirmed_or_being_confirmed (nano::block_hash const & hash_a)
|
|
{
|
|
return block_confirmed_or_being_confirmed (ledger.tx_begin_read (), hash_a);
|
|
}
|
|
|
|
bool nano::node::online () const
|
|
{
|
|
return rep_crawler.total_weight () > online_reps.delta ();
|
|
}
|
|
|
|
std::shared_ptr<nano::node> nano::node::shared ()
|
|
{
|
|
return shared_from_this ();
|
|
}
|
|
|
|
int nano::node::store_version ()
|
|
{
|
|
auto transaction (store.tx_begin_read ());
|
|
return store.version.get (transaction);
|
|
}
|
|
|
|
bool nano::node::init_error () const
|
|
{
|
|
return store.init_error () || wallets_store.init_error ();
|
|
}
|
|
|
|
std::pair<uint64_t, std::unordered_map<nano::account, nano::uint128_t>> nano::node::get_bootstrap_weights () const
|
|
{
|
|
std::vector<std::pair<std::string, std::string>> preconfigured_weights = network_params.network.is_live_network () ? nano::weights::preconfigured_weights_live : nano::weights::preconfigured_weights_beta;
|
|
uint64_t max_blocks = network_params.network.is_live_network () ? nano::weights::max_blocks_live : nano::weights::max_blocks_beta;
|
|
std::unordered_map<nano::account, nano::uint128_t> weights;
|
|
|
|
for (const auto & entry : preconfigured_weights)
|
|
{
|
|
nano::account account;
|
|
account.decode_account (entry.first);
|
|
weights[account] = nano::uint128_t (entry.second);
|
|
}
|
|
|
|
return { max_blocks, weights };
|
|
}
|
|
|
|
void nano::node::bootstrap_block (const nano::block_hash & hash)
|
|
{
|
|
// If we are running pruning node check if block was not already pruned
|
|
if (!ledger.pruning || !store.pruned.exists (store.tx_begin_read (), hash))
|
|
{
|
|
// We don't have the block, try to bootstrap it
|
|
// TODO: Use ascending bootstraper to bootstrap block hash
|
|
}
|
|
}
|
|
|
|
uint64_t nano::node::block_count () const
|
|
{
|
|
return ledger.block_count ();
|
|
}
|
|
|
|
uint64_t nano::node::cemented_count () const
|
|
{
|
|
return ledger.cemented_count ();
|
|
}
|
|
|
|
nano::account nano::node::get_node_id () const
|
|
{
|
|
return node_id.pub;
|
|
};
|
|
|
|
nano::telemetry_data nano::node::local_telemetry () const
|
|
{
|
|
nano::telemetry_data telemetry_data;
|
|
telemetry_data.node_id = node_id.pub;
|
|
telemetry_data.block_count = ledger.block_count ();
|
|
telemetry_data.cemented_count = ledger.cemented_count ();
|
|
telemetry_data.bandwidth_cap = config.bandwidth_limit;
|
|
telemetry_data.protocol_version = network_params.network.protocol_version;
|
|
telemetry_data.uptime = std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now () - startup_time).count ();
|
|
telemetry_data.unchecked_count = unchecked.count ();
|
|
telemetry_data.genesis_block = network_params.ledger.genesis->hash ();
|
|
telemetry_data.peer_count = nano::narrow_cast<decltype (telemetry_data.peer_count)> (network.size ());
|
|
telemetry_data.account_count = ledger.account_count ();
|
|
telemetry_data.major_version = nano::get_major_node_version ();
|
|
telemetry_data.minor_version = nano::get_minor_node_version ();
|
|
telemetry_data.patch_version = nano::get_patch_node_version ();
|
|
telemetry_data.pre_release_version = nano::get_pre_release_node_version ();
|
|
telemetry_data.maker = static_cast<std::underlying_type_t<telemetry_maker>> (ledger.pruning ? telemetry_maker::nf_pruned_node : telemetry_maker::nf_node);
|
|
telemetry_data.timestamp = std::chrono::system_clock::now ();
|
|
telemetry_data.active_difficulty = default_difficulty (nano::work_version::work_1);
|
|
// Make sure this is the final operation!
|
|
telemetry_data.sign (node_id);
|
|
return telemetry_data;
|
|
}
|
|
|
|
std::string nano::node::identifier () const
|
|
{
|
|
return make_logger_identifier (node_id);
|
|
}
|
|
|
|
std::string nano::node::make_logger_identifier (const nano::keypair & node_id)
|
|
{
|
|
// Node identifier consists of first 10 characters of node id
|
|
return node_id.pub.to_node_id ().substr (0, 10);
|
|
}
|
|
|
|
nano::container_info nano::node::container_info () const
|
|
{
|
|
/*
|
|
* TODO: Add container infos for:
|
|
* - bootstrap_server
|
|
* - peer_history
|
|
* - port_mapping
|
|
* - epoch_upgrader
|
|
* - websocket
|
|
*/
|
|
|
|
nano::container_info info;
|
|
info.add ("work", work.container_info ());
|
|
info.add ("ledger", ledger.container_info ());
|
|
info.add ("active", active.container_info ());
|
|
info.add ("tcp_listener", tcp_listener.container_info ());
|
|
info.add ("network", network.container_info ());
|
|
info.add ("telemetry", telemetry.container_info ());
|
|
info.add ("workers", workers.container_info ());
|
|
info.add ("bootstrap_workers", bootstrap_workers.container_info ());
|
|
info.add ("wallet_workers", wallet_workers.container_info ());
|
|
info.add ("election_workers", election_workers.container_info ());
|
|
info.add ("observers", observers.container_info ());
|
|
info.add ("wallets", wallets.container_info ());
|
|
info.add ("vote_processor", vote_processor.container_info ());
|
|
info.add ("vote_cache_processor", vote_cache_processor.container_info ());
|
|
info.add ("rep_crawler", rep_crawler.container_info ());
|
|
info.add ("block_processor", block_processor.container_info ());
|
|
info.add ("online_reps", online_reps.container_info ());
|
|
info.add ("history", history.container_info ());
|
|
info.add ("block_uniquer", block_uniquer.container_info ());
|
|
info.add ("vote_uniquer", vote_uniquer.container_info ());
|
|
info.add ("confirming_set", confirming_set.container_info ());
|
|
info.add ("distributed_work", distributed_work.container_info ());
|
|
info.add ("aggregator", aggregator.container_info ());
|
|
info.add ("scheduler", scheduler.container_info ());
|
|
info.add ("vote_cache", vote_cache.container_info ());
|
|
info.add ("vote_router", vote_router.container_info ());
|
|
info.add ("generator", generator.container_info ());
|
|
info.add ("final_generator", final_generator.container_info ());
|
|
info.add ("bootstrap", bootstrap.container_info ());
|
|
info.add ("unchecked", unchecked.container_info ());
|
|
info.add ("local_block_broadcaster", local_block_broadcaster.container_info ());
|
|
info.add ("rep_tiers", rep_tiers.container_info ());
|
|
info.add ("message_processor", message_processor.container_info ());
|
|
info.add ("bandwidth", outbound_limiter.container_info ());
|
|
info.add ("backlog_scan", backlog_scan.container_info ());
|
|
info.add ("bounded_backlog", backlog.container_info ());
|
|
info.add ("http_callbacks", http_callbacks.container_info ());
|
|
info.add ("pruning", pruning.container_info ());
|
|
info.add ("vote_rebroadcaster", vote_rebroadcaster.container_info ());
|
|
return info;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|
|
nano::keypair nano::load_or_create_node_id (std::filesystem::path const & application_path)
|
|
{
|
|
auto & logger = nano::default_logger ();
|
|
|
|
logger.info (nano::log::type::init, "Using data directory: {}", application_path.string ());
|
|
|
|
auto node_private_key_path = application_path / "node_id_private.key";
|
|
std::ifstream ifs (node_private_key_path.c_str ());
|
|
if (ifs.good ())
|
|
{
|
|
std::string node_private_key;
|
|
ifs >> node_private_key;
|
|
release_assert (node_private_key.size () == 64);
|
|
nano::keypair kp = nano::keypair (node_private_key);
|
|
|
|
logger.info (nano::log::type::init, "Loaded local node ID: {}",
|
|
kp.pub.to_node_id ());
|
|
|
|
return kp;
|
|
}
|
|
else
|
|
{
|
|
nano::keypair kp;
|
|
std::ofstream ofs (node_private_key_path.c_str (), std::ofstream::out | std::ofstream::trunc);
|
|
ofs << kp.prv.to_string () << std::endl
|
|
<< std::flush;
|
|
ofs.close ();
|
|
release_assert (!ofs.fail ());
|
|
|
|
logger.info (nano::log::type::init, "Generated new local node ID: {}",
|
|
kp.pub.to_node_id ());
|
|
|
|
return kp;
|
|
}
|
|
} |