diff --git a/nano/core_test/block.cpp b/nano/core_test/block.cpp index 827a68d86a..e6360be079 100644 --- a/nano/core_test/block.cpp +++ b/nano/core_test/block.cpp @@ -277,7 +277,7 @@ TEST (frontier_req, serialization) std::vector bytes; { nano::vectorstream stream (bytes); - request1.serialize (stream); + request1.serialize (stream, false); } auto error (false); nano::bufferstream stream (bytes.data (), bytes.size ()); @@ -297,7 +297,7 @@ TEST (block, publish_req_serialization) std::vector bytes; { nano::vectorstream stream (bytes); - req.serialize (stream); + req.serialize (stream, false); } auto error (false); nano::bufferstream stream2 (bytes.data (), bytes.size ()); diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 50f1d061d9..5118fc26ce 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -3098,3 +3098,25 @@ TEST (ledger, epoch_2_started_flag) nano::ledger ledger (node1.store, stats); ASSERT_TRUE (ledger.cache.epoch_2_started.load ()); } + +TEST (ledger, epoch_2_upgrade_callback) +{ + nano::genesis genesis; + nano::stat stats; + nano::logger_mt logger; + auto store = nano::make_store (logger, nano::unique_path ()); + ASSERT_TRUE (!store->init_error ()); + bool cb_hit = false; + nano::ledger ledger (*store, stats, nano::generate_cache (), [&cb_hit]() { + cb_hit = true; + }); + { + auto transaction (store->tx_begin_write ()); + store->initialize (transaction, genesis, ledger.cache); + } + nano::work_pool pool (std::numeric_limits::max ()); + upgrade_epoch (pool, ledger, nano::epoch::epoch_1); + ASSERT_FALSE (cb_hit); + auto latest = upgrade_epoch (pool, ledger, nano::epoch::epoch_2); + ASSERT_TRUE (cb_hit); +} diff --git a/nano/core_test/message.cpp b/nano/core_test/message.cpp index 08dbc916df..16ac42eee0 100644 --- a/nano/core_test/message.cpp +++ b/nano/core_test/message.cpp @@ -12,7 +12,7 @@ TEST (message, keepalive_serialization) std::vector bytes; { nano::vectorstream stream (bytes); - request1.serialize (stream); + request1.serialize (stream, false); } auto error (false); nano::bufferstream stream (bytes.data (), bytes.size ()); @@ -30,7 +30,7 @@ TEST (message, keepalive_deserialize) std::vector bytes; { nano::vectorstream stream (bytes); - message1.serialize (stream); + message1.serialize (stream, false); } nano::bufferstream stream (bytes.data (), bytes.size ()); auto error (false); @@ -50,14 +50,14 @@ TEST (message, publish_serialization) std::vector bytes; { nano::vectorstream stream (bytes); - publish.header.serialize (stream); + publish.header.serialize (stream, false); } ASSERT_EQ (8, bytes.size ()); ASSERT_EQ (0x52, bytes[0]); ASSERT_EQ (0x41, bytes[1]); ASSERT_EQ (params.protocol.protocol_version, bytes[2]); ASSERT_EQ (params.protocol.protocol_version, bytes[3]); - ASSERT_EQ (params.protocol.protocol_version_min, bytes[4]); + ASSERT_EQ (params.protocol.protocol_version_min (false), bytes[4]); ASSERT_EQ (static_cast (nano::message_type::publish), bytes[5]); ASSERT_EQ (0x00, bytes[6]); // extensions ASSERT_EQ (static_cast (nano::block_type::send), bytes[7]); @@ -65,7 +65,7 @@ TEST (message, publish_serialization) auto error (false); nano::message_header header (error, stream); ASSERT_FALSE (error); - ASSERT_EQ (params.protocol.protocol_version_min, header.version_min); + ASSERT_EQ (params.protocol.protocol_version_min (false), header.version_min ()); ASSERT_EQ (params.protocol.protocol_version, header.version_using); ASSERT_EQ (params.protocol.protocol_version, header.version_max); ASSERT_EQ (nano::message_type::publish, header.type); @@ -79,7 +79,7 @@ TEST (message, confirm_ack_serialization) std::vector bytes; { nano::vectorstream stream1 (bytes); - con1.serialize (stream1); + con1.serialize (stream1, false); } nano::bufferstream stream2 (bytes.data (), bytes.size ()); bool error (false); @@ -107,7 +107,7 @@ TEST (message, confirm_ack_hash_serialization) std::vector bytes; { nano::vectorstream stream1 (bytes); - con1.serialize (stream1); + con1.serialize (stream1, false); } nano::bufferstream stream2 (bytes.data (), bytes.size ()); bool error (false); @@ -135,7 +135,7 @@ TEST (message, confirm_req_serialization) std::vector bytes; { nano::vectorstream stream (bytes); - req.serialize (stream); + req.serialize (stream, false); } auto error (false); nano::bufferstream stream2 (bytes.data (), bytes.size ()); @@ -155,7 +155,7 @@ TEST (message, confirm_req_hash_serialization) std::vector bytes; { nano::vectorstream stream (bytes); - req.serialize (stream); + req.serialize (stream, false); } auto error (false); nano::bufferstream stream2 (bytes.data (), bytes.size ()); @@ -188,7 +188,7 @@ TEST (message, confirm_req_hash_batch_serialization) std::vector bytes; { nano::vectorstream stream (bytes); - req.serialize (stream); + req.serialize (stream, false); } auto error (false); nano::bufferstream stream2 (bytes.data (), bytes.size ()); diff --git a/nano/core_test/message_parser.cpp b/nano/core_test/message_parser.cpp index e13540a053..a0649af803 100644 --- a/nano/core_test/message_parser.cpp +++ b/nano/core_test/message_parser.cpp @@ -66,14 +66,14 @@ TEST (message_parser, exact_confirm_ack_size) nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work, false); auto block (std::make_shared (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1)))); auto vote (std::make_shared (0, nano::keypair ().prv, 0, std::move (block))); nano::confirm_ack message (vote); std::vector bytes; { nano::vectorstream stream (bytes); - message.serialize (stream); + message.serialize (stream, true); } ASSERT_EQ (0, visitor.confirm_ack_count); ASSERT_EQ (parser.status, nano::message_parser::parse_status::success); @@ -100,13 +100,13 @@ TEST (message_parser, exact_confirm_req_size) nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work, false); auto block (std::make_shared (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1)))); nano::confirm_req message (std::move (block)); std::vector bytes; { nano::vectorstream stream (bytes); - message.serialize (stream); + message.serialize (stream, false); } ASSERT_EQ (0, visitor.confirm_req_count); ASSERT_EQ (parser.status, nano::message_parser::parse_status::success); @@ -133,13 +133,13 @@ TEST (message_parser, exact_confirm_req_hash_size) nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work, true); nano::send_block block (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1))); nano::confirm_req message (block.hash (), block.root ()); std::vector bytes; { nano::vectorstream stream (bytes); - message.serialize (stream); + message.serialize (stream, false); } ASSERT_EQ (0, visitor.confirm_req_count); ASSERT_EQ (parser.status, nano::message_parser::parse_status::success); @@ -166,13 +166,13 @@ TEST (message_parser, exact_publish_size) nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work, true); auto block (std::make_shared (1, 1, 2, nano::keypair ().prv, 4, *system.work.generate (nano::root (1)))); nano::publish message (std::move (block)); std::vector bytes; { nano::vectorstream stream (bytes); - message.serialize (stream); + message.serialize (stream, false); } ASSERT_EQ (0, visitor.publish_count); ASSERT_EQ (parser.status, nano::message_parser::parse_status::success); @@ -199,12 +199,12 @@ TEST (message_parser, exact_keepalive_size) nano::network_filter filter (1); nano::block_uniquer block_uniquer; nano::vote_uniquer vote_uniquer (block_uniquer); - nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work); + nano::message_parser parser (filter, block_uniquer, vote_uniquer, visitor, system.work, true); nano::keepalive message; std::vector bytes; { nano::vectorstream stream (bytes); - message.serialize (stream); + message.serialize (stream, true); } ASSERT_EQ (0, visitor.keepalive_count); ASSERT_EQ (parser.status, nano::message_parser::parse_status::success); diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index 37784e0111..4842b42773 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -784,7 +784,7 @@ TEST (tcp_listener, tcp_node_id_handshake) auto bootstrap_endpoint (system.nodes[0]->bootstrap.endpoint ()); auto cookie (system.nodes[0]->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (bootstrap_endpoint))); nano::node_id_handshake node_id_handshake (cookie, boost::none); - auto input (node_id_handshake.to_shared_const_buffer ()); + auto input (node_id_handshake.to_shared_const_buffer (false)); std::atomic write_done (false); socket->async_connect (bootstrap_endpoint, [&input, socket, &write_done](boost::system::error_code const & ec) { ASSERT_FALSE (ec); @@ -803,7 +803,7 @@ TEST (tcp_listener, tcp_node_id_handshake) boost::optional> response_zero (std::make_pair (nano::account (0), nano::signature (0))); nano::node_id_handshake node_id_handshake_response (boost::none, response_zero); - auto output (node_id_handshake_response.to_bytes ()); + auto output (node_id_handshake_response.to_bytes (false)); std::atomic done (false); socket->async_read (output, output->size (), [&output, &done](boost::system::error_code const & ec, size_t size_a) { ASSERT_FALSE (ec); @@ -851,7 +851,7 @@ TEST (tcp_listener, tcp_listener_timeout_node_id_handshake) auto socket (std::make_shared (node0)); auto cookie (node0->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (node0->bootstrap.endpoint ()))); nano::node_id_handshake node_id_handshake (cookie, boost::none); - auto input (node_id_handshake.to_shared_const_buffer ()); + auto input (node_id_handshake.to_shared_const_buffer (false)); socket->async_connect (node0->bootstrap.endpoint (), [&input, socket](boost::system::error_code const & ec) { ASSERT_FALSE (ec); socket->async_write (input, [&input](boost::system::error_code const & ec, size_t size_a) { @@ -1014,7 +1014,7 @@ TEST (network, bandwidth_limiter) nano::system system; nano::genesis genesis; nano::publish message (genesis.open); - auto message_size = message.to_bytes ()->size (); + auto message_size = message.to_bytes (false)->size (); auto message_limit = 4; // must be multiple of the number of channels nano::node_config node_config (nano::get_available_port (), system.logging); node_config.bandwidth_limit = message_limit * message_size; diff --git a/nano/core_test/network_filter.cpp b/nano/core_test/network_filter.cpp index ddc56ea9ee..ad54457d6b 100644 --- a/nano/core_test/network_filter.cpp +++ b/nano/core_test/network_filter.cpp @@ -12,7 +12,7 @@ TEST (network_filter, unit) nano::network_filter filter (1); auto one_block = [&filter](std::shared_ptr const & block_a, bool expect_duplicate_a) { nano::publish message (block_a); - auto bytes (message.to_bytes ()); + auto bytes (message.to_bytes (false)); nano::bufferstream stream (bytes->data (), bytes->size ()); // First read the header @@ -60,7 +60,7 @@ TEST (network_filter, many) auto block (std::make_shared (nano::test_genesis_key.pub, genesis.open->hash (), nano::test_genesis_key.pub, nano::genesis_amount - i * 10 * nano::xrb_ratio, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); nano::publish message (block); - auto bytes (message.to_bytes ()); + auto bytes (message.to_bytes (false)); nano::bufferstream stream (bytes->data (), bytes->size ()); // First read the header diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index a172b2246a..831ea7e979 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -1655,7 +1655,7 @@ TEST (node, fork_no_vote_quorum) std::vector buffer; { nano::vectorstream stream (buffer); - confirm.serialize (stream); + confirm.serialize (stream, false); } nano::transport::channel_udp channel (node2.network.udp_channels, node3.network.endpoint (), node1.network_params.protocol.protocol_version); channel.send_buffer (nano::shared_const_buffer (std::move (buffer)), nano::stat::detail::confirm_ack); diff --git a/nano/core_test/peer_container.cpp b/nano/core_test/peer_container.cpp index 6d14fd86ea..738e2617b4 100644 --- a/nano/core_test/peer_container.cpp +++ b/nano/core_test/peer_container.cpp @@ -182,7 +182,7 @@ TEST (peer_container, depeer) nano::endpoint endpoint0 (boost::asio::ip::address_v6::loopback (), nano::get_available_port ()); nano::keepalive message; message.header.version_using = 1; - auto bytes (message.to_bytes ()); + auto bytes (message.to_bytes (false)); nano::message_buffer buffer = { bytes->data (), bytes->size (), endpoint0 }; system.nodes[0]->network.udp_channels.receive_action (&buffer); ASSERT_EQ (1, system.nodes[0]->stats.count (nano::stat::type::udp, nano::stat::detail::outdated_version)); diff --git a/nano/node/bootstrap/bootstrap_server.cpp b/nano/node/bootstrap/bootstrap_server.cpp index 2da045e538..85b77d2132 100644 --- a/nano/node/bootstrap/bootstrap_server.cpp +++ b/nano/node/bootstrap/bootstrap_server.cpp @@ -669,7 +669,7 @@ class request_response_visitor : public nano::message_visitor debug_assert (!nano::validate_message (response->first, *message_a.query, response->second)); auto cookie (connection->node->network.syn_cookies.assign (nano::transport::map_tcp_to_endpoint (connection->remote_endpoint))); nano::node_id_handshake response_message (cookie, response); - auto shared_const_buffer = response_message.to_shared_const_buffer (); + auto shared_const_buffer = response_message.to_shared_const_buffer (connection->node->ledger.cache.epoch_2_started); connection->socket->async_write (shared_const_buffer, [connection = std::weak_ptr (connection)](boost::system::error_code const & ec, size_t size_a) { if (auto connection_l = connection.lock ()) { diff --git a/nano/node/common.cpp b/nano/node/common.cpp index d2fd6e4fed..b99e6c8d57 100644 --- a/nano/node/common.cpp +++ b/nano/node/common.cpp @@ -50,7 +50,6 @@ uint64_t nano::ip_address_hash_raw (boost::asio::ip::address const & ip_a, uint1 nano::message_header::message_header (nano::message_type type_a) : version_max (get_protocol_constants ().protocol_version), version_using (get_protocol_constants ().protocol_version), -version_min (get_protocol_constants ().protocol_version_min), type (type_a) { } @@ -63,13 +62,13 @@ nano::message_header::message_header (bool & error_a, nano::stream & stream_a) } } -void nano::message_header::serialize (nano::stream & stream_a) const +void nano::message_header::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { static nano::network_params network_params; nano::write (stream_a, network_params.header_magic_number); nano::write (stream_a, version_max); nano::write (stream_a, version_using); - nano::write (stream_a, version_min); + nano::write (stream_a, get_protocol_constants ().protocol_version_min (use_epoch_2_min_version_a)); nano::write (stream_a, type); nano::write (stream_a, static_cast (extensions.to_ullong ())); } @@ -90,7 +89,7 @@ bool nano::message_header::deserialize (nano::stream & stream_a) nano::read (stream_a, version_max); nano::read (stream_a, version_using); - nano::read (stream_a, version_min); + nano::read (stream_a, version_min_m); nano::read (stream_a, type); nano::read (stream_a, extensions_l); extensions = extensions_l; @@ -103,6 +102,12 @@ bool nano::message_header::deserialize (nano::stream & stream_a) return error; } +uint8_t nano::message_header::version_min () const +{ + debug_assert (version_min_m != std::numeric_limits::max ()); + return version_min_m; +} + nano::message::message (nano::message_type type_a) : header (type_a) { @@ -113,17 +118,17 @@ header (header_a) { } -std::shared_ptr> nano::message::to_bytes () const +std::shared_ptr> nano::message::to_bytes (bool use_epoch_2_min_version_a) const { auto bytes = std::make_shared> (); nano::vectorstream stream (*bytes); - serialize (stream); + serialize (stream, use_epoch_2_min_version_a); return bytes; } -nano::shared_const_buffer nano::message::to_shared_const_buffer () const +nano::shared_const_buffer nano::message::to_shared_const_buffer (bool use_epoch_2_min_version_a) const { - return shared_const_buffer (to_bytes ()); + return shared_const_buffer (to_bytes (use_epoch_2_min_version_a)); } nano::block_type nano::message_header::block_type () const @@ -323,13 +328,14 @@ std::string nano::message_parser::status_string () return "[unknown parse_status]"; } -nano::message_parser::message_parser (nano::network_filter & publish_filter_a, nano::block_uniquer & block_uniquer_a, nano::vote_uniquer & vote_uniquer_a, nano::message_visitor & visitor_a, nano::work_pool & pool_a) : +nano::message_parser::message_parser (nano::network_filter & publish_filter_a, nano::block_uniquer & block_uniquer_a, nano::vote_uniquer & vote_uniquer_a, nano::message_visitor & visitor_a, nano::work_pool & pool_a, bool use_epoch_2_min_version_a) : publish_filter (publish_filter_a), block_uniquer (block_uniquer_a), vote_uniquer (vote_uniquer_a), visitor (visitor_a), pool (pool_a), -status (parse_status::success) +status (parse_status::success), +use_epoch_2_min_version (use_epoch_2_min_version_a) { } @@ -345,7 +351,7 @@ void nano::message_parser::deserialize_buffer (uint8_t const * buffer_a, size_t nano::message_header header (error, stream); if (!error) { - if (header.version_using < get_protocol_constants ().protocol_version_min) + if (header.version_using < get_protocol_constants ().protocol_version_min (use_epoch_2_min_version)) { status = parse_status::outdated_version; } @@ -569,9 +575,9 @@ void nano::keepalive::visit (nano::message_visitor & visitor_a) const visitor_a.keepalive (*this); } -void nano::keepalive::serialize (nano::stream & stream_a) const +void nano::keepalive::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); for (auto i (peers.begin ()), j (peers.end ()); i != j; ++i) { debug_assert (i->address ().is_v6 ()); @@ -623,10 +629,10 @@ block (block_a) header.block_type_set (block->type ()); } -void nano::publish::serialize (nano::stream & stream_a) const +void nano::publish::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { debug_assert (block != nullptr); - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); block->serialize (stream_a); } @@ -690,9 +696,9 @@ void nano::confirm_req::visit (nano::message_visitor & visitor_a) const visitor_a.confirm_req (*this); } -void nano::confirm_req::serialize (nano::stream & stream_a) const +void nano::confirm_req::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); if (header.block_type () == nano::block_type::not_a_block) { debug_assert (!roots_hashes.empty ()); @@ -816,10 +822,10 @@ vote (vote_a) } } -void nano::confirm_ack::serialize (nano::stream & stream_a) const +void nano::confirm_ack::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { debug_assert (header.block_type () == nano::block_type::not_a_block || header.block_type () == nano::block_type::send || header.block_type () == nano::block_type::receive || header.block_type () == nano::block_type::open || header.block_type () == nano::block_type::change || header.block_type () == nano::block_type::state); - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); vote->serialize (stream_a, header.block_type ()); } @@ -862,9 +868,9 @@ message (header_a) } } -void nano::frontier_req::serialize (nano::stream & stream_a) const +void nano::frontier_req::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); write (stream_a, start.bytes); write (stream_a, age); write (stream_a, count); @@ -917,7 +923,7 @@ void nano::bulk_pull::visit (nano::message_visitor & visitor_a) const visitor_a.bulk_pull (*this); } -void nano::bulk_pull::serialize (nano::stream & stream_a) const +void nano::bulk_pull::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { /* * Ensure the "count_present" flag is set if there @@ -929,7 +935,7 @@ void nano::bulk_pull::serialize (nano::stream & stream_a) const */ debug_assert ((count == 0 && !is_count_present ()) || (count != 0 && is_count_present ())); - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); write (stream_a, start); write (stream_a, end); @@ -1013,9 +1019,9 @@ void nano::bulk_pull_account::visit (nano::message_visitor & visitor_a) const visitor_a.bulk_pull_account (*this); } -void nano::bulk_pull_account::serialize (nano::stream & stream_a) const +void nano::bulk_pull_account::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); write (stream_a, account); write (stream_a, minimum_amount); write (stream_a, flags); @@ -1055,9 +1061,9 @@ bool nano::bulk_push::deserialize (nano::stream & stream_a) return false; } -void nano::bulk_push::serialize (nano::stream & stream_a) const +void nano::bulk_push::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); } void nano::bulk_push::visit (nano::message_visitor & visitor_a) const @@ -1081,9 +1087,9 @@ bool nano::telemetry_req::deserialize (nano::stream & stream_a) return false; } -void nano::telemetry_req::serialize (nano::stream & stream_a) const +void nano::telemetry_req::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); } void nano::telemetry_req::visit (nano::message_visitor & visitor_a) const @@ -1112,9 +1118,9 @@ data (telemetry_data_a) header.extensions = telemetry_data::size; } -void nano::telemetry_ack::serialize (nano::stream & stream_a) const +void nano::telemetry_ack::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); if (!is_empty_payload ()) { data.serialize (stream_a); @@ -1343,9 +1349,9 @@ response (response) } } -void nano::node_id_handshake::serialize (nano::stream & stream_a) const +void nano::node_id_handshake::serialize (nano::stream & stream_a, bool use_epoch_2_min_version_a) const { - header.serialize (stream_a); + header.serialize (stream_a, use_epoch_2_min_version_a); if (query) { write (stream_a, *query); diff --git a/nano/node/common.hpp b/nano/node/common.hpp index 5213ac9892..4bee09ae34 100644 --- a/nano/node/common.hpp +++ b/nano/node/common.hpp @@ -189,7 +189,7 @@ class message_header final public: explicit message_header (nano::message_type); message_header (bool &, nano::stream &); - void serialize (nano::stream &) const; + void serialize (nano::stream &, bool) const; bool deserialize (nano::stream &); nano::block_type block_type () const; void block_type_set (nano::block_type); @@ -197,10 +197,14 @@ class message_header final void count_set (uint8_t); uint8_t version_max; uint8_t version_using; - uint8_t version_min; + +private: + uint8_t version_min_m{ std::numeric_limits::max () }; + +public: nano::message_type type; std::bitset<16> extensions; - static size_t constexpr size = sizeof (network_params::header_magic_number) + sizeof (version_max) + sizeof (version_using) + sizeof (version_min) + sizeof (type) + sizeof (/* extensions */ uint16_t); + static size_t constexpr size = sizeof (network_params::header_magic_number) + sizeof (version_max) + sizeof (version_using) + sizeof (version_min_m) + sizeof (type) + sizeof (/* extensions */ uint16_t); void flag_set (uint8_t); static uint8_t constexpr bulk_pull_count_present_flag = 0; @@ -209,6 +213,7 @@ class message_header final static uint8_t constexpr node_id_handshake_response_flag = 1; bool node_id_handshake_is_query () const; bool node_id_handshake_is_response () const; + uint8_t version_min () const; /** Size of the payload in bytes. For some messages, the payload size is based on header flags. */ size_t payload_length_bytes () const; @@ -222,10 +227,10 @@ class message explicit message (nano::message_type); explicit message (nano::message_header const &); virtual ~message () = default; - virtual void serialize (nano::stream &) const = 0; + virtual void serialize (nano::stream &, bool) const = 0; virtual void visit (nano::message_visitor &) const = 0; - std::shared_ptr> to_bytes () const; - nano::shared_const_buffer to_shared_const_buffer () const; + std::shared_ptr> to_bytes (bool) const; + nano::shared_const_buffer to_shared_const_buffer (bool) const; nano::message_header header; }; @@ -251,7 +256,7 @@ class message_parser final invalid_network, duplicate_publish_message }; - message_parser (nano::network_filter &, nano::block_uniquer &, nano::vote_uniquer &, nano::message_visitor &, nano::work_pool &); + message_parser (nano::network_filter &, nano::block_uniquer &, nano::vote_uniquer &, nano::message_visitor &, nano::work_pool &, bool); void deserialize_buffer (uint8_t const *, size_t); void deserialize_keepalive (nano::stream &, nano::message_header const &); void deserialize_publish (nano::stream &, nano::message_header const &, nano::uint128_t const & = 0); @@ -267,6 +272,7 @@ class message_parser final nano::message_visitor & visitor; nano::work_pool & pool; parse_status status; + bool use_epoch_2_min_version; std::string status_string (); static const size_t max_safe_udp_message_size; }; @@ -276,7 +282,7 @@ class keepalive final : public message keepalive (); keepalive (bool &, nano::stream &, nano::message_header const &); void visit (nano::message_visitor &) const override; - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &); bool operator== (nano::keepalive const &) const; std::array peers; @@ -288,7 +294,7 @@ class publish final : public message publish (bool &, nano::stream &, nano::message_header const &, nano::uint128_t const & = 0, nano::block_uniquer * = nullptr); explicit publish (std::shared_ptr); void visit (nano::message_visitor &) const override; - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &, nano::block_uniquer * = nullptr); bool operator== (nano::publish const &) const; std::shared_ptr block; @@ -301,7 +307,7 @@ class confirm_req final : public message explicit confirm_req (std::shared_ptr); confirm_req (std::vector> const &); confirm_req (nano::block_hash const &, nano::root const &); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &, nano::block_uniquer * = nullptr); void visit (nano::message_visitor &) const override; bool operator== (nano::confirm_req const &) const; @@ -315,7 +321,7 @@ class confirm_ack final : public message public: confirm_ack (bool &, nano::stream &, nano::message_header const &, nano::vote_uniquer * = nullptr); explicit confirm_ack (std::shared_ptr); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; void visit (nano::message_visitor &) const override; bool operator== (nano::confirm_ack const &) const; std::shared_ptr vote; @@ -326,7 +332,7 @@ class frontier_req final : public message public: frontier_req (); frontier_req (bool &, nano::stream &, nano::message_header const &); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &); void visit (nano::message_visitor &) const override; bool operator== (nano::frontier_req const &) const; @@ -376,7 +382,7 @@ class telemetry_req final : public message public: telemetry_req (); explicit telemetry_req (nano::message_header const &); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &); void visit (nano::message_visitor &) const override; }; @@ -386,7 +392,7 @@ class telemetry_ack final : public message telemetry_ack (); telemetry_ack (bool &, nano::stream &, nano::message_header const &); explicit telemetry_ack (telemetry_data const &); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; void visit (nano::message_visitor &) const override; bool deserialize (nano::stream &); uint16_t size () const; @@ -401,7 +407,7 @@ class bulk_pull final : public message using count_t = uint32_t; bulk_pull (); bulk_pull (bool &, nano::stream &, nano::message_header const &); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &); void visit (nano::message_visitor &) const override; nano::hash_or_account start{ 0 }; @@ -418,7 +424,7 @@ class bulk_pull_account final : public message public: bulk_pull_account (); bulk_pull_account (bool &, nano::stream &, nano::message_header const &); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &); void visit (nano::message_visitor &) const override; nano::account account; @@ -431,7 +437,7 @@ class bulk_push final : public message public: bulk_push (); explicit bulk_push (nano::message_header const &); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &); void visit (nano::message_visitor &) const override; }; @@ -440,7 +446,7 @@ class node_id_handshake final : public message public: node_id_handshake (bool &, nano::stream &, nano::message_header const &); node_id_handshake (boost::optional, boost::optional>); - void serialize (nano::stream &) const override; + void serialize (nano::stream &, bool) const override; bool deserialize (nano::stream &); void visit (nano::message_visitor &) const override; bool operator== (nano::node_id_handshake const &) const; diff --git a/nano/node/lmdb/lmdb.cpp b/nano/node/lmdb/lmdb.cpp index 97c70ded12..5b54eac32f 100644 --- a/nano/node/lmdb/lmdb.cpp +++ b/nano/node/lmdb/lmdb.cpp @@ -40,7 +40,7 @@ void mdb_val::convert_buffer_to_value () } } -nano::mdb_store::mdb_store (nano::logger_mt & logger_a, boost::filesystem::path const & path_a, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, size_t const batch_size, bool backup_before_upgrade) : +nano::mdb_store::mdb_store (nano::logger_mt & logger_a, boost::filesystem::path const & path_a, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, size_t const batch_size_a, bool backup_before_upgrade_a) : logger (logger_a), env (error, path_a, nano::mdb_env::options::make ().set_config (lmdb_config_a).set_use_no_mem_init (true)), mdb_txn_tracker (logger_a, txn_tracking_config_a, block_processor_batch_max_time_a), @@ -73,7 +73,7 @@ txn_tracking_enabled (txn_tracking_config_a.enable) { std::cout << "Upgrade in progress..." << std::endl; } - if (backup_before_upgrade) + if (backup_before_upgrade_a) { create_backup_file (env, path_a, logger_a); } @@ -84,7 +84,7 @@ txn_tracking_enabled (txn_tracking_config_a.enable) open_databases (error, transaction, MDB_CREATE); if (!error) { - error |= do_upgrades (transaction, needs_vacuuming, batch_size); + error |= do_upgrades (transaction, needs_vacuuming, batch_size_a); } } @@ -792,7 +792,6 @@ void nano::mdb_store::upgrade_v17_to_v18 (nano::write_transaction const & transa auto count_pre (count (transaction_a, state_blocks)); - nano::network_params network_params; auto num = 0u; for (nano::mdb_iterator state_i (transaction_a, state_blocks), state_n{}; state_i != state_n; ++state_i, ++num) { diff --git a/nano/node/network.cpp b/nano/node/network.cpp index f2b9ff6cdd..d247ade9f6 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -628,11 +628,11 @@ nano::tcp_endpoint nano::network::bootstrap_peer (bool lazy_bootstrap) bool use_udp_peer (nano::random_pool::generate_word32 (0, 1)); if (use_udp_peer || tcp_channels.size () == 0) { - result = udp_channels.bootstrap_peer (node.network_params.protocol.protocol_version_bootstrap_min); + result = udp_channels.bootstrap_peer (node.network_params.protocol.protocol_version_min (node.ledger.cache.epoch_2_started)); } if (result == nano::tcp_endpoint (boost::asio::ip::address_v6::any (), 0)) { - result = tcp_channels.bootstrap_peer (node.network_params.protocol.protocol_version_bootstrap_min); + result = tcp_channels.bootstrap_peer (node.network_params.protocol.protocol_version_min (node.ledger.cache.epoch_2_started)); } return result; } @@ -723,6 +723,18 @@ bool nano::network::empty () const return size () == 0; } +void nano::network::erase_below_version (uint8_t cutoff_version_a) +{ + std::vector> channels_to_remove; + tcp_channels.list_below_version (channels_to_remove, cutoff_version_a); + udp_channels.list_below_version (channels_to_remove, cutoff_version_a); + for (auto const & channel_to_remove : channels_to_remove) + { + debug_assert (channel_to_remove->get_network_version () < cutoff_version_a); + erase (*channel_to_remove); + } +} + void nano::network::erase (nano::transport::channel const & channel_a) { if (channel_a.get_type () == nano::transport::transport_type::tcp) diff --git a/nano/node/network.hpp b/nano/node/network.hpp index 0fb3b5a777..3db093c18f 100644 --- a/nano/node/network.hpp +++ b/nano/node/network.hpp @@ -150,7 +150,8 @@ class network final size_t size () const; float size_sqrt () const; bool empty () const; - void erase (nano::transport::channel const & channel_a); + void erase (nano::transport::channel const &); + void erase_below_version (uint8_t); nano::message_buffer_manager buffer_container; boost::asio::ip::udp::resolver resolver; std::vector packet_processing_threads; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 7854032c97..c2172313f0 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -103,7 +103,7 @@ store (*store_impl), wallets_store_impl (std::make_unique (application_path_a / "wallets.ldb", config_a.lmdb_config)), wallets_store (*wallets_store_impl), gap_cache (*this), -ledger (store, stats, flags_a.generate_cache), +ledger (store, stats, flags_a.generate_cache, [this]() { this->network.erase_below_version (network_params.protocol.protocol_version_min (true)); }), checker (config.signature_checker_threads), network (*this, config.peering_port), telemetry (std::make_shared (network, alarm, worker, observers.telemetry, stats, network_params, flags.disable_ongoing_telemetry_requests)), diff --git a/nano/node/repcrawler.cpp b/nano/node/repcrawler.cpp index 40d925726b..55ac9dadbd 100644 --- a/nano/node/repcrawler.cpp +++ b/nano/node/repcrawler.cpp @@ -320,9 +320,9 @@ void nano::rep_crawler::update_weights () } } -std::vector nano::rep_crawler::representatives (size_t count_a, nano::uint128_t const weight_a, boost::optional const & opt_version_min_a) +std::vector nano::rep_crawler::representatives (size_t count_a, nano::uint128_t const weight_a, boost::optional const & opt_version_min_a) { - auto version_min (opt_version_min_a.value_or (node.network_params.protocol.protocol_version_min)); + auto version_min (opt_version_min_a.value_or (node.network_params.protocol.protocol_version_min (node.ledger.cache.epoch_2_started))); std::vector result; nano::lock_guard lock (probable_reps_mutex); for (auto i (probable_reps.get ().begin ()), n (probable_reps.get ().end ()); i != n && result.size () < count_a; ++i) @@ -335,7 +335,7 @@ std::vector nano::rep_crawler::representatives (size_t cou return result; } -std::vector nano::rep_crawler::principal_representatives (size_t count_a, boost::optional const & opt_version_min_a) +std::vector nano::rep_crawler::principal_representatives (size_t count_a, boost::optional const & opt_version_min_a) { return representatives (count_a, node.minimum_principal_weight (), opt_version_min_a); } diff --git a/nano/node/repcrawler.hpp b/nano/node/repcrawler.hpp index b94d569b2f..a18d2e4743 100644 --- a/nano/node/repcrawler.hpp +++ b/nano/node/repcrawler.hpp @@ -103,10 +103,10 @@ class rep_crawler final nano::uint128_t total_weight () const; /** Request a list of the top \p count_a known representatives in descending order of weight, with at least \p weight_a voting weight, and optionally with a minimum version \p opt_version_min_a */ - std::vector representatives (size_t count_a = std::numeric_limits::max (), nano::uint128_t const weight_a = 0, boost::optional const & opt_version_min_a = boost::none); + std::vector representatives (size_t count_a = std::numeric_limits::max (), nano::uint128_t const weight_a = 0, boost::optional const & opt_version_min_a = boost::none); /** Request a list of the top \p count_a known principal representatives in descending order of weight, optionally with a minimum version \p opt_version_min_a */ - std::vector principal_representatives (size_t count_a = std::numeric_limits::max (), boost::optional const & opt_version_min_a = boost::none); + std::vector principal_representatives (size_t count_a = std::numeric_limits::max (), boost::optional const & opt_version_min_a = boost::none); /** Request a list of the top \p count_a known representative endpoints. */ std::vector> representative_endpoints (size_t count_a); diff --git a/nano/node/testing.cpp b/nano/node/testing.cpp index 947347f917..fe07659c90 100644 --- a/nano/node/testing.cpp +++ b/nano/node/testing.cpp @@ -172,28 +172,39 @@ uint64_t nano::system::work_generate_limited (nano::block_hash const & root_a, u return result; } -std::unique_ptr nano::system::upgrade_genesis_epoch (nano::node & node_a, nano::epoch const epoch_a) +std::unique_ptr nano::upgrade_epoch (nano::work_pool & pool_a, nano::ledger & ledger_a, nano::epoch epoch_a) { - bool error{ true }; + auto transaction (ledger_a.store.tx_begin_write ()); + auto account = nano::test_genesis_key.pub; + auto latest = ledger_a.latest (transaction, account); + auto balance = ledger_a.account_balance (transaction, account); + nano::state_block_builder builder; std::error_code ec; - auto latest (node_a.latest (nano::test_genesis_key.pub)); auto epoch = builder .account (nano::test_genesis_key.pub) .previous (latest) - .balance (node_a.balance (nano::test_genesis_key.pub)) - .link (node_a.ledger.epoch_link (epoch_a)) + .balance (balance) + .link (ledger_a.epoch_link (epoch_a)) .representative (nano::test_genesis_key.pub) .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) - .work (*work.generate (latest, nano::work_threshold (nano::work_version::work_1, nano::block_details (epoch_a, false, false, true)))) + .work (*pool_a.generate (latest, nano::work_threshold (nano::work_version::work_1, nano::block_details (epoch_a, false, false, true)))) .build (ec); + + bool error{ true }; if (!ec && epoch) { - error = node_a.process (*epoch).code != nano::process_result::progress; + error = ledger_a.process (transaction, *epoch).code != nano::process_result::progress; } + return !error ? std::move (epoch) : nullptr; } +std::unique_ptr nano::system::upgrade_genesis_epoch (nano::node & node_a, nano::epoch const epoch_a) +{ + return upgrade_epoch (work, node_a.ledger, epoch_a); +} + void nano::system::deadline_set (std::chrono::duration const & delta_a) { deadline = std::chrono::steady_clock::now () + delta_a * deadline_scaling_factor; diff --git a/nano/node/testing.hpp b/nano/node/testing.hpp index 1780b36296..16562449b3 100644 --- a/nano/node/testing.hpp +++ b/nano/node/testing.hpp @@ -54,5 +54,6 @@ class system final std::chrono::time_point> deadline{ std::chrono::steady_clock::time_point::max () }; double deadline_scaling_factor{ 1.0 }; }; +std::unique_ptr upgrade_epoch (nano::work_pool &, nano::ledger &, nano::epoch); } REGISTER_ERROR_CODES (nano, error_system); diff --git a/nano/node/transport/tcp.cpp b/nano/node/transport/tcp.cpp index 9837e70341..984d976e24 100644 --- a/nano/node/transport/tcp.cpp +++ b/nano/node/transport/tcp.cpp @@ -261,7 +261,7 @@ nano::tcp_endpoint nano::transport::tcp_channels::bootstrap_peer (uint8_t connec void nano::transport::tcp_channels::process_message (nano::message const & message_a, nano::tcp_endpoint const & endpoint_a, nano::account const & node_id_a, std::shared_ptr socket_a, nano::bootstrap_server_type type_a) { - if (!stopped) + if (!stopped && message_a.header.version_using >= protocol_constants ().protocol_version_min (node.ledger.cache.epoch_2_started)) { auto channel (node.network.find_channel (nano::transport::map_tcp_to_endpoint (endpoint_a))); if (channel) @@ -411,6 +411,10 @@ void nano::transport::tcp_channels::purge (std::chrono::steady_clock::time_point auto attempts_cutoff (attempts.get ().lower_bound (cutoff_a)); attempts.get ().erase (attempts.get ().begin (), attempts_cutoff); + // Check if any tcp channels belonging to old protocol versions which may still be alive due to async operations + auto lower_bound = channels.get ().lower_bound (node.network_params.protocol.protocol_version_min (node.ledger.cache.epoch_2_started)); + channels.get ().erase (channels.get ().begin (), lower_bound); + // Cleanup any sockets which may still be existing from failed node id handshakes node_id_handshake_sockets.erase (std::remove_if (node_id_handshake_sockets.begin (), node_id_handshake_sockets.end (), [this](auto socket) { return channels.get ().find (socket->remote_endpoint ()) == channels.get ().end (); @@ -442,7 +446,7 @@ void nano::transport::tcp_channels::ongoing_keepalive () size_t random_count (std::min (static_cast (6), static_cast (std::ceil (std::sqrt (node.network.udp_channels.size ()))))); for (auto i (0); i <= random_count; ++i) { - auto tcp_endpoint (node.network.udp_channels.bootstrap_peer (node.network_params.protocol.protocol_version_min)); + auto tcp_endpoint (node.network.udp_channels.bootstrap_peer (node.network_params.protocol.protocol_version_min (node.ledger.cache.epoch_2_started))); if (tcp_endpoint != invalid_endpoint && find_channel (tcp_endpoint) == nullptr && !node.network.excluded_peers.check (tcp_endpoint)) { start_tcp (nano::transport::map_tcp_to_endpoint (tcp_endpoint)); @@ -461,6 +465,16 @@ void nano::transport::tcp_channels::ongoing_keepalive () }); } +void nano::transport::tcp_channels::list_below_version (std::vector> & channels_a, uint8_t cutoff_version_a) +{ + nano::lock_guard lock (mutex); + // clang-format off + nano::transform_if (channels.get ().begin (), channels.get ().end (), std::back_inserter (channels_a), + [cutoff_version_a](auto & channel_a) { return channel_a.channel->get_network_version () < cutoff_version_a; }, + [](const auto & channel) { return channel.channel; }); + // clang-format on +} + void nano::transport::tcp_channels::list (std::deque> & deque_a, uint8_t minimum_version_a, bool include_temporary_channels_a) { nano::lock_guard lock (mutex); @@ -537,7 +551,7 @@ void nano::transport::tcp_channels::start_tcp (nano::endpoint const & endpoint_a // TCP node ID handshake auto cookie (node_l->network.syn_cookies.assign (endpoint_a)); nano::node_id_handshake message (cookie, boost::none); - auto bytes = message.to_shared_const_buffer (); + auto bytes = message.to_shared_const_buffer (node_l->ledger.cache.epoch_2_started); if (node_l->config.logging.network_node_id_handshake_logging ()) { node_l->logger.try_log (boost::str (boost::format ("Node ID handshake request sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % (*cookie).to_string ())); @@ -581,11 +595,9 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptr node_w (node.shared ()); if (auto socket_l = channel_a->socket.lock ()) { - auto cleanup_and_udp_fallback = [socket_w = channel_a->socket, node_w](nano::endpoint const & endpoint_a, std::function)> const & callback_a) { + auto cleanup_node_id_handshake_socket = [socket_w = channel_a->socket, node_w](nano::endpoint const & endpoint_a, std::function)> const & callback_a) { if (auto node_l = node_w.lock ()) { - node_l->network.tcp_channels.udp_fallback (endpoint_a, callback_a); - if (auto socket_l = socket_w.lock ()) { node_l->network.tcp_channels.remove_node_id_handshake_socket (socket_l); @@ -594,7 +606,15 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrasync_read (receive_buffer_a, 8 + sizeof (nano::account) + sizeof (nano::account) + sizeof (nano::signature), [node_w, channel_a, endpoint_a, receive_buffer_a, callback_a, cleanup_and_udp_fallback](boost::system::error_code const & ec, size_t size_a) { + auto cleanup_and_udp_fallback = [socket_w = channel_a->socket, node_w, cleanup_node_id_handshake_socket](nano::endpoint const & endpoint_a, std::function)> const & callback_a) { + if (auto node_l = node_w.lock ()) + { + node_l->network.tcp_channels.udp_fallback (endpoint_a, callback_a); + cleanup_node_id_handshake_socket (endpoint_a, callback_a); + } + }; + + socket_l->async_read (receive_buffer_a, 8 + sizeof (nano::account) + sizeof (nano::account) + sizeof (nano::signature), [node_w, channel_a, endpoint_a, receive_buffer_a, callback_a, cleanup_and_udp_fallback, cleanup_node_id_handshake_socket](boost::system::error_code const & ec, size_t size_a) { if (auto node_l = node_w.lock ()) { if (!ec && channel_a) @@ -603,79 +623,91 @@ void nano::transport::tcp_channels::start_tcp_receive_node_id (std::shared_ptrdata (), size_a); nano::message_header header (error, stream); - if (!error && header.type == nano::message_type::node_id_handshake && header.version_using >= node_l->network_params.protocol.protocol_version_min) + if (!error && header.type == nano::message_type::node_id_handshake) { - nano::node_id_handshake message (error, stream, header); - if (!error && message.response && message.query) + if (header.version_using >= node_l->network_params.protocol.protocol_version_min (node_l->ledger.cache.epoch_2_started)) { - channel_a->set_network_version (header.version_using); - auto node_id (message.response->first); - bool process (!node_l->network.syn_cookies.validate (endpoint_a, node_id, message.response->second) && node_id != node_l->node_id.pub); - if (process) + nano::node_id_handshake message (error, stream, header); + if (!error && message.response && message.query) { - /* If node ID is known, don't establish new connection - Exception: temporary channels from bootstrap_server */ - auto existing_channel (node_l->network.tcp_channels.find_node_id (node_id)); - if (existing_channel) + channel_a->set_network_version (header.version_using); + auto node_id (message.response->first); + bool process (!node_l->network.syn_cookies.validate (endpoint_a, node_id, message.response->second) && node_id != node_l->node_id.pub); + if (process) { - process = existing_channel->temporary; + /* If node ID is known, don't establish new connection + Exception: temporary channels from bootstrap_server */ + auto existing_channel (node_l->network.tcp_channels.find_node_id (node_id)); + if (existing_channel) + { + process = existing_channel->temporary; + } } - } - if (process) - { - channel_a->set_node_id (node_id); - channel_a->set_last_packet_received (std::chrono::steady_clock::now ()); - boost::optional> response (std::make_pair (node_l->node_id.pub, nano::sign_message (node_l->node_id.prv, node_l->node_id.pub, *message.query))); - nano::node_id_handshake response_message (boost::none, response); - auto bytes = response_message.to_shared_const_buffer (); - if (node_l->config.logging.network_node_id_handshake_logging ()) + if (process) { - node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % (*message.query).to_string ())); - } - channel_a->send_buffer (bytes, nano::stat::detail::node_id_handshake, [node_w, channel_a, endpoint_a, callback_a, cleanup_and_udp_fallback](boost::system::error_code const & ec, size_t size_a) { - if (auto node_l = node_w.lock ()) + channel_a->set_node_id (node_id); + channel_a->set_last_packet_received (std::chrono::steady_clock::now ()); + boost::optional> response (std::make_pair (node_l->node_id.pub, nano::sign_message (node_l->node_id.prv, node_l->node_id.pub, *message.query))); + nano::node_id_handshake response_message (boost::none, response); + auto bytes = response_message.to_shared_const_buffer (node_l->ledger.cache.epoch_2_started); + if (node_l->config.logging.network_node_id_handshake_logging ()) { - if (!ec && channel_a) + node_l->logger.try_log (boost::str (boost::format ("Node ID handshake response sent with node ID %1% to %2%: query %3%") % node_l->node_id.pub.to_node_id () % endpoint_a % (*message.query).to_string ())); + } + channel_a->send_buffer (bytes, nano::stat::detail::node_id_handshake, [node_w, channel_a, endpoint_a, callback_a, cleanup_and_udp_fallback](boost::system::error_code const & ec, size_t size_a) { + if (auto node_l = node_w.lock ()) { - // Insert new node ID connection - if (auto socket_l = channel_a->socket.lock ()) + if (!ec && channel_a) { - channel_a->set_last_packet_sent (std::chrono::steady_clock::now ()); - auto response_server = std::make_shared (socket_l, node_l); - node_l->network.tcp_channels.insert (channel_a, socket_l, response_server); - if (callback_a) + // Insert new node ID connection + if (auto socket_l = channel_a->socket.lock ()) { - callback_a (channel_a); - } - // Listen for possible responses - response_server->type = nano::bootstrap_server_type::realtime_response_server; - response_server->remote_node_id = channel_a->get_node_id (); - response_server->receive (); - node_l->network.tcp_channels.remove_node_id_handshake_socket (socket_l); - - if (!node_l->flags.disable_initial_telemetry_requests) - { - node_l->telemetry->get_metrics_single_peer_async (channel_a, [](nano::telemetry_data_response /* unused */) { - // Intentionally empty, starts the telemetry request cycle to more quickly disconnect from invalid peers - }); + channel_a->set_last_packet_sent (std::chrono::steady_clock::now ()); + auto response_server = std::make_shared (socket_l, node_l); + node_l->network.tcp_channels.insert (channel_a, socket_l, response_server); + if (callback_a) + { + callback_a (channel_a); + } + // Listen for possible responses + response_server->type = nano::bootstrap_server_type::realtime_response_server; + response_server->remote_node_id = channel_a->get_node_id (); + response_server->receive (); + node_l->network.tcp_channels.remove_node_id_handshake_socket (socket_l); + + if (!node_l->flags.disable_initial_telemetry_requests) + { + node_l->telemetry->get_metrics_single_peer_async (channel_a, [](nano::telemetry_data_response /* unused */) { + // Intentionally empty, starts the telemetry request cycle to more quickly disconnect from invalid peers + }); + } } } - } - else - { - if (node_l->config.logging.network_node_id_handshake_logging ()) + else { - node_l->logger.try_log (boost::str (boost::format ("Error sending node_id_handshake to %1%: %2%") % endpoint_a % ec.message ())); + if (node_l->config.logging.network_node_id_handshake_logging ()) + { + node_l->logger.try_log (boost::str (boost::format ("Error sending node_id_handshake to %1%: %2%") % endpoint_a % ec.message ())); + } + cleanup_and_udp_fallback (endpoint_a, callback_a); } - cleanup_and_udp_fallback (endpoint_a, callback_a); } - } - }); + }); + } + } + else + { + cleanup_and_udp_fallback (endpoint_a, callback_a); } } else { - cleanup_and_udp_fallback (endpoint_a, callback_a); + // Version of channel is not high enough, just abort. Don't fallback to udp, instead cleanup attempt + cleanup_node_id_handshake_socket (endpoint_a, callback_a); + { + nano::lock_guard lock (node_l->network.tcp_channels.mutex); + node_l->network.tcp_channels.attempts.get ().erase (nano::transport::map_endpoint_to_tcp (endpoint_a)); + } } } else diff --git a/nano/node/transport/tcp.hpp b/nano/node/transport/tcp.hpp index 6dd4738a19..e8cbf5659c 100644 --- a/nano/node/transport/tcp.hpp +++ b/nano/node/transport/tcp.hpp @@ -103,6 +103,7 @@ namespace transport std::unique_ptr collect_container_info (std::string const &); void purge (std::chrono::steady_clock::time_point const &); void ongoing_keepalive (); + void list_below_version (std::vector> &, uint8_t); void list (std::deque> &, uint8_t = 0, bool = true); void modify (std::shared_ptr, std::function)>); void update (nano::tcp_endpoint const &); @@ -137,6 +138,10 @@ namespace transport class node_id_tag { }; + class version_tag + { + }; + class channel_tcp_wrapper final { public: @@ -169,6 +174,10 @@ namespace transport debug_assert (!node_id.is_zero ()); return node_id; } + uint8_t network_version () const + { + return channel->get_network_version (); + } }; class tcp_endpoint_attempt final { @@ -196,6 +205,8 @@ namespace transport mi::const_mem_fun>, mi::ordered_non_unique, mi::const_mem_fun>, + mi::ordered_non_unique, + mi::const_mem_fun>, mi::hashed_non_unique, mi::const_mem_fun>>> channels; diff --git a/nano/node/transport/transport.cpp b/nano/node/transport/transport.cpp index 733c107754..6843745ec3 100644 --- a/nano/node/transport/transport.cpp +++ b/nano/node/transport/transport.cpp @@ -89,7 +89,7 @@ void nano::transport::channel::send (nano::message const & message_a, std::funct { callback_visitor visitor; message_a.visit (visitor); - auto buffer (message_a.to_shared_const_buffer ()); + auto buffer (message_a.to_shared_const_buffer (node.ledger.cache.epoch_2_started)); auto detail (visitor.result); auto is_droppable_by_limiter = drop_policy_a == nano::buffer_drop_policy::limiter; auto should_drop (node.network.limiter.should_drop (buffer.size ())); diff --git a/nano/node/transport/udp.cpp b/nano/node/transport/udp.cpp index 4610f93690..89d40dbcd5 100644 --- a/nano/node/transport/udp.cpp +++ b/nano/node/transport/udp.cpp @@ -544,7 +544,7 @@ void nano::transport::udp_channels::receive_action (nano::message_buffer * data_ if (allowed_sender) { udp_message_visitor visitor (node, data_a->endpoint); - nano::message_parser parser (node.network.publish_filter, node.block_uniquer, node.vote_uniquer, visitor, node.work); + nano::message_parser parser (node.network.publish_filter, node.block_uniquer, node.vote_uniquer, visitor, node.work, node.ledger.cache.epoch_2_started); parser.deserialize_buffer (data_a->buffer, data_a->size); if (parser.status == nano::message_parser::parse_status::success) { @@ -716,11 +716,21 @@ void nano::transport::udp_channels::ongoing_keepalive () }); } +void nano::transport::udp_channels::list_below_version (std::vector> & channels_a, uint8_t cutoff_version_a) +{ + nano::lock_guard lock (mutex); + // clang-format off + nano::transform_if (channels.get ().begin (), channels.get ().end (), std::back_inserter (channels_a), + [cutoff_version_a](auto & channel_a) { return channel_a.channel->get_network_version () < cutoff_version_a; }, + [](const auto & channel) { return channel.channel; }); + // clang-format on +} + void nano::transport::udp_channels::list (std::deque> & deque_a, uint8_t minimum_version_a) { nano::lock_guard lock (mutex); // clang-format off - nano::transform_if (channels.begin (), channels.end (), std::back_inserter (deque_a), + nano::transform_if (channels.get ().begin (), channels.get ().end (), std::back_inserter (deque_a), [minimum_version_a](auto & channel_a) { return channel_a.channel->get_network_version () >= minimum_version_a; }, [](const auto & channel) { return channel.channel; }); // clang-format on diff --git a/nano/node/transport/udp.hpp b/nano/node/transport/udp.hpp index 50f0149244..19d629ad65 100644 --- a/nano/node/transport/udp.hpp +++ b/nano/node/transport/udp.hpp @@ -103,6 +103,7 @@ namespace transport std::unique_ptr collect_container_info (std::string const &); void purge (std::chrono::steady_clock::time_point const &); void ongoing_keepalive (); + void list_below_version (std::vector> &, uint8_t); void list (std::deque> &, uint8_t = 0); void modify (std::shared_ptr, std::function)>); nano::node & node; diff --git a/nano/secure/common.cpp b/nano/secure/common.cpp index bf52172e3a..3a60a36bd5 100644 --- a/nano/secure/common.cpp +++ b/nano/secure/common.cpp @@ -73,7 +73,7 @@ network_params (network_constants::active_network) } nano::network_params::network_params (nano::nano_networks network_a) : -network (network_a), protocol (network_a), ledger (network), voting (network), node (network), portmapping (network), bootstrap (network) +network (network_a), ledger (network), voting (network), node (network), portmapping (network), bootstrap (network) { unsigned constexpr kdf_full_work = 64 * 1024; unsigned constexpr kdf_test_work = 8; @@ -81,8 +81,9 @@ network (network_a), protocol (network_a), ledger (network), voting (network), n header_magic_number = network.is_test_network () ? std::array{ { 'R', 'A' } } : network.is_beta_network () ? std::array{ { 'N', 'D' } } : std::array{ { 'R', 'C' } }; } -nano::protocol_constants::protocol_constants (nano::nano_networks network_a) +uint8_t nano::protocol_constants::protocol_version_min (bool use_epoch_2_min_version_a) const { + return use_epoch_2_min_version_a ? protocol_version_min_epoch_2 : protocol_version_min_pre_epoch_2; } nano::ledger_constants::ledger_constants (nano::network_constants & network_constants) : diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 4fa1124d70..3f5df09150 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -352,21 +352,25 @@ class network_params; class protocol_constants { public: - protocol_constants (nano::nano_networks network_a); - /** Current protocol version */ - uint8_t protocol_version = 0x12; + uint8_t const protocol_version = 0x12; /** Minimum accepted protocol version */ - uint8_t protocol_version_min = 0x11; - - /** Do not bootstrap from nodes older than this version. */ - uint8_t protocol_version_bootstrap_min = 0x11; + uint8_t protocol_version_min (bool epoch_2_started) const; /** Do not request telemetry metrics to nodes older than this version */ - uint8_t telemetry_protocol_version_min = 0x12; + uint8_t const telemetry_protocol_version_min = 0x12; + +private: + /* Minimum protocol version before an epoch 2 block is seen */ + uint8_t const protocol_version_min_pre_epoch_2 = 0x11; + /* Minimum protocol version after an epoch 2 block is seen */ + uint8_t const protocol_version_min_epoch_2 = 0x12; }; +// Some places use the decltype of protocol_version instead of protocol_version_min. To keep those checks simpler we check that the decltypes match ignoring differences in const +static_assert (std::is_same, decltype (protocol_constants ().protocol_version_min (false))>::value, "protocol_min should match"); + /** Genesis keys and ledger constants for network variants */ class ledger_constants { @@ -491,6 +495,7 @@ class generate_cache bool cemented_count = true; bool unchecked_count = true; bool account_count = true; + bool epoch_2 = true; }; /* Holds an in-memory cache of various counts */ @@ -502,7 +507,7 @@ class ledger_cache std::atomic block_count{ 0 }; std::atomic unchecked_count{ 0 }; std::atomic account_count{ 0 }; - std::atomic epoch_2_started{ 0 }; + std::atomic epoch_2_started{ false }; }; /* Defines the possible states for an election to stop in */ diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index faa564521d..5dfb988e7f 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -447,7 +447,14 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a) } if (epoch == nano::epoch::epoch_2) { - ledger.cache.epoch_2_started.store (true); + if (!ledger.cache.epoch_2_started.exchange (true)) + { + // The first epoch 2 block has been seen + if (ledger.epoch_2_started_cb) + { + ledger.epoch_2_started_cb (); + } + } } } } @@ -725,15 +732,16 @@ verification (verification_a) } } // namespace -nano::ledger::ledger (nano::block_store & store_a, nano::stat & stat_a, nano::generate_cache const & generate_cache_a) : +nano::ledger::ledger (nano::block_store & store_a, nano::stat & stat_a, nano::generate_cache const & generate_cache_a, std::function epoch_2_started_cb_a) : store (store_a), stats (stat_a), -check_bootstrap_weights (true) +check_bootstrap_weights (true), +epoch_2_started_cb (epoch_2_started_cb_a) { if (!store.init_error ()) { auto transaction = store.tx_begin_read (); - if (generate_cache_a.reps || generate_cache_a.account_count) + if (generate_cache_a.reps || generate_cache_a.account_count || generate_cache_a.epoch_2) { bool epoch_2_started_l{ false }; for (auto i (store.latest_begin (transaction)), n (store.latest_end ()); i != n; ++i) diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 00a460c58b..2f5ae84b7a 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -15,7 +15,7 @@ using tally_t = std::map, std::gre class ledger final { public: - ledger (nano::block_store &, nano::stat &, nano::generate_cache const & = nano::generate_cache ()); + ledger (nano::block_store &, nano::stat &, nano::generate_cache const & = nano::generate_cache (), std::function = nullptr); nano::account account (nano::transaction const &, nano::block_hash const &) const; nano::uint128_t amount (nano::transaction const &, nano::account const &); nano::uint128_t amount (nano::transaction const &, nano::block_hash const &); @@ -56,6 +56,7 @@ class ledger final std::atomic bootstrap_weights_size{ 0 }; uint64_t bootstrap_weight_max_blocks{ 1 }; std::atomic check_bootstrap_weights; + std::function epoch_2_started_cb; }; std::unique_ptr collect_container_info (ledger & ledger, const std::string & name);