From d4a4299bebc30502e987fe376b6c2469294ea20a Mon Sep 17 00:00:00 2001 From: clemahieu Date: Mon, 11 Apr 2022 18:42:22 +0100 Subject: [PATCH 1/7] Bound the unchecked store after the initial bootstrap amount has been reached. This prevents the unchecked table size from increasing beyond reasonable bounds when it's in sync. --- nano/node/blockprocessor.cpp | 12 ++++++++++++ nano/node/blockprocessor.hpp | 1 + nano/secure/ledger.cpp | 5 +++++ nano/secure/ledger.hpp | 1 + 4 files changed, 19 insertions(+) diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 93756feb3b..26aea5071e 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -383,6 +383,10 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction } nano::unchecked_key unchecked_key (block->previous (), hash); + if (node.ledger.bootstrap_weight_reached () && node.store.unchecked.count (transaction_a) > max) + { + node.store.unchecked.clear (transaction_a); + } node.store.unchecked.put (transaction_a, unchecked_key, info_a); events_a.events.emplace_back ([this, hash] (nano::transaction const & /* unused */) { this->node.gap_cache.add (hash); }); @@ -403,6 +407,10 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction } nano::unchecked_key unchecked_key (node.ledger.block_source (transaction_a, *(block)), hash); + if (node.ledger.bootstrap_weight_reached () && node.store.unchecked.count (transaction_a) > max) + { + node.store.unchecked.clear (transaction_a); + } node.store.unchecked.put (transaction_a, unchecked_key, info_a); events_a.events.emplace_back ([this, hash] (nano::transaction const & /* unused */) { this->node.gap_cache.add (hash); }); @@ -423,6 +431,10 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction } nano::unchecked_key unchecked_key (block->account (), hash); // Specific unchecked key starting with epoch open block account public key + if (node.ledger.bootstrap_weight_reached () && node.store.unchecked.count (transaction_a) > max) + { + node.store.unchecked.clear (transaction_a); + } node.store.unchecked.put (transaction_a, unchecked_key, info_a); node.stats.inc (nano::stat::type::ledger, nano::stat::detail::gap_source); diff --git a/nano/node/blockprocessor.hpp b/nano/node/blockprocessor.hpp index b3f2ace522..d81815d7a6 100644 --- a/nano/node/blockprocessor.hpp +++ b/nano/node/blockprocessor.hpp @@ -87,6 +87,7 @@ class block_processor final nano::mutex mutex{ mutex_identifier (mutexes::block_processor) }; nano::state_block_signature_verification state_block_signature_verification; std::thread processing_thread; + static uint64_t constexpr max = 256 * 1024; friend std::unique_ptr collect_container_info (block_processor & block_processor, std::string const & name); }; diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 8d96206ccc..5850e6af1c 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -1546,6 +1546,11 @@ bool nano::ledger::migrate_lmdb_to_rocksdb (boost::filesystem::path const & data return error; } +bool nano::ledger::bootstrap_weight_reached () const +{ + return cache.block_count >= bootstrap_weight_max_blocks; +} + nano::uncemented_info::uncemented_info (nano::block_hash const & cemented_frontier, nano::block_hash const & frontier, nano::account const & account) : cemented_frontier (cemented_frontier), frontier (frontier), account (account) { diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index a27ee1be97..7987296dac 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -67,6 +67,7 @@ class ledger final nano::link const & epoch_link (nano::epoch) const; std::multimap> unconfirmed_frontiers () const; bool migrate_lmdb_to_rocksdb (boost::filesystem::path const &) const; + bool bootstrap_weight_reached () const; static nano::uint128_t const unit; nano::ledger_constants & constants; nano::store & store; From 5164933ea64b7be2c542a45bc208fe229c36e622 Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Tue, 26 Apr 2022 03:42:16 +0100 Subject: [PATCH 2/7] If multiple handshake queries are received then close the socket --- nano/node/bootstrap/bootstrap_server.cpp | 14 ++++++++++++++ nano/node/bootstrap/bootstrap_server.hpp | 1 + 2 files changed, 15 insertions(+) diff --git a/nano/node/bootstrap/bootstrap_server.cpp b/nano/node/bootstrap/bootstrap_server.cpp index 35a5d233ea..9c898589c4 100644 --- a/nano/node/bootstrap/bootstrap_server.cpp +++ b/nano/node/bootstrap/bootstrap_server.cpp @@ -662,10 +662,24 @@ class request_response_visitor : public nano::message_visitor } void node_id_handshake (nano::node_id_handshake const & message_a) override { + // check for multiple handshake messages, there is no reason to receive more than one + if (message_a.query && connection->handshake_query_received) + { + if (connection->node->config.logging.network_node_id_handshake_logging ()) + { + connection->node->logger.try_log (boost::str (boost::format ("Detected multiple node_id_handshake query from %1%") % connection->remote_endpoint)); + } + connection->stop (); + return; + } + + connection->handshake_query_received = true; + if (connection->node->config.logging.network_node_id_handshake_logging ()) { connection->node->logger.try_log (boost::str (boost::format ("Received node_id_handshake message from %1%") % connection->remote_endpoint)); } + if (message_a.query) { boost::optional> response (std::make_pair (connection->node->node_id.pub, nano::sign_message (connection->node->node_id.prv, connection->node->node_id.pub, *message_a.query))); diff --git a/nano/node/bootstrap/bootstrap_server.hpp b/nano/node/bootstrap/bootstrap_server.hpp index 30fd073f0e..d91c2d4422 100644 --- a/nano/node/bootstrap/bootstrap_server.hpp +++ b/nano/node/bootstrap/bootstrap_server.hpp @@ -64,6 +64,7 @@ class bootstrap_server final : public std::enable_shared_from_this> requests; std::atomic stopped{ false }; + std::atomic handshake_query_received{ false }; // Remote enpoint used to remove response channel even after socket closing nano::tcp_endpoint remote_endpoint{ boost::asio::ip::address_v6::any (), 0 }; nano::account remote_node_id{}; From e355eda2b8075bda0bc3aa0952acffb171418ec6 Mon Sep 17 00:00:00 2001 From: Dimitrios Siganos Date: Fri, 13 May 2022 01:36:00 +0100 Subject: [PATCH 3/7] Node ID persistence (#3816) * Node ID persistence After this change, node IDs will persist on the disk file system and reused. The node ID will not be changing after every restart. This will help to more reliably track PRs. The node can be forced to generate a new ID by deleting the file: node_id_private.key Note that this change does not prevent the man in the middle attack. However, it makes it possible to detect telemetry spoofing. Sites like nanolooker and nanoticker can maintain associations between node ID and PR and only accept a telemetry packet, if the telemetry message is signed by the known Node ID. Co-authored-by: clemahieu # Conflicts: # nano/core_test/node.cpp --- nano/core_test/node.cpp | 28 ++++++++++++++++++++++++++++ nano/node/node.cpp | 30 +++++++++++++++++++++++++++++- nano/node/node.hpp | 1 + 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 6b13aca79e..0548d9f48d 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -11,6 +11,7 @@ #include #include +#include #include using namespace std::chrono_literals; @@ -4836,6 +4837,33 @@ TEST (node, pruning_depth) ASSERT_TRUE (node1.ledger.block_or_pruned_exists (send2->hash ())); } +TEST (node_config, node_id_private_key_persistence) +{ + nano::logger_mt logger; + + // create the directory and the file + auto path = nano::unique_path (); + ASSERT_TRUE (boost::filesystem::create_directories (path)); + auto priv_key_filename = path / "node_id_private.key"; + + // check that the key generated is random when the key does not exist + nano::keypair kp1 = nano::load_or_create_node_id (path, logger); + boost::filesystem::remove (priv_key_filename); + nano::keypair kp2 = nano::load_or_create_node_id (path, logger); + ASSERT_NE (kp1.prv, kp2.prv); + + // check that the key persists + nano::keypair kp3 = nano::load_or_create_node_id (path, logger); + ASSERT_EQ (kp2.prv, kp3.prv); + + // write the key file manually and check that right key is loaded + std::ofstream ofs (priv_key_filename.string (), std::ofstream::out | std::ofstream::trunc); + ofs << "3F28D035B8AA75EA53DF753BFD065CF6138E742971B2C99B84FD8FE328FED2D9" << std::flush; + ofs.close (); + nano::keypair kp4 = nano::load_or_create_node_id (path, logger); + ASSERT_EQ (kp4.prv, nano::keypair ("3F28D035B8AA75EA53DF753BFD065CF6138E742971B2C99B84FD8FE328FED2D9").prv); +} + namespace { void add_required_children_node_config_tree (nano::jsonconfig & tree) diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 5ce53efe7f..c252e9113a 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -74,6 +75,33 @@ std::unique_ptr nano::collect_container_info (re return composite; } +nano::keypair nano::load_or_create_node_id (boost::filesystem::path const & application_path, nano::logger_mt & logger) +{ + auto node_private_key_path = application_path / "node_id_private.key"; + std::ifstream ifs (node_private_key_path.c_str ()); + if (ifs.good ()) + { + logger.always_log (boost::str (boost::format ("%1% exists, reading node id from it") % node_private_key_path.string ())); + std::string node_private_key; + ifs >> node_private_key; + release_assert (node_private_key.size () == 64); + nano::keypair kp = nano::keypair (node_private_key); + return kp; + } + else + { + // no node_id found, generate new one + logger.always_log (boost::str (boost::format ("%1% does not exist, creating a new node_id") % node_private_key_path.string ())); + 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 ()); + return kp; + } +} + nano::node::node (boost::asio::io_context & io_ctx_a, uint16_t peering_port_a, boost::filesystem::path const & application_path_a, nano::logging const & logging_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, logging_a), work_a, flags_a, seq) { @@ -381,7 +409,7 @@ nano::node::node (boost::asio::io_context & io_ctx_a, boost::filesystem::path co logger.always_log (stream.str ()); } - node_id = nano::keypair (); + node_id = nano::load_or_create_node_id (application_path, logger); logger.always_log ("Node ID: ", node_id.pub.to_node_id ()); if ((network_params.network.is_live_network () || network_params.network.is_beta_network ()) && !flags.inactive_node) diff --git a/nano/node/node.hpp b/nano/node/node.hpp index a188940d43..7bc3599aed 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -213,6 +213,7 @@ class node final : public std::enable_shared_from_this nano::locked> epoch_upgrading; }; +nano::keypair load_or_create_node_id (boost::filesystem::path const & application_path, nano::logger_mt & logger); std::unique_ptr collect_container_info (node & node, std::string const & name); nano::node_flags const & inactive_node_flag_defaults (); From 3da48f4b4f9468d4d2f3a45e3748118cb80d900c Mon Sep 17 00:00:00 2001 From: clemahieu Date: Sun, 15 May 2022 00:56:50 +0100 Subject: [PATCH 4/7] Only allow 2 entries to have the same dependency. --- nano/secure/store/unchecked_store_partial.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nano/secure/store/unchecked_store_partial.hpp b/nano/secure/store/unchecked_store_partial.hpp index 845135172d..299feec8b4 100644 --- a/nano/secure/store/unchecked_store_partial.hpp +++ b/nano/secure/store/unchecked_store_partial.hpp @@ -36,6 +36,8 @@ class unchecked_store_partial : public unchecked_store void put (nano::write_transaction const & transaction_a, nano::unchecked_key const & key_a, nano::unchecked_info const & info_a) override { + if (get (transaction_a, key_a.previous).size () > 1) + return; nano::db_val info (info_a); auto status (store.put (transaction_a, tables::unchecked, key_a, info)); release_assert_success (store, status); From 2a77129ed129225f96b4a2cb96d073c1b8f888ac Mon Sep 17 00:00:00 2001 From: clemahieu Date: Wed, 18 May 2022 13:33:10 +0100 Subject: [PATCH 5/7] Lower log level of message rejecting transaction from burn account. --- nano/node/blockprocessor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 26aea5071e..6a2fece75e 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -486,7 +486,10 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction } case nano::process_result::opened_burn_account: { - node.logger.always_log (boost::str (boost::format ("*** Rejecting open block for burn account ***: %1%") % hash.to_string ())); + if (node.config.logging.ledger_logging ()) + { + node.logger.try_log (boost::str (boost::format ("Rejecting open block for burn account: %1%") % hash.to_string ())); + } break; } case nano::process_result::balance_mismatch: From d39088df0d463641147b24ed48d756b8ca79bfc2 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Thu, 19 May 2022 14:34:09 +0100 Subject: [PATCH 6/7] Disable vote_processor flushing within the request loop which can block under heavy vote load. --- nano/node/active_transactions.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 38391e4fb1..745ec1b0de 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -571,14 +571,6 @@ void nano::active_transactions::request_loop () while (!stopped && !node.flags.disable_request_loop) { - // If many votes are queued, ensure at least the currently active ones finish processing - lock.unlock (); - if (node.vote_processor.half_full ()) - { - node.vote_processor.flush_active (); - } - lock.lock (); - auto const stamp_l = std::chrono::steady_clock::now (); request_confirm (lock); From 0c5c2b1444a46b0172cc96e3d37a4ad7bee13cd9 Mon Sep 17 00:00:00 2001 From: clemahieu Date: Thu, 19 May 2022 14:34:33 +0100 Subject: [PATCH 7/7] Set inactive votes cache size to 0 as it causes excessive vote relaying. --- nano/node/active_transactions.cpp | 5 +++++ nano/node/nodeconfig.hpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 745ec1b0de..2163121921 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -1118,6 +1118,11 @@ std::size_t nano::active_transactions::inactive_votes_cache_size () void nano::active_transactions::add_inactive_votes_cache (nano::unique_lock & lock_a, nano::block_hash const & hash_a, nano::account const & representative_a, uint64_t const timestamp_a) { + if (node.flags.inactive_votes_cache_size == 0) + { + return; + } + // Check principal representative status if (node.ledger.weight (representative_a) > node.minimum_principal_weight ()) { diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index 54a219906b..1c6fbad2bb 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -157,7 +157,7 @@ class node_flags final std::size_t block_processor_batch_size{ 0 }; std::size_t block_processor_full_size{ 65536 }; std::size_t block_processor_verification_size{ 0 }; - std::size_t inactive_votes_cache_size{ 16 * 1024 }; + std::size_t inactive_votes_cache_size{ 0 }; std::size_t vote_processor_capacity{ 144 * 1024 }; std::size_t bootstrap_interval{ 0 }; // For testing only };