From 84c7248aa1f27d20e08dc6d8e750d7d5ef62c9d4 Mon Sep 17 00:00:00 2001 From: Sergey Kroshnin Date: Tue, 18 Aug 2020 17:52:29 +0300 Subject: [PATCH] Epoch 1 legacy blocks work validation before processing (#2850) * Epoch 1 legacy blocks work validation before processing Legacy blocks are always epoch 0, cannot use lower epoch_2_receive difficulty * Move vote block work validation to TCP realtime server * Required header * Better logging for bulk pull/push invalid work * Update new test with dev network --- nano/core_test/difficulty.cpp | 6 +++- nano/core_test/network.cpp | 37 ++++++++++++++++++++- nano/lib/work.cpp | 26 ++++++++++----- nano/lib/work.hpp | 3 +- nano/node/bootstrap/bootstrap_bulk_pull.cpp | 10 +++++- nano/node/bootstrap/bootstrap_bulk_push.cpp | 10 +++++- nano/node/bootstrap/bootstrap_server.cpp | 31 +++++++++++++++-- nano/node/json_handler.cpp | 2 +- nano/node/network.cpp | 21 +++++++----- 9 files changed, 120 insertions(+), 26 deletions(-) diff --git a/nano/core_test/difficulty.cpp b/nano/core_test/difficulty.cpp index b655af384..1f9e8e2eb 100644 --- a/nano/core_test/difficulty.cpp +++ b/nano/core_test/difficulty.cpp @@ -147,7 +147,11 @@ TEST (difficulty, network_constants) nano::work_version version{ nano::work_version::work_1 }; ASSERT_EQ (constants.publish_thresholds.base, constants.publish_thresholds.epoch_2); ASSERT_EQ (constants.publish_thresholds.base, nano::work_threshold_base (version)); - ASSERT_EQ (constants.publish_thresholds.entry, nano::work_threshold_entry (version)); + ASSERT_EQ (constants.publish_thresholds.entry, nano::work_threshold_entry (version, nano::block_type::state)); + ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold_entry (version, nano::block_type::send)); + ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold_entry (version, nano::block_type::receive)); + ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold_entry (version, nano::block_type::open)); + ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold_entry (version, nano::block_type::change)); ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_0, false, false, false))); ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_1, false, false, false))); ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_1, false, false, false))); diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index e02057af3..22bd35694 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -289,7 +289,7 @@ TEST (network, send_valid_publish) } } -TEST (network, send_insufficient_work) +TEST (network, send_insufficient_work_udp) { nano::system system; nano::node_flags node_flags; @@ -305,6 +305,41 @@ TEST (network, send_insufficient_work) ASSERT_EQ (1, node2.stats.count (nano::stat::type::error, nano::stat::detail::insufficient_work)); } +TEST (network, send_insufficient_work) +{ + nano::system system (2); + auto & node1 = *system.nodes[0]; + auto & node2 = *system.nodes[1]; + // Block zero work + auto block1 (std::make_shared (0, 1, 20, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); + nano::publish publish1 (block1); + auto tcp_channel (node1.network.tcp_channels.find_channel (nano::transport::map_endpoint_to_tcp (node2.network.endpoint ()))); + tcp_channel->send (publish1, [](boost::system::error_code const & ec, size_t size) {}); + ASSERT_EQ (0, node1.stats.count (nano::stat::type::error, nano::stat::detail::insufficient_work)); + ASSERT_TIMELY (10s, node2.stats.count (nano::stat::type::error, nano::stat::detail::insufficient_work) != 0); + ASSERT_EQ (1, node2.stats.count (nano::stat::type::error, nano::stat::detail::insufficient_work)); + // Legacy block work between epoch_2_recieve & epoch_1 + auto block2 (std::make_shared (block1->hash (), 1, 20, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, system.work_generate_limited (block1->hash (), node1.network_params.network.publish_thresholds.epoch_2_receive, node1.network_params.network.publish_thresholds.epoch_1 - 1))); + nano::publish publish2 (block2); + tcp_channel->send (publish2, [](boost::system::error_code const & ec, size_t size) {}); + ASSERT_TIMELY (10s, node2.stats.count (nano::stat::type::error, nano::stat::detail::insufficient_work) != 1); + ASSERT_EQ (2, node2.stats.count (nano::stat::type::error, nano::stat::detail::insufficient_work)); + // Legacy block work epoch_1 + auto block3 (std::make_shared (block2->hash (), 1, 20, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (block2->hash (), node1.network_params.network.publish_thresholds.epoch_2))); + nano::publish publish3 (block3); + tcp_channel->send (publish3, [](boost::system::error_code const & ec, size_t size) {}); + ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in)); + ASSERT_TIMELY (10s, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in) != 0); + ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in)); + // State block work epoch_2_recieve + auto block4 (std::make_shared (nano::dev_genesis_key.pub, block1->hash (), nano::dev_genesis_key.pub, 20, 1, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, system.work_generate_limited (block1->hash (), node1.network_params.network.publish_thresholds.epoch_2_receive, node1.network_params.network.publish_thresholds.epoch_1 - 1))); + nano::publish publish4 (block4); + tcp_channel->send (publish4, [](boost::system::error_code const & ec, size_t size) {}); + ASSERT_TIMELY (10s, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in) != 0); + ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::in)); + ASSERT_EQ (2, node2.stats.count (nano::stat::type::error, nano::stat::detail::insufficient_work)); +} + TEST (receivable_processor, confirm_insufficient_pos) { nano::system system (1); diff --git a/nano/lib/work.cpp b/nano/lib/work.cpp index ee4811253..1fbc31d83 100644 --- a/nano/lib/work.cpp +++ b/nano/lib/work.cpp @@ -24,12 +24,12 @@ std::string nano::to_string (nano::work_version const version_a) bool nano::work_validate_entry (nano::block const & block_a) { - return block_a.difficulty () < nano::work_threshold_entry (block_a.work_version ()); + return block_a.difficulty () < nano::work_threshold_entry (block_a.work_version (), block_a.type ()); } bool nano::work_validate_entry (nano::work_version const version_a, nano::root const & root_a, uint64_t const work_a) { - return nano::work_difficulty (version_a, root_a, work_a) < nano::work_threshold_entry (version_a); + return nano::work_difficulty (version_a, root_a, work_a) < nano::work_threshold_entry (version_a, nano::block_type::state); } uint64_t nano::work_difficulty (nano::work_version const version_a, nano::root const & root_a, uint64_t const work_a) @@ -60,16 +60,24 @@ uint64_t nano::work_threshold_base (nano::work_version const version_a) return result; } -uint64_t nano::work_threshold_entry (nano::work_version const version_a) +uint64_t nano::work_threshold_entry (nano::work_version const version_a, nano::block_type const type_a) { uint64_t result{ std::numeric_limits::max () }; - switch (version_a) + if (type_a == nano::block_type::state) { - case nano::work_version::work_1: - result = nano::work_v1::threshold_entry (); - break; - default: - debug_assert (false && "Invalid version specified to work_threshold_entry"); + switch (version_a) + { + case nano::work_version::work_1: + result = nano::work_v1::threshold_entry (); + break; + default: + debug_assert (false && "Invalid version specified to work_threshold_entry"); + } + } + else + { + static nano::network_constants network_constants; + result = network_constants.publish_thresholds.epoch_1; } return result; } diff --git a/nano/lib/work.hpp b/nano/lib/work.hpp index f65261cfa..d518fc4a2 100644 --- a/nano/lib/work.hpp +++ b/nano/lib/work.hpp @@ -22,13 +22,14 @@ std::string to_string (nano::work_version const version_a); class block; class block_details; +enum class block_type : uint8_t; bool work_validate_entry (nano::block const &); bool work_validate_entry (nano::work_version const, nano::root const &, uint64_t const); uint64_t work_difficulty (nano::work_version const, nano::root const &, uint64_t const); uint64_t work_threshold_base (nano::work_version const); -uint64_t work_threshold_entry (nano::work_version const); +uint64_t work_threshold_entry (nano::work_version const, nano::block_type const); // Ledger threshold uint64_t work_threshold (nano::work_version const, nano::block_details const); diff --git a/nano/node/bootstrap/bootstrap_bulk_pull.cpp b/nano/node/bootstrap/bootstrap_bulk_pull.cpp index d43bd3c30..7f17207f8 100644 --- a/nano/node/bootstrap/bootstrap_bulk_pull.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_pull.cpp @@ -260,7 +260,7 @@ void nano::bulk_pull_client::received_block (boost::system::error_code const & e connection->connections->pool_connection (connection); } } - else + else if (block == nullptr) { if (connection->node->config.logging.bulk_pull_logging ()) { @@ -268,6 +268,14 @@ void nano::bulk_pull_client::received_block (boost::system::error_code const & e } connection->node->stats.inc (nano::stat::type::bootstrap, nano::stat::detail::bulk_pull_deserialize_receive_block, nano::stat::dir::in); } + else // Work invalid + { + if (connection->node->config.logging.bulk_pull_logging ()) + { + connection->node->logger.try_log (boost::str (boost::format ("Insufficient work for bulk pull block: %1%") % block->hash ().to_string ())); + } + connection->node->stats.inc_detail_only (nano::stat::type::error, nano::stat::detail::insufficient_work); + } } else { diff --git a/nano/node/bootstrap/bootstrap_bulk_push.cpp b/nano/node/bootstrap/bootstrap_bulk_push.cpp index 1ee7b9c2d..d4cd91431 100644 --- a/nano/node/bootstrap/bootstrap_bulk_push.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_push.cpp @@ -237,12 +237,20 @@ void nano::bulk_push_server::received_block (boost::system::error_code const & e connection->node->process_active (std::move (block)); throttled_receive (); } - else + else if (block == nullptr) { if (connection->node->config.logging.bulk_pull_logging ()) { connection->node->logger.try_log ("Error deserializing block received from pull request"); } } + else // Work invalid + { + if (connection->node->config.logging.bulk_pull_logging ()) + { + connection->node->logger.try_log (boost::str (boost::format ("Insufficient work for bulk push block: %1%") % block->hash ().to_string ())); + } + connection->node->stats.inc_detail_only (nano::stat::type::error, nano::stat::detail::insufficient_work); + } } } diff --git a/nano/node/bootstrap/bootstrap_server.cpp b/nano/node/bootstrap/bootstrap_server.cpp index 8478d3fe2..4badab5de 100644 --- a/nano/node/bootstrap/bootstrap_server.cpp +++ b/nano/node/bootstrap/bootstrap_server.cpp @@ -5,6 +5,7 @@ #include #include +#include nano::bootstrap_listener::bootstrap_listener (uint16_t port_a, nano::node & node_a) : node (node_a), @@ -431,7 +432,14 @@ void nano::bootstrap_server::receive_publish_action (boost::system::error_code c { if (is_realtime_connection ()) { - add_request (std::unique_ptr (request.release ())); + if (!nano::work_validate_entry (*request->block)) + { + add_request (std::unique_ptr (request.release ())); + } + else + { + node->stats.inc_detail_only (nano::stat::type::error, nano::stat::detail::insufficient_work); + } } receive (); } @@ -484,7 +492,26 @@ void nano::bootstrap_server::receive_confirm_ack_action (boost::system::error_co { if (is_realtime_connection ()) { - add_request (std::unique_ptr (request.release ())); + bool process_vote (true); + if (header_a.block_type () != nano::block_type::not_a_block) + { + for (auto & vote_block : request->vote->blocks) + { + if (!vote_block.which ()) + { + auto block (boost::get> (vote_block)); + if (nano::work_validate_entry (*block)) + { + process_vote = false; + node->stats.inc_detail_only (nano::stat::type::error, nano::stat::detail::insufficient_work); + } + } + } + } + if (process_vote) + { + add_request (std::unique_ptr (request.release ())); + } } receive (); } diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 5b6d530de..66d511da1 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -4741,7 +4741,7 @@ void nano::json_handler::work_generate () auto hash (hash_impl ()); auto difficulty (difficulty_optional_impl (work_version)); multiplier_optional_impl (work_version, difficulty); - if (!ec && (difficulty > node.max_work_generate_difficulty (work_version) || difficulty < nano::work_threshold_entry (work_version))) + if (!ec && (difficulty > node.max_work_generate_difficulty (work_version) || difficulty < nano::work_threshold_entry (work_version, nano::block_type::state))) { ec = nano::error_rpc::difficulty_limit; } diff --git a/nano/node/network.cpp b/nano/node/network.cpp index c42d1bf13..ab4b03b80 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -447,18 +447,21 @@ public: node.stats.inc (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::in); if (!message_a.vote->account.is_zero ()) { - for (auto & vote_block : message_a.vote->blocks) + if (message_a.header.block_type () != nano::block_type::not_a_block) { - if (!vote_block.which ()) + for (auto & vote_block : message_a.vote->blocks) { - auto block (boost::get> (vote_block)); - if (!node.block_processor.full ()) + if (!vote_block.which ()) { - node.process_active (block); - } - else - { - node.stats.inc (nano::stat::type::drop, nano::stat::detail::confirm_ack, nano::stat::dir::in); + auto block (boost::get> (vote_block)); + if (!node.block_processor.full ()) + { + node.process_active (block); + } + else + { + node.stats.inc (nano::stat::type::drop, nano::stat::detail::confirm_ack, nano::stat::dir::in); + } } } }