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
This commit is contained in:
Sergey Kroshnin 2020-08-18 17:52:29 +03:00 committed by GitHub
commit 84c7248aa1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 120 additions and 26 deletions

View file

@ -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)));

View file

@ -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<nano::send_block> (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<nano::send_block> (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<nano::send_block> (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::state_block> (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);

View file

@ -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<uint64_t>::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;
}

View file

@ -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);

View file

@ -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
{

View file

@ -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);
}
}
}

View file

@ -5,6 +5,7 @@
#include <nano/node/transport/tcp.hpp>
#include <boost/format.hpp>
#include <boost/variant/get.hpp>
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<nano::message> (request.release ()));
if (!nano::work_validate_entry (*request->block))
{
add_request (std::unique_ptr<nano::message> (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<nano::message> (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<std::shared_ptr<nano::block>> (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<nano::message> (request.release ()));
}
}
receive ();
}

View file

@ -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;
}

View file

@ -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<std::shared_ptr<nano::block>> (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<std::shared_ptr<nano::block>> (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);
}
}
}
}