dncurrency/nano/node/node.cpp
Wesley Shillingford f8158fc03c
Remove compiler warnings (incl from third party headers) (#2072)
* Remove compiler warnings

* Fix new warnings in test

* Remove new msvc warnings
2019-07-12 17:28:21 +01:00

1643 lines
55 KiB
C++

#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/timer.hpp>
#include <nano/lib/utility.hpp>
#include <nano/node/common.hpp>
#include <nano/node/node.hpp>
#include <nano/rpc/rpc.hpp>
#include <boost/polymorphic_cast.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <algorithm>
#include <cstdlib>
#include <future>
#include <numeric>
#include <sstream>
double constexpr nano::node::price_max;
double constexpr nano::node::free_cutoff;
size_t constexpr nano::block_arrival::arrival_size_min;
std::chrono::seconds constexpr nano::block_arrival::arrival_time_min;
namespace nano
{
extern unsigned char nano_bootstrap_weights_live[];
extern size_t nano_bootstrap_weights_live_size;
extern unsigned char nano_bootstrap_weights_beta[];
extern size_t nano_bootstrap_weights_beta_size;
}
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::udp::resolver::query (address_a, std::to_string (port_a)), [node_l, address_a, port_a](boost::system::error_code const & ec, boost::asio::ip::udp::resolver::iterator i_a) {
if (!ec)
{
for (auto i (i_a), n (boost::asio::ip::udp::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, [node_w](std::shared_ptr<nano::transport::channel> channel_a) {
if (auto node_l = node_w.lock ())
{
node_l->network.send_keepalive (channel_a);
}
});
}
else
{
node_l->network.send_keepalive (channel);
}
}
}
else
{
node_l->logger.try_log (boost::str (boost::format ("Error resolving address: %1%:%2%: %3%") % address_a % port_a % ec.message ()));
}
});
}
bool nano::operation::operator> (nano::operation const & other_a) const
{
return wakeup > other_a.wakeup;
}
nano::alarm::alarm (boost::asio::io_context & io_ctx_a) :
io_ctx (io_ctx_a),
thread ([this]() {
nano::thread_role::set (nano::thread_role::name::alarm);
run ();
})
{
}
nano::alarm::~alarm ()
{
add (std::chrono::steady_clock::now (), nullptr);
thread.join ();
}
void nano::alarm::run ()
{
std::unique_lock<std::mutex> lock (mutex);
auto done (false);
while (!done)
{
if (!operations.empty ())
{
auto & operation (operations.top ());
if (operation.function)
{
if (operation.wakeup <= std::chrono::steady_clock::now ())
{
io_ctx.post (operation.function);
operations.pop ();
}
else
{
auto wakeup (operation.wakeup);
condition.wait_until (lock, wakeup);
}
}
else
{
done = true;
}
}
else
{
condition.wait (lock);
}
}
}
void nano::alarm::add (std::chrono::steady_clock::time_point const & wakeup_a, std::function<void()> const & operation)
{
{
std::lock_guard<std::mutex> lock (mutex);
operations.push (nano::operation ({ wakeup_a, operation }));
}
condition.notify_all ();
}
namespace nano
{
std::unique_ptr<seq_con_info_component> collect_seq_con_info (alarm & alarm, const std::string & name)
{
auto composite = std::make_unique<seq_con_info_composite> (name);
size_t count = 0;
{
std::lock_guard<std::mutex> guard (alarm.mutex);
count = alarm.operations.size ();
}
auto sizeof_element = sizeof (decltype (alarm.operations)::value_type);
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "operations", count, sizeof_element }));
return composite;
}
}
bool nano::node_init::error () const
{
return block_store_init || wallets_store_init;
}
namespace nano
{
std::unique_ptr<seq_con_info_component> collect_seq_con_info (rep_crawler & rep_crawler, const std::string & name)
{
size_t count = 0;
{
std::lock_guard<std::mutex> guard (rep_crawler.active_mutex);
count = rep_crawler.active.size ();
}
auto sizeof_element = sizeof (decltype (rep_crawler.active)::value_type);
auto composite = std::make_unique<seq_con_info_composite> (name);
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "active", count, sizeof_element }));
return composite;
}
std::unique_ptr<seq_con_info_component> collect_seq_con_info (block_processor & block_processor, const std::string & name)
{
size_t state_blocks_count = 0;
size_t blocks_count = 0;
size_t blocks_hashes_count = 0;
size_t forced_count = 0;
size_t rolled_back_count = 0;
{
std::lock_guard<std::mutex> guard (block_processor.mutex);
state_blocks_count = block_processor.state_blocks.size ();
blocks_count = block_processor.blocks.size ();
blocks_hashes_count = block_processor.blocks_hashes.size ();
forced_count = block_processor.forced.size ();
rolled_back_count = block_processor.rolled_back.size ();
}
auto composite = std::make_unique<seq_con_info_composite> (name);
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "state_blocks", state_blocks_count, sizeof (decltype (block_processor.state_blocks)::value_type) }));
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "blocks", blocks_count, sizeof (decltype (block_processor.blocks)::value_type) }));
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "blocks_hashes", blocks_hashes_count, sizeof (decltype (block_processor.blocks_hashes)::value_type) }));
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "forced", forced_count, sizeof (decltype (block_processor.forced)::value_type) }));
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "rolled_back", rolled_back_count, sizeof (decltype (block_processor.rolled_back)::value_type) }));
composite->add_component (collect_seq_con_info (block_processor.generator, "generator"));
return composite;
}
}
nano::node::node (nano::node_init & init_a, boost::asio::io_context & io_ctx_a, uint16_t peering_port_a, boost::filesystem::path const & application_path_a, nano::alarm & alarm_a, nano::logging const & logging_a, nano::work_pool & work_a) :
node (init_a, io_ctx_a, application_path_a, alarm_a, nano::node_config (peering_port_a, logging_a), work_a)
{
}
nano::node::node (nano::node_init & init_a, boost::asio::io_context & io_ctx_a, boost::filesystem::path const & application_path_a, nano::alarm & alarm_a, nano::node_config const & config_a, nano::work_pool & work_a, nano::node_flags flags_a) :
io_ctx (io_ctx_a),
node_initialized_latch (1),
config (config_a),
stats (config.stat_config),
flags (flags_a),
alarm (alarm_a),
work (work_a),
logger (config_a.logging.min_time_between_log_output),
store_impl (std::make_unique<nano::mdb_store> (init_a.block_store_init, logger, application_path_a / "data.ldb", config_a.diagnostics_config.txn_tracking, config_a.block_processor_batch_max_time, config_a.lmdb_max_dbs, !flags.disable_unchecked_drop, flags.sideband_batch_size)),
store (*store_impl),
wallets_store_impl (std::make_unique<nano::mdb_wallets_store> (init_a.wallets_store_init, application_path_a / "wallets.ldb", config_a.lmdb_max_dbs)),
wallets_store (*wallets_store_impl),
gap_cache (*this),
ledger (store, stats, config.epoch_block_link, config.epoch_block_signer),
checker (config.signature_checker_threads),
network (*this, config.peering_port),
bootstrap_initiator (*this),
bootstrap (config.peering_port, *this),
application_path (application_path_a),
port_mapping (*this),
vote_processor (*this),
rep_crawler (*this),
warmed_up (0),
block_processor (*this, write_database_queue),
block_processor_thread ([this]() {
nano::thread_role::set (nano::thread_role::name::block_processing);
this->block_processor.process_blocks ();
}),
online_reps (*this, config.online_weight_minimum.number ()),
vote_uniquer (block_uniquer),
active (*this),
confirmation_height_processor (pending_confirmation_height, store, ledger.stats, active, ledger.epoch_link, write_database_queue, config.conf_height_processor_batch_min_time, logger),
payment_observer_processor (observers.blocks),
wallets (init_a.wallets_store_init, *this),
startup_time (std::chrono::steady_clock::now ())
{
if (!init_a.error ())
{
if (config.websocket_config.enabled)
{
auto endpoint_l (nano::tcp_endpoint (config.websocket_config.address, config.websocket_config.port));
websocket_server = std::make_shared<nano::websocket::listener> (*this, endpoint_l);
this->websocket_server->run ();
}
wallets.observer = [this](bool active) {
observers.wallet.notify (active);
};
network.channel_observer = [this](std::shared_ptr<nano::transport::channel> channel_a) {
observers.endpoint.notify (channel_a);
};
network.disconnect_observer = [this]() {
observers.disconnect.notify ();
};
if (!config.callback_address.empty ())
{
observers.blocks.add ([this](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
auto block_a (status_a.winner);
if ((status_a.type == nano::election_status_type::active_confirmed_quorum || status_a.type == nano::election_status_type::active_confirmation_height) && this->block_arrival.recent (block_a->hash ()))
{
auto node_l (shared_from_this ());
background ([node_l, block_a, account_a, amount_a, is_state_send_a]() {
boost::property_tree::ptree event;
event.add ("account", account_a.to_account ());
event.add ("hash", block_a->hash ().to_string ());
std::string block_text;
block_a->serialize_json (block_text);
event.add ("block", block_text);
event.add ("amount", amount_a.to_string_dec ());
if (is_state_send_a)
{
event.add ("is_send", is_state_send_a);
event.add ("subtype", "send");
}
// Subtype field
else if (block_a->type () == nano::block_type::state)
{
if (block_a->link ().is_zero ())
{
event.add ("subtype", "change");
}
else if (amount_a == 0 && !node_l->ledger.epoch_link.is_zero () && node_l->ledger.is_epoch_link (block_a->link ()))
{
event.add ("subtype", "epoch");
}
else
{
event.add ("subtype", "receive");
}
}
std::stringstream ostream;
boost::property_tree::write_json (ostream, event);
ostream.flush ();
auto body (std::make_shared<std::string> (ostream.str ()));
auto address (node_l->config.callback_address);
auto port (node_l->config.callback_port);
auto target (std::make_shared<std::string> (node_l->config.callback_target));
auto resolver (std::make_shared<boost::asio::ip::tcp::resolver> (node_l->io_ctx));
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (address, std::to_string (port)), [node_l, address, port, target, body, resolver](boost::system::error_code const & ec, boost::asio::ip::tcp::resolver::iterator i_a) {
if (!ec)
{
node_l->do_rpc_callback (i_a, address, port, target, body, resolver);
}
else
{
if (node_l->config.logging.callback_logging ())
{
node_l->logger.always_log (boost::str (boost::format ("Error resolving callback: %1%:%2%: %3%") % address % port % ec.message ()));
}
node_l->stats.inc (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out);
}
});
});
}
});
}
if (websocket_server)
{
observers.blocks.add ([this](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
assert (status_a.type != nano::election_status_type::ongoing);
if (this->websocket_server->any_subscriber (nano::websocket::topic::confirmation))
{
auto block_a (status_a.winner);
std::string subtype;
if (is_state_send_a)
{
subtype = "send";
}
else if (block_a->type () == nano::block_type::state)
{
if (block_a->link ().is_zero ())
{
subtype = "change";
}
else if (amount_a == 0 && !this->ledger.epoch_link.is_zero () && this->ledger.is_epoch_link (block_a->link ()))
{
subtype = "epoch";
}
else
{
subtype = "receive";
}
}
this->websocket_server->broadcast_confirmation (block_a, account_a, amount_a, subtype, status_a.type);
}
});
observers.active_stopped.add ([this](nano::block_hash const & hash_a) {
if (this->websocket_server->any_subscriber (nano::websocket::topic::stopped_election))
{
nano::websocket::message_builder builder;
this->websocket_server->broadcast (builder.stopped_election (hash_a));
}
});
observers.difficulty.add ([this](uint64_t active_difficulty) {
if (this->websocket_server->any_subscriber (nano::websocket::topic::active_difficulty))
{
nano::websocket::message_builder builder;
auto msg (builder.difficulty_changed (network_params.network.publish_threshold, active_difficulty));
this->websocket_server->broadcast (msg);
}
});
}
// Add block confirmation type stats regardless of http-callback and websocket subscriptions
observers.blocks.add ([this](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
assert (status_a.type != nano::election_status_type::ongoing);
switch (status_a.type)
{
case nano::election_status_type::active_confirmed_quorum:
this->stats.inc (nano::stat::type::observer, nano::stat::detail::observer_confirmation_active_quorum, nano::stat::dir::out);
break;
case nano::election_status_type::active_confirmation_height:
this->stats.inc (nano::stat::type::observer, nano::stat::detail::observer_confirmation_active_conf_height, nano::stat::dir::out);
break;
case nano::election_status_type::inactive_confirmation_height:
this->stats.inc (nano::stat::type::observer, nano::stat::detail::observer_confirmation_inactive, nano::stat::dir::out);
break;
default:
break;
}
});
observers.endpoint.add ([this](std::shared_ptr<nano::transport::channel> channel_a) {
if (channel_a->get_type () == nano::transport::transport_type::udp)
{
this->network.send_keepalive (channel_a);
}
else
{
this->network.send_keepalive_self (channel_a);
}
});
observers.vote.add ([this](nano::transaction const & transaction, std::shared_ptr<nano::vote> vote_a, std::shared_ptr<nano::transport::channel> channel_a) {
this->gap_cache.vote (vote_a);
this->online_reps.observe (vote_a->account);
nano::uint128_t rep_weight;
nano::uint128_t min_rep_weight;
{
rep_weight = ledger.weight (transaction, vote_a->account);
min_rep_weight = online_reps.online_stake () / 1000;
}
if (rep_weight > min_rep_weight)
{
bool rep_crawler_exists (false);
for (auto hash : *vote_a)
{
if (this->rep_crawler.exists (hash))
{
rep_crawler_exists = true;
break;
}
}
if (rep_crawler_exists)
{
// We see a valid non-replay vote for a block we requested, this node is probably a representative
if (this->rep_crawler.response (channel_a, vote_a->account, rep_weight))
{
logger.try_log (boost::str (boost::format ("Found a representative at %1%") % channel_a->to_string ()));
// Rebroadcasting all active votes to new representative
auto blocks (this->active.list_blocks (true));
for (auto i (blocks.begin ()), n (blocks.end ()); i != n; ++i)
{
if (*i != nullptr)
{
nano::confirm_req req (*i);
channel_a->send (req);
}
}
}
}
}
});
if (this->websocket_server)
{
observers.vote.add ([this](nano::transaction const & transaction, std::shared_ptr<nano::vote> vote_a, std::shared_ptr<nano::transport::channel> channel_a) {
if (this->websocket_server->any_subscriber (nano::websocket::topic::vote))
{
nano::websocket::message_builder builder;
auto msg (builder.vote_received (vote_a));
this->websocket_server->broadcast (msg);
}
});
}
if (NANO_VERSION_PATCH == 0)
{
logger.always_log ("Node starting, version: ", NANO_MAJOR_MINOR_VERSION);
}
else
{
logger.always_log ("Node starting, version: ", NANO_MAJOR_MINOR_RC_VERSION);
}
auto network_label = network_params.network.get_current_network_as_string ();
logger.always_log ("Active network: ", network_label);
logger.always_log (boost::str (boost::format ("Work pool running %1% threads") % work.threads.size ()));
if (config.logging.node_lifetime_tracing ())
{
logger.always_log ("Constructing node");
}
logger.always_log (boost::str (boost::format ("Outbound Voting Bandwidth limited to %1% bytes per second") % config.bandwidth_limit));
// 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 transaction (store.tx_begin_read ());
is_initialized = (store.latest_begin (transaction) != store.latest_end ());
}
nano::genesis genesis;
if (!is_initialized)
{
auto transaction (store.tx_begin_write ());
// Store was empty meaning we just created it, add the genesis block
store.initialize (transaction, genesis);
}
auto transaction (store.tx_begin_read ());
if (!store.block_exists (transaction, genesis.hash ()))
{
logger.always_log ("Genesis block not found. Make sure the node network ID is correct.");
std::exit (1);
}
node_id = nano::keypair ();
logger.always_log ("Node ID: ", node_id.pub.to_account ());
const uint8_t * weight_buffer = network_params.network.is_live_network () ? nano_bootstrap_weights_live : nano_bootstrap_weights_beta;
size_t weight_size = network_params.network.is_live_network () ? nano_bootstrap_weights_live_size : nano_bootstrap_weights_beta_size;
if (network_params.network.is_live_network () || network_params.network.is_beta_network ())
{
nano::bufferstream weight_stream ((const uint8_t *)weight_buffer, weight_size);
nano::uint128_union block_height;
if (!nano::try_read (weight_stream, block_height))
{
auto max_blocks = (uint64_t)block_height.number ();
auto transaction (store.tx_begin_read ());
if (ledger.store.block_count (transaction).sum () < max_blocks)
{
ledger.bootstrap_weight_max_blocks = max_blocks;
while (true)
{
nano::account account;
if (nano::try_read (weight_stream, account.bytes))
{
break;
}
nano::amount weight;
if (nano::try_read (weight_stream, weight.bytes))
{
break;
}
logger.always_log ("Using bootstrap rep weight: ", account.to_account (), " -> ", weight.format_balance (Mxrb_ratio, 0, true), " XRB");
ledger.bootstrap_weights[account] = weight.number ();
}
}
}
}
}
node_initialized_latch.count_down ();
}
nano::node::~node ()
{
if (config.logging.node_lifetime_tracing ())
{
logger.always_log ("Destructing node");
}
stop ();
}
void nano::node::do_rpc_callback (boost::asio::ip::tcp::resolver::iterator i_a, std::string const & address, uint16_t port, std::shared_ptr<std::string> target, std::shared_ptr<std::string> body, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
{
if (i_a != boost::asio::ip::tcp::resolver::iterator{})
{
auto node_l (shared_from_this ());
auto sock (std::make_shared<boost::asio::ip::tcp::socket> (node_l->io_ctx));
sock->async_connect (i_a->endpoint (), [node_l, target, body, sock, address, port, i_a, resolver](boost::system::error_code const & ec) mutable {
if (!ec)
{
auto req (std::make_shared<boost::beast::http::request<boost::beast::http::string_body>> ());
req->method (boost::beast::http::verb::post);
req->target (*target);
req->version (11);
req->insert (boost::beast::http::field::host, address);
req->insert (boost::beast::http::field::content_type, "application/json");
req->body () = *body;
req->prepare_payload ();
boost::beast::http::async_write (*sock, *req, [node_l, sock, address, port, req, i_a, target, body, resolver](boost::system::error_code const & ec, size_t bytes_transferred) mutable {
if (!ec)
{
auto sb (std::make_shared<boost::beast::flat_buffer> ());
auto resp (std::make_shared<boost::beast::http::response<boost::beast::http::string_body>> ());
boost::beast::http::async_read (*sock, *sb, *resp, [node_l, sb, resp, sock, address, port, i_a, target, body, resolver](boost::system::error_code const & ec, size_t bytes_transferred) mutable {
if (!ec)
{
if (resp->result () == boost::beast::http::status::ok)
{
node_l->stats.inc (nano::stat::type::http_callback, nano::stat::detail::initiate, nano::stat::dir::out);
}
else
{
if (node_l->config.logging.callback_logging ())
{
node_l->logger.try_log (boost::str (boost::format ("Callback to %1%:%2% failed with status: %3%") % address % port % resp->result ()));
}
node_l->stats.inc (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out);
}
}
else
{
if (node_l->config.logging.callback_logging ())
{
node_l->logger.try_log (boost::str (boost::format ("Unable complete callback: %1%:%2%: %3%") % address % port % ec.message ()));
}
node_l->stats.inc (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out);
};
});
}
else
{
if (node_l->config.logging.callback_logging ())
{
node_l->logger.try_log (boost::str (boost::format ("Unable to send callback: %1%:%2%: %3%") % address % port % ec.message ()));
}
node_l->stats.inc (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out);
}
});
}
else
{
if (node_l->config.logging.callback_logging ())
{
node_l->logger.try_log (boost::str (boost::format ("Unable to connect to callback address: %1%:%2%: %3%") % address % port % ec.message ()));
}
node_l->stats.inc (nano::stat::type::error, nano::stat::detail::http_callback, nano::stat::dir::out);
++i_a;
node_l->do_rpc_callback (i_a, address, port, target, body, resolver);
}
});
}
}
bool nano::node::copy_with_compaction (boost::filesystem::path const & destination_file)
{
return !mdb_env_copy2 (boost::polymorphic_downcast<nano::mdb_store *> (store_impl.get ())->env.environment, destination_file.string ().c_str (), MDB_CP_COMPACT);
}
void nano::node::process_fork (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a)
{
auto root (block_a->root ());
if (!store.block_exists (transaction_a, block_a->type (), block_a->hash ()) && store.root_exists (transaction_a, block_a->root ()))
{
std::shared_ptr<nano::block> ledger_block (ledger.forked_block (transaction_a, *block_a));
if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ()))
{
std::weak_ptr<nano::node> this_w (shared_from_this ());
if (!active.start (ledger_block, [this_w, root](std::shared_ptr<nano::block>) {
if (auto this_l = this_w.lock ())
{
auto attempt (this_l->bootstrap_initiator.current_attempt ());
if (attempt && attempt->mode == nano::bootstrap_mode::legacy)
{
auto transaction (this_l->store.tx_begin_read ());
auto account (this_l->ledger.store.frontier_get (transaction, root));
if (!account.is_zero ())
{
attempt->requeue_pull (nano::pull_info (account, root, root));
}
else if (this_l->ledger.store.account_exists (transaction, root))
{
attempt->requeue_pull (nano::pull_info (root, nano::block_hash (0), nano::block_hash (0)));
}
}
}
}))
{
logger.always_log (boost::str (boost::format ("Resolving fork between our block: %1% and block %2% both with root %3%") % ledger_block->hash ().to_string () % block_a->hash ().to_string () % block_a->root ().to_string ()));
network.broadcast_confirm_req (ledger_block);
}
}
}
}
namespace nano
{
std::unique_ptr<seq_con_info_component> collect_seq_con_info (node & node, const std::string & name)
{
auto composite = std::make_unique<seq_con_info_composite> (name);
composite->add_component (collect_seq_con_info (node.alarm, "alarm"));
composite->add_component (collect_seq_con_info (node.work, "work"));
composite->add_component (collect_seq_con_info (node.gap_cache, "gap_cache"));
composite->add_component (collect_seq_con_info (node.ledger, "ledger"));
composite->add_component (collect_seq_con_info (node.active, "active"));
composite->add_component (collect_seq_con_info (node.bootstrap_initiator, "bootstrap_initiator"));
composite->add_component (collect_seq_con_info (node.bootstrap, "bootstrap"));
composite->add_component (node.network.tcp_channels.collect_seq_con_info ("tcp_channels"));
composite->add_component (node.network.udp_channels.collect_seq_con_info ("udp_channels"));
composite->add_component (node.network.response_channels.collect_seq_con_info ("response_channels"));
composite->add_component (node.network.syn_cookies.collect_seq_con_info ("syn_cookies"));
composite->add_component (collect_seq_con_info (node.observers, "observers"));
composite->add_component (collect_seq_con_info (node.wallets, "wallets"));
composite->add_component (collect_seq_con_info (node.vote_processor, "vote_processor"));
composite->add_component (collect_seq_con_info (node.rep_crawler, "rep_crawler"));
composite->add_component (collect_seq_con_info (node.block_processor, "block_processor"));
composite->add_component (collect_seq_con_info (node.block_arrival, "block_arrival"));
composite->add_component (collect_seq_con_info (node.online_reps, "online_reps"));
composite->add_component (collect_seq_con_info (node.votes_cache, "votes_cache"));
composite->add_component (collect_seq_con_info (node.block_uniquer, "block_uniquer"));
composite->add_component (collect_seq_con_info (node.vote_uniquer, "vote_uniquer"));
composite->add_component (collect_seq_con_info (node.confirmation_height_processor, "confirmation_height_processor"));
composite->add_component (collect_seq_con_info (node.pending_confirmation_height, "pending_confirmation_height"));
return composite;
}
}
void nano::node::process_active (std::shared_ptr<nano::block> incoming)
{
block_arrival.add (incoming->hash ());
block_processor.add (incoming, nano::seconds_since_epoch ());
}
nano::process_return nano::node::process (nano::block const & block_a)
{
auto transaction (store.tx_begin_write ());
auto result (ledger.process (transaction, block_a));
return result;
}
void nano::node::start ()
{
network.start ();
add_initial_peers ();
if (!flags.disable_legacy_bootstrap)
{
ongoing_bootstrap ();
}
else if (!flags.disable_unchecked_cleanup)
{
ongoing_unchecked_cleanup ();
}
ongoing_store_flush ();
rep_crawler.start ();
ongoing_rep_calculation ();
ongoing_peer_store ();
ongoing_online_weight_calculation_queue ();
if (config.tcp_incoming_connections_max > 0)
{
bootstrap.start ();
}
if (!flags.disable_backup)
{
backup_wallet ();
}
search_pending ();
if (!flags.disable_wallet_bootstrap)
{
// Delay to start wallet lazy bootstrap
auto this_l (shared ());
alarm.add (std::chrono::steady_clock::now () + std::chrono::minutes (1), [this_l]() {
this_l->bootstrap_wallet ();
});
}
if (config.external_address != boost::asio::ip::address_v6{} && config.external_port != 0)
{
port_mapping.start ();
}
}
void nano::node::stop ()
{
if (!stopped.exchange (true))
{
logger.always_log ("Node stopping");
block_processor.stop ();
if (block_processor_thread.joinable ())
{
block_processor_thread.join ();
}
vote_processor.stop ();
confirmation_height_processor.stop ();
active.stop ();
network.stop ();
if (websocket_server)
{
websocket_server->stop ();
}
bootstrap_initiator.stop ();
bootstrap.stop ();
port_mapping.stop ();
checker.stop ();
wallets.stop ();
stats.stop ();
write_database_queue.stop ();
}
}
void nano::node::keepalive_preconfigured (std::vector<std::string> const & peers_a)
{
for (auto i (peers_a.begin ()), n (peers_a.end ()); i != n; ++i)
{
keepalive (*i, network_params.network.default_node_port);
}
}
nano::block_hash nano::node::latest (nano::account const & account_a)
{
auto transaction (store.tx_begin_read ());
return ledger.latest (transaction, account_a);
}
nano::uint128_t nano::node::balance (nano::account const & account_a)
{
auto transaction (store.tx_begin_read ());
return ledger.account_balance (transaction, account_a);
}
std::shared_ptr<nano::block> nano::node::block (nano::block_hash const & hash_a)
{
auto transaction (store.tx_begin_read ());
return store.block_get (transaction, hash_a);
}
std::pair<nano::uint128_t, nano::uint128_t> nano::node::balance_pending (nano::account const & account_a)
{
std::pair<nano::uint128_t, nano::uint128_t> result;
auto transaction (store.tx_begin_read ());
result.first = ledger.account_balance (transaction, account_a);
result.second = ledger.account_pending (transaction, account_a);
return result;
}
nano::uint128_t nano::node::weight (nano::account const & account_a)
{
auto transaction (store.tx_begin_read ());
return ledger.weight (transaction, account_a);
}
nano::account nano::node::representative (nano::account const & account_a)
{
auto transaction (store.tx_begin_read ());
nano::account_info info;
nano::account result (0);
if (!store.account_get (transaction, account_a, info))
{
result = info.rep_block;
}
return result;
}
void nano::node::ongoing_rep_calculation ()
{
auto now (std::chrono::steady_clock::now ());
vote_processor.calculate_weights ();
std::weak_ptr<nano::node> node_w (shared_from_this ());
alarm.add (now + std::chrono::minutes (10), [node_w]() {
if (auto node_l = node_w.lock ())
{
node_l->ongoing_rep_calculation ();
}
});
}
void nano::node::ongoing_bootstrap ()
{
auto next_wakeup (300);
if (warmed_up < 3)
{
// Re-attempt bootstrapping more aggressively on startup
next_wakeup = 5;
if (!bootstrap_initiator.in_progress () && !network.empty ())
{
++warmed_up;
}
}
bootstrap_initiator.bootstrap ();
std::weak_ptr<nano::node> node_w (shared_from_this ());
alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (next_wakeup), [node_w]() {
if (auto node_l = node_w.lock ())
{
node_l->ongoing_bootstrap ();
}
});
}
void nano::node::ongoing_store_flush ()
{
{
auto transaction (store.tx_begin_write ());
store.flush (transaction);
}
std::weak_ptr<nano::node> node_w (shared_from_this ());
alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [node_w]() {
if (auto node_l = node_w.lock ())
{
node_l->ongoing_store_flush ();
}
});
}
void nano::node::ongoing_peer_store ()
{
bool stored (network.tcp_channels.store_all (true));
network.udp_channels.store_all (!stored);
std::weak_ptr<nano::node> node_w (shared_from_this ());
alarm.add (std::chrono::steady_clock::now () + network_params.node.peer_interval, [node_w]() {
if (auto node_l = node_w.lock ())
{
node_l->ongoing_peer_store ();
}
});
}
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");
boost::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 ());
alarm.add (std::chrono::steady_clock::now () + network_params.node.backup_interval, [this_l]() {
this_l->backup_wallet ();
});
}
void nano::node::search_pending ()
{
// Reload wallets from disk
wallets.reload ();
// Search pending
wallets.search_pending_all ();
auto this_l (shared ());
alarm.add (std::chrono::steady_clock::now () + network_params.node.search_pending_interval, [this_l]() {
this_l->search_pending ();
});
}
void nano::node::bootstrap_wallet ()
{
std::deque<nano::account> accounts;
{
std::lock_guard<std::mutex> lock (wallets.mutex);
auto transaction (wallets.tx_begin_read ());
for (auto i (wallets.items.begin ()), n (wallets.items.end ()); i != n && accounts.size () < 128; ++i)
{
auto & wallet (*i->second);
std::lock_guard<std::recursive_mutex> wallet_lock (wallet.store.mutex);
for (auto j (wallet.store.begin (transaction)), m (wallet.store.end ()); j != m && accounts.size () < 128; ++j)
{
nano::account account (j->first);
accounts.push_back (account);
}
}
}
bootstrap_initiator.bootstrap_wallet (accounts);
}
void nano::node::unchecked_cleanup ()
{
std::deque<nano::unchecked_key> cleaning_list;
// Collect old unchecked keys
{
auto now (nano::seconds_since_epoch ());
auto transaction (store.tx_begin_read ());
// Max 128k records to clean, max 2 minutes reading to prevent slow i/o systems start issues
for (auto i (store.unchecked_begin (transaction)), n (store.unchecked_end ()); i != n && cleaning_list.size () < 128 * 1024 && nano::seconds_since_epoch () - now < 120; ++i)
{
nano::unchecked_key const & key (i->first);
nano::unchecked_info const & info (i->second);
if ((now - info.modified) > static_cast<uint64_t> (config.unchecked_cutoff_time.count ()))
{
cleaning_list.push_back (key);
}
}
}
// Delete old unchecked keys in batches
while (!cleaning_list.empty ())
{
size_t deleted_count (0);
auto transaction (store.tx_begin_write ());
while (deleted_count++ < 2 * 1024 && !cleaning_list.empty ())
{
auto key (cleaning_list.front ());
cleaning_list.pop_front ();
store.unchecked_del (transaction, key);
}
}
}
void nano::node::ongoing_unchecked_cleanup ()
{
if (!bootstrap_initiator.in_progress ())
{
unchecked_cleanup ();
}
auto this_l (shared ());
alarm.add (std::chrono::steady_clock::now () + network_params.node.unchecked_cleaning_interval, [this_l]() {
this_l->ongoing_unchecked_cleanup ();
});
}
int nano::node::price (nano::uint128_t const & balance_a, int amount_a)
{
assert (balance_a >= amount_a * nano::Gxrb_ratio);
auto balance_l (balance_a);
double result (0.0);
for (auto i (0); i < amount_a; ++i)
{
balance_l -= nano::Gxrb_ratio;
auto balance_scaled ((balance_l / nano::Mxrb_ratio).convert_to<double> ());
auto units (balance_scaled / 1000.0);
auto unit_price (((free_cutoff - units) / free_cutoff) * price_max);
result += std::min (std::max (0.0, unit_price), price_max);
}
return static_cast<int> (result * 100.0);
}
namespace
{
class work_request
{
public:
work_request (boost::asio::io_context & io_ctx_a, boost::asio::ip::address address_a, uint16_t port_a) :
address (address_a),
port (port_a),
socket (io_ctx_a)
{
}
boost::asio::ip::address address;
uint16_t port;
boost::beast::flat_buffer buffer;
boost::beast::http::response<boost::beast::http::string_body> response;
boost::asio::ip::tcp::socket socket;
};
class distributed_work : public std::enable_shared_from_this<distributed_work>
{
public:
distributed_work (std::shared_ptr<nano::node> const & node_a, nano::block_hash const & root_a, std::function<void(uint64_t)> callback_a, uint64_t difficulty_a) :
distributed_work (1, node_a, root_a, callback_a, difficulty_a)
{
assert (node_a != nullptr);
}
distributed_work (unsigned int backoff_a, std::shared_ptr<nano::node> const & node_a, nano::block_hash const & root_a, std::function<void(uint64_t)> callback_a, uint64_t difficulty_a) :
callback (callback_a),
backoff (backoff_a),
node (node_a),
root (root_a),
need_resolve (node_a->config.work_peers),
difficulty (difficulty_a)
{
assert (node_a != nullptr);
completed.clear ();
}
void start ()
{
if (need_resolve.empty ())
{
start_work ();
}
else
{
auto current (need_resolve.back ());
need_resolve.pop_back ();
auto this_l (shared_from_this ());
boost::system::error_code ec;
auto parsed_address (boost::asio::ip::address_v6::from_string (current.first, ec));
if (!ec)
{
outstanding[parsed_address] = current.second;
start ();
}
else
{
node->network.resolver.async_resolve (boost::asio::ip::udp::resolver::query (current.first, std::to_string (current.second)), [current, this_l](boost::system::error_code const & ec, boost::asio::ip::udp::resolver::iterator i_a) {
if (!ec)
{
for (auto i (i_a), n (boost::asio::ip::udp::resolver::iterator{}); i != n; ++i)
{
auto endpoint (i->endpoint ());
this_l->outstanding[endpoint.address ()] = endpoint.port ();
}
}
else
{
this_l->node->logger.try_log (boost::str (boost::format ("Error resolving work peer: %1%:%2%: %3%") % current.first % current.second % ec.message ()));
}
this_l->start ();
});
}
}
}
void start_work ()
{
if (!outstanding.empty ())
{
auto this_l (shared_from_this ());
std::lock_guard<std::mutex> lock (mutex);
for (auto const & i : outstanding)
{
auto host (i.first);
auto service (i.second);
node->background ([this_l, host, service]() {
auto connection (std::make_shared<work_request> (this_l->node->io_ctx, host, service));
connection->socket.async_connect (nano::tcp_endpoint (host, service), [this_l, connection](boost::system::error_code const & ec) {
if (!ec)
{
std::string request_string;
{
boost::property_tree::ptree request;
request.put ("action", "work_generate");
request.put ("hash", this_l->root.to_string ());
request.put ("difficulty", nano::to_string_hex (this_l->difficulty));
std::stringstream ostream;
boost::property_tree::write_json (ostream, request);
request_string = ostream.str ();
}
auto request (std::make_shared<boost::beast::http::request<boost::beast::http::string_body>> ());
request->method (boost::beast::http::verb::post);
request->target ("/");
request->version (11);
request->body () = request_string;
request->prepare_payload ();
boost::beast::http::async_write (connection->socket, *request, [this_l, connection, request](boost::system::error_code const & ec, size_t bytes_transferred) {
if (!ec)
{
boost::beast::http::async_read (connection->socket, connection->buffer, connection->response, [this_l, connection](boost::system::error_code const & ec, size_t bytes_transferred) {
if (!ec)
{
if (connection->response.result () == boost::beast::http::status::ok)
{
this_l->success (connection->response.body (), connection->address);
}
else
{
this_l->node->logger.try_log (boost::str (boost::format ("Work peer responded with an error %1% %2%: %3%") % connection->address % connection->port % connection->response.result ()));
this_l->failure (connection->address);
}
}
else
{
this_l->node->logger.try_log (boost::str (boost::format ("Unable to read from work_peer %1% %2%: %3% (%4%)") % connection->address % connection->port % ec.message () % ec.value ()));
this_l->failure (connection->address);
}
});
}
else
{
this_l->node->logger.try_log (boost::str (boost::format ("Unable to write to work_peer %1% %2%: %3% (%4%)") % connection->address % connection->port % ec.message () % ec.value ()));
this_l->failure (connection->address);
}
});
}
else
{
this_l->node->logger.try_log (boost::str (boost::format ("Unable to connect to work_peer %1% %2%: %3% (%4%)") % connection->address % connection->port % ec.message () % ec.value ()));
this_l->failure (connection->address);
}
});
});
}
}
else
{
handle_failure (true);
}
}
void stop ()
{
auto this_l (shared_from_this ());
std::lock_guard<std::mutex> lock (mutex);
for (auto const & i : outstanding)
{
auto host (i.first);
node->background ([this_l, host]() {
std::string request_string;
{
boost::property_tree::ptree request;
request.put ("action", "work_cancel");
request.put ("hash", this_l->root.to_string ());
std::stringstream ostream;
boost::property_tree::write_json (ostream, request);
request_string = ostream.str ();
}
boost::beast::http::request<boost::beast::http::string_body> request;
request.method (boost::beast::http::verb::post);
request.target ("/");
request.version (11);
request.body () = request_string;
request.prepare_payload ();
auto socket (std::make_shared<boost::asio::ip::tcp::socket> (this_l->node->io_ctx));
boost::beast::http::async_write (*socket, request, [socket](boost::system::error_code const & ec, size_t bytes_transferred) {
});
});
}
outstanding.clear ();
}
void success (std::string const & body_a, boost::asio::ip::address const & address)
{
auto last (remove (address));
std::stringstream istream (body_a);
try
{
boost::property_tree::ptree result;
boost::property_tree::read_json (istream, result);
auto work_text (result.get<std::string> ("work"));
uint64_t work;
if (!nano::from_string_hex (work_text, work))
{
uint64_t result_difficulty (0);
if (!nano::work_validate (root, work, &result_difficulty) && result_difficulty >= difficulty)
{
set_once (work);
stop ();
}
else
{
node->logger.try_log (boost::str (boost::format ("Incorrect work response from %1% for root %2% with diffuculty %3%: %4%") % address % root.to_string () % nano::to_string_hex (difficulty) % work_text));
handle_failure (last);
}
}
else
{
node->logger.try_log (boost::str (boost::format ("Work response from %1% wasn't a number: %2%") % address % work_text));
handle_failure (last);
}
}
catch (...)
{
node->logger.try_log (boost::str (boost::format ("Work response from %1% wasn't parsable: %2%") % address % body_a));
handle_failure (last);
}
}
void set_once (uint64_t work_a)
{
if (!completed.test_and_set ())
{
callback (work_a);
}
}
void failure (boost::asio::ip::address const & address)
{
auto last (remove (address));
handle_failure (last);
}
void handle_failure (bool last)
{
if (last)
{
if (!completed.test_and_set ())
{
if (node->config.work_threads != 0 || node->work.opencl)
{
auto callback_l (callback);
// clang-format off
node->work.generate (root, [callback_l](boost::optional<uint64_t> const & work_a) {
callback_l (work_a.value ());
},
difficulty);
// clang-format on
}
else
{
if (backoff == 1 && node->config.logging.work_generation_time ())
{
node->logger.try_log ("Work peer(s) failed to generate work for root ", root.to_string (), ", retrying...");
}
auto now (std::chrono::steady_clock::now ());
auto root_l (root);
auto callback_l (callback);
std::weak_ptr<nano::node> node_w (node);
auto next_backoff (std::min (backoff * 2, (unsigned int)60 * 5));
// clang-format off
node->alarm.add (now + std::chrono::seconds (backoff), [ node_w, root_l, callback_l, next_backoff, difficulty = difficulty ] {
if (auto node_l = node_w.lock ())
{
auto work_generation (std::make_shared<distributed_work> (next_backoff, node_l, root_l, callback_l, difficulty));
work_generation->start ();
}
});
// clang-format on
}
}
}
}
bool remove (boost::asio::ip::address const & address)
{
std::lock_guard<std::mutex> lock (mutex);
outstanding.erase (address);
return outstanding.empty ();
}
std::function<void(uint64_t)> callback;
unsigned int backoff; // in seconds
std::shared_ptr<nano::node> node;
nano::block_hash root;
std::mutex mutex;
std::map<boost::asio::ip::address, uint16_t> outstanding;
std::vector<std::pair<std::string, uint16_t>> need_resolve;
std::atomic_flag completed;
uint64_t difficulty;
};
}
void nano::node::work_generate_blocking (nano::block & block_a)
{
work_generate_blocking (block_a, network_params.network.publish_threshold);
}
void nano::node::work_generate_blocking (nano::block & block_a, uint64_t difficulty_a)
{
block_a.block_work_set (work_generate_blocking (block_a.root (), difficulty_a));
}
void nano::node::work_generate (nano::uint256_union const & hash_a, std::function<void(uint64_t)> callback_a)
{
work_generate (hash_a, callback_a, network_params.network.publish_threshold);
}
void nano::node::work_generate (nano::uint256_union const & hash_a, std::function<void(uint64_t)> callback_a, uint64_t difficulty_a)
{
auto work_generation (std::make_shared<distributed_work> (shared (), hash_a, callback_a, difficulty_a));
work_generation->start ();
}
uint64_t nano::node::work_generate_blocking (nano::uint256_union const & block_a)
{
return work_generate_blocking (block_a, network_params.network.publish_threshold);
}
uint64_t nano::node::work_generate_blocking (nano::uint256_union const & hash_a, uint64_t difficulty_a)
{
std::promise<uint64_t> promise;
std::future<uint64_t> future = promise.get_future ();
// clang-format off
work_generate (hash_a, [&promise](uint64_t work_a) {
promise.set_value (work_a);
},
difficulty_a);
// clang-format on
return future.get ();
}
void nano::node::add_initial_peers ()
{
auto transaction (store.tx_begin_read ());
for (auto i (store.peers_begin (transaction)), n (store.peers_end ()); i != n; ++i)
{
nano::endpoint endpoint (boost::asio::ip::address_v6 (i->first.address_bytes ()), i->first.port ());
if (!network.reachout (endpoint, config.allow_local_peers))
{
std::weak_ptr<nano::node> node_w (shared_from_this ());
network.tcp_channels.start_tcp (endpoint, [node_w](std::shared_ptr<nano::transport::channel> channel_a) {
if (auto node_l = node_w.lock ())
{
node_l->network.send_keepalive (channel_a);
node_l->rep_crawler.query (channel_a);
}
});
}
}
}
void nano::node::block_confirm (std::shared_ptr<nano::block> block_a)
{
active.start (block_a);
network.broadcast_confirm_req (block_a);
// Calculate votes for local representatives
if (config.enable_voting && active.active (*block_a))
{
block_processor.generator.add (block_a->hash ());
}
}
bool nano::node::block_confirmed_or_being_confirmed (nano::transaction const & transaction_a, nano::block_hash const & hash_a)
{
return ledger.block_confirmed (transaction_a, hash_a) || pending_confirmation_height.is_processing_block (hash_a);
}
nano::uint128_t nano::node::delta () const
{
auto result ((online_reps.online_stake () / 100) * config.online_weight_quorum);
return result;
}
void nano::node::ongoing_online_weight_calculation_queue ()
{
std::weak_ptr<nano::node> node_w (shared_from_this ());
alarm.add (std::chrono::steady_clock::now () + (std::chrono::seconds (network_params.node.weight_period)), [node_w]() {
if (auto node_l = node_w.lock ())
{
node_l->ongoing_online_weight_calculation ();
}
});
}
bool nano::node::online () const
{
return rep_crawler.total_weight () > (std::max (config.online_weight_minimum.number (), delta ()));
}
void nano::node::ongoing_online_weight_calculation ()
{
online_reps.sample ();
ongoing_online_weight_calculation_queue ();
}
namespace
{
class confirmed_visitor : public nano::block_visitor
{
public:
confirmed_visitor (nano::transaction const & transaction_a, nano::node & node_a, std::shared_ptr<nano::block> block_a, nano::block_hash const & hash_a) :
transaction (transaction_a),
node (node_a),
block (block_a),
hash (hash_a)
{
}
virtual ~confirmed_visitor () = default;
void scan_receivable (nano::account const & account_a)
{
for (auto i (node.wallets.items.begin ()), n (node.wallets.items.end ()); i != n; ++i)
{
auto const & wallet (i->second);
auto transaction_l (node.wallets.tx_begin_read ());
if (wallet->store.exists (transaction_l, account_a))
{
nano::account representative;
nano::pending_info pending;
representative = wallet->store.representative (transaction_l);
auto error (node.store.pending_get (transaction, nano::pending_key (account_a, hash), pending));
if (!error)
{
auto node_l (node.shared ());
auto amount (pending.amount.number ());
wallet->receive_async (block, representative, amount, [](std::shared_ptr<nano::block>) {});
}
else
{
if (!node.store.block_exists (transaction, hash))
{
node.logger.try_log (boost::str (boost::format ("Confirmed block is missing: %1%") % hash.to_string ()));
assert (false && "Confirmed block is missing");
}
else
{
node.logger.try_log (boost::str (boost::format ("Block %1% has already been received") % hash.to_string ()));
}
}
}
}
}
void state_block (nano::state_block const & block_a) override
{
scan_receivable (block_a.hashables.link);
}
void send_block (nano::send_block const & block_a) override
{
scan_receivable (block_a.hashables.destination);
}
void receive_block (nano::receive_block const &) override
{
}
void open_block (nano::open_block const &) override
{
}
void change_block (nano::change_block const &) override
{
}
nano::transaction const & transaction;
nano::node & node;
std::shared_ptr<nano::block> block;
nano::block_hash const & hash;
};
}
void nano::node::receive_confirmed (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a, nano::block_hash const & hash_a)
{
confirmed_visitor visitor (transaction_a, *this, block_a, hash_a);
block_a->visit (visitor);
}
void nano::node::process_confirmed_data (nano::transaction const & transaction_a, std::shared_ptr<nano::block> block_a, nano::block_hash const & hash_a, nano::block_sideband const & sideband_a, nano::account & account_a, nano::uint128_t & amount_a, bool & is_state_send_a, nano::account & pending_account_a)
{
// Faster account calculation
account_a = block_a->account ();
if (account_a.is_zero ())
{
account_a = sideband_a.account;
}
// Faster amount calculation
auto previous (block_a->previous ());
auto previous_balance (ledger.balance (transaction_a, previous));
auto block_balance (store.block_balance_calculated (block_a, sideband_a));
if (hash_a != ledger.network_params.ledger.genesis_account)
{
amount_a = block_balance > previous_balance ? block_balance - previous_balance : previous_balance - block_balance;
}
else
{
amount_a = ledger.network_params.ledger.genesis_amount;
}
if (auto state = dynamic_cast<nano::state_block *> (block_a.get ()))
{
if (state->hashables.balance < previous_balance)
{
is_state_send_a = true;
}
pending_account_a = state->hashables.link;
}
if (auto send = dynamic_cast<nano::send_block *> (block_a.get ()))
{
pending_account_a = send->hashables.destination;
}
}
void nano::node::process_confirmed (nano::election_status const & status_a, uint8_t iteration)
{
auto block_a (status_a.winner);
auto hash (block_a->hash ());
nano::block_sideband sideband;
auto transaction (store.tx_begin_read ());
if (store.block_get (transaction, hash, &sideband) != nullptr)
{
confirmation_height_processor.add (hash);
receive_confirmed (transaction, block_a, hash);
nano::account account (0);
nano::uint128_t amount (0);
bool is_state_send (false);
nano::account pending_account (0);
process_confirmed_data (transaction, block_a, hash, sideband, account, amount, is_state_send, pending_account);
observers.blocks.notify (status_a, account, amount, is_state_send);
if (amount > 0)
{
observers.account_balance.notify (account, false);
if (!pending_account.is_zero ())
{
observers.account_balance.notify (pending_account, true);
}
}
}
// Limit to 0.5 * 20 = 10 seconds (more than max block_processor::process_batch finish time)
else if (iteration < 20)
{
iteration++;
std::weak_ptr<nano::node> node_w (shared ());
alarm.add (std::chrono::steady_clock::now () + network_params.node.process_confirmed_interval, [node_w, status_a, iteration]() {
if (auto node_l = node_w.lock ())
{
node_l->process_confirmed (status_a, iteration);
}
});
}
}
bool nano::block_arrival::add (nano::block_hash const & hash_a)
{
std::lock_guard<std::mutex> lock (mutex);
auto now (std::chrono::steady_clock::now ());
auto inserted (arrival.insert (nano::block_arrival_info{ now, hash_a }));
auto result (!inserted.second);
return result;
}
bool nano::block_arrival::recent (nano::block_hash const & hash_a)
{
std::lock_guard<std::mutex> lock (mutex);
auto now (std::chrono::steady_clock::now ());
while (arrival.size () > arrival_size_min && arrival.begin ()->arrival + arrival_time_min < now)
{
arrival.erase (arrival.begin ());
}
return arrival.get<1> ().find (hash_a) != arrival.get<1> ().end ();
}
namespace nano
{
std::unique_ptr<seq_con_info_component> collect_seq_con_info (block_arrival & block_arrival, const std::string & name)
{
size_t count = 0;
{
std::lock_guard<std::mutex> guard (block_arrival.mutex);
count = block_arrival.arrival.size ();
}
auto sizeof_element = sizeof (decltype (block_arrival.arrival)::value_type);
auto composite = std::make_unique<seq_con_info_composite> (name);
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "arrival", count, sizeof_element }));
return composite;
}
}
std::shared_ptr<nano::node> nano::node::shared ()
{
return shared_from_this ();
}
bool nano::node::validate_block_by_previous (nano::transaction const & transaction, std::shared_ptr<nano::block> block_a)
{
bool result (false);
nano::account account;
if (!block_a->previous ().is_zero ())
{
if (store.block_exists (transaction, block_a->previous ()))
{
account = ledger.account (transaction, block_a->previous ());
}
else
{
result = true;
}
}
else
{
account = block_a->root ();
}
if (!result && block_a->type () == nano::block_type::state)
{
std::shared_ptr<nano::state_block> block_l (std::static_pointer_cast<nano::state_block> (block_a));
nano::amount prev_balance (0);
if (!block_l->hashables.previous.is_zero ())
{
if (store.block_exists (transaction, block_l->hashables.previous))
{
prev_balance = ledger.balance (transaction, block_l->hashables.previous);
}
else
{
result = true;
}
}
if (!result)
{
if (block_l->hashables.balance == prev_balance && !ledger.epoch_link.is_zero () && ledger.is_epoch_link (block_l->hashables.link))
{
account = ledger.epoch_signer;
}
}
}
if (!result && (account.is_zero () || nano::validate_message (account, block_a->hash (), block_a->block_signature ())))
{
result = true;
}
return result;
}
int nano::node::store_version ()
{
auto transaction (store.tx_begin_read ());
return store.version_get (transaction);
}
nano::inactive_node::inactive_node (boost::filesystem::path const & path_a, uint16_t peering_port_a) :
path (path_a),
io_context (std::make_shared<boost::asio::io_context> ()),
alarm (*io_context),
work (1),
peering_port (peering_port_a)
{
boost::system::error_code error_chmod;
/*
* @warning May throw a filesystem exception
*/
boost::filesystem::create_directories (path);
nano::set_secure_perm_directory (path, error_chmod);
logging.max_size = std::numeric_limits<std::uintmax_t>::max ();
logging.init (path);
node = std::make_shared<nano::node> (init, *io_context, peering_port, path, alarm, logging, work);
node->active.stop ();
}
nano::inactive_node::~inactive_node ()
{
node->stop ();
}