From baad9327ee404b3af67a89b415cffc56ab2330d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20W=C3=B3jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Wed, 12 Oct 2022 18:19:38 +0200 Subject: [PATCH] Move message definitions to separate file (#3971) --- nano/node/CMakeLists.txt | 2 + nano/node/bootstrap/bootstrap_bulk_pull.hpp | 3 +- nano/node/bootstrap/bootstrap_server.cpp | 3 +- nano/node/bootstrap/bootstrap_server.hpp | 1 + nano/node/bootstrap/message_deserializer.hpp | 1 + nano/node/common.cpp | 1514 ----------------- nano/node/common.hpp | 382 ----- nano/node/messages.cpp | 1584 ++++++++++++++++++ nano/node/messages.hpp | 402 +++++ nano/node/telemetry.hpp | 3 +- nano/node/transport/transport.cpp | 2 +- nano/node/transport/transport.hpp | 1 + nano/test_common/telemetry.cpp | 1 + 13 files changed, 1999 insertions(+), 1900 deletions(-) create mode 100644 nano/node/messages.cpp create mode 100644 nano/node/messages.hpp diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index 8d3a9b9bf..9e9261a52 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -216,6 +216,8 @@ add_library( websocket_stream.cpp write_database_queue.hpp write_database_queue.cpp + messages.hpp + messages.cpp xorshift.hpp) if(NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") diff --git a/nano/node/bootstrap/bootstrap_bulk_pull.hpp b/nano/node/bootstrap/bootstrap_bulk_pull.hpp index 3619c6e2b..10efd5bf0 100644 --- a/nano/node/bootstrap/bootstrap_bulk_pull.hpp +++ b/nano/node/bootstrap/bootstrap_bulk_pull.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -56,7 +57,7 @@ private: * Tracks the account number for this account chain * Used when an account chain has a mix between state blocks and legacy blocks which do not encode the account number in the block * 0 if the account is unknown - */ + */ nano::account known_account{ 0 }; /** * Original pull request diff --git a/nano/node/bootstrap/bootstrap_server.cpp b/nano/node/bootstrap/bootstrap_server.cpp index 57f45131e..d3a3e8caf 100644 --- a/nano/node/bootstrap/bootstrap_server.cpp +++ b/nano/node/bootstrap/bootstrap_server.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -234,7 +235,7 @@ void nano::bootstrap_server::received_message (std::unique_ptr me bool nano::bootstrap_server::process_message (std::unique_ptr message) { - node->stats.inc (nano::stat::type::bootstrap_server, nano::message_type_to_stat_detail (message->header.type), nano::stat::dir::in); + node->stats.inc (nano::stat::type::bootstrap_server, nano::to_stat_detail (message->header.type), nano::stat::dir::in); debug_assert (is_undefined_connection () || is_realtime_connection () || is_bootstrap_connection ()); diff --git a/nano/node/bootstrap/bootstrap_server.hpp b/nano/node/bootstrap/bootstrap_server.hpp index 124b3c4dc..637d4d14b 100644 --- a/nano/node/bootstrap/bootstrap_server.hpp +++ b/nano/node/bootstrap/bootstrap_server.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include diff --git a/nano/node/bootstrap/message_deserializer.hpp b/nano/node/bootstrap/message_deserializer.hpp index 78fa2852a..4159217f6 100644 --- a/nano/node/bootstrap/message_deserializer.hpp +++ b/nano/node/bootstrap/message_deserializer.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include diff --git a/nano/node/common.cpp b/nano/node/common.cpp index 8540d5bd0..f7947c4f1 100644 --- a/nano/node/common.cpp +++ b/nano/node/common.cpp @@ -16,10 +16,6 @@ #include #include -std::bitset<16> constexpr nano::message_header::block_type_mask; -std::bitset<16> constexpr nano::message_header::count_mask; -std::bitset<16> constexpr nano::message_header::telemetry_size_mask; - std::chrono::seconds constexpr nano::telemetry_cache_cutoffs::dev; std::chrono::seconds constexpr nano::telemetry_cache_cutoffs::beta; std::chrono::seconds constexpr nano::telemetry_cache_cutoffs::live; @@ -42,1516 +38,6 @@ uint64_t nano::ip_address_hash_raw (boost::asio::ip::address const & ip_a, uint1 return result; } -nano::message_header::message_header (nano::network_constants const & constants, nano::message_type type_a) : - network{ constants.current_network }, - version_max{ constants.protocol_version }, - version_using{ constants.protocol_version }, - version_min{ constants.protocol_version_min }, - type (type_a) -{ -} - -nano::message_header::message_header (bool & error_a, nano::stream & stream_a) -{ - if (!error_a) - { - error_a = deserialize (stream_a); - } -} - -void nano::message_header::serialize (nano::stream & stream_a) const -{ - nano::write (stream_a, boost::endian::native_to_big (static_cast (network))); - nano::write (stream_a, version_max); - nano::write (stream_a, version_using); - nano::write (stream_a, version_min); - nano::write (stream_a, type); - nano::write (stream_a, static_cast (extensions.to_ullong ())); -} - -bool nano::message_header::deserialize (nano::stream & stream_a) -{ - auto error (false); - try - { - uint16_t network_bytes; - nano::read (stream_a, network_bytes); - network = static_cast (boost::endian::big_to_native (network_bytes)); - nano::read (stream_a, version_max); - nano::read (stream_a, version_using); - nano::read (stream_a, version_min); - nano::read (stream_a, type); - uint16_t extensions_l; - nano::read (stream_a, extensions_l); - extensions = extensions_l; - } - catch (std::runtime_error const &) - { - error = true; - } - - return error; -} - -std::string nano::message_type_to_string (nano::message_type message_type_l) -{ - switch (message_type_l) - { - case nano::message_type::invalid: - return "invalid"; - case nano::message_type::not_a_type: - return "not_a_type"; - case nano::message_type::keepalive: - return "keepalive"; - case nano::message_type::publish: - return "publish"; - case nano::message_type::confirm_req: - return "confirm_req"; - case nano::message_type::confirm_ack: - return "confirm_ack"; - case nano::message_type::bulk_pull: - return "bulk_pull"; - case nano::message_type::bulk_push: - return "bulk_push"; - case nano::message_type::frontier_req: - return "frontier_req"; - case nano::message_type::node_id_handshake: - return "node_id_handshake"; - case nano::message_type::bulk_pull_account: - return "bulk_pull_account"; - case nano::message_type::telemetry_req: - return "telemetry_req"; - case nano::message_type::telemetry_ack: - return "telemetry_ack"; - // default case intentionally omitted to cause warnings for unhandled enums - } - - return "n/a"; -} - -nano::stat::detail nano::message_type_to_stat_detail (nano::message_type message_type) -{ - switch (message_type) - { - case nano::message_type::invalid: - return nano::stat::detail::invalid; - case nano::message_type::not_a_type: - return nano::stat::detail::not_a_type; - case nano::message_type::keepalive: - return nano::stat::detail::keepalive; - case nano::message_type::publish: - return nano::stat::detail::publish; - case nano::message_type::confirm_req: - return nano::stat::detail::confirm_req; - case nano::message_type::confirm_ack: - return nano::stat::detail::confirm_ack; - case nano::message_type::bulk_pull: - return nano::stat::detail::bulk_pull; - case nano::message_type::bulk_push: - return nano::stat::detail::bulk_push; - case nano::message_type::frontier_req: - return nano::stat::detail::frontier_req; - case nano::message_type::node_id_handshake: - return nano::stat::detail::node_id_handshake; - case nano::message_type::bulk_pull_account: - return nano::stat::detail::bulk_pull_account; - case nano::message_type::telemetry_req: - return nano::stat::detail::telemetry_req; - case nano::message_type::telemetry_ack: - return nano::stat::detail::telemetry_ack; - // default case intentionally omitted to cause warnings for unhandled enums - } - debug_assert (false); - return {}; -} - -std::string nano::message_header::to_string () -{ - // Cast to uint16_t to get integer value since uint8_t is treated as an unsigned char in string formatting. - uint16_t type_l = static_cast (type); - uint16_t version_max_l = static_cast (version_max); - uint16_t version_using_l = static_cast (version_using); - uint16_t version_min_l = static_cast (version_min); - std::string type_text = nano::message_type_to_string (type); - - std::stringstream stream; - - stream << boost::format ("NetID: %1%(%2%), ") % nano::to_string_hex (static_cast (network)) % nano::network::to_string (network); - stream << boost::format ("VerMaxUsingMin: %1%/%2%/%3%, ") % version_max_l % version_using_l % version_min_l; - stream << boost::format ("MsgType: %1%(%2%), ") % type_l % type_text; - stream << boost::format ("Extensions: %1%") % nano::to_string_hex (static_cast (extensions.to_ulong ())); - - return stream.str (); -} - -nano::message::message (nano::network_constants const & constants, nano::message_type type_a) : - header (constants, type_a) -{ -} - -nano::message::message (nano::message_header const & header_a) : - header (header_a) -{ -} - -std::shared_ptr> nano::message::to_bytes () const -{ - auto bytes = std::make_shared> (); - nano::vectorstream stream (*bytes); - serialize (stream); - return bytes; -} - -nano::shared_const_buffer nano::message::to_shared_const_buffer () const -{ - return shared_const_buffer (to_bytes ()); -} - -nano::block_type nano::message_header::block_type () const -{ - return static_cast (((extensions & block_type_mask) >> 8).to_ullong ()); -} - -void nano::message_header::block_type_set (nano::block_type type_a) -{ - extensions &= ~block_type_mask; - extensions |= std::bitset<16> (static_cast (type_a) << 8); -} - -uint8_t nano::message_header::count_get () const -{ - return static_cast (((extensions & count_mask) >> 12).to_ullong ()); -} - -void nano::message_header::count_set (uint8_t count_a) -{ - debug_assert (count_a < 16); - extensions &= ~count_mask; - extensions |= std::bitset<16> (static_cast (count_a) << 12); -} - -void nano::message_header::flag_set (uint8_t flag_a) -{ - // Flags from 8 are block_type & count - debug_assert (flag_a < 8); - extensions.set (flag_a, true); -} - -bool nano::message_header::bulk_pull_is_count_present () const -{ - auto result (false); - if (type == nano::message_type::bulk_pull) - { - if (extensions.test (bulk_pull_count_present_flag)) - { - result = true; - } - } - return result; -} - -bool nano::message_header::bulk_pull_ascending () const -{ - auto result (false); - if (type == nano::message_type::bulk_pull) - { - if (extensions.test (bulk_pull_ascending_flag)) - { - result = true; - } - } - return result; -} - -bool nano::message_header::frontier_req_is_only_confirmed_present () const -{ - auto result (false); - if (type == nano::message_type::frontier_req) - { - if (extensions.test (frontier_req_only_confirmed)) - { - result = true; - } - } - return result; -} - -bool nano::message_header::node_id_handshake_is_query () const -{ - auto result (false); - if (type == nano::message_type::node_id_handshake) - { - if (extensions.test (node_id_handshake_query_flag)) - { - result = true; - } - } - return result; -} - -bool nano::message_header::node_id_handshake_is_response () const -{ - auto result (false); - if (type == nano::message_type::node_id_handshake) - { - if (extensions.test (node_id_handshake_response_flag)) - { - result = true; - } - } - return result; -} - -std::size_t nano::message_header::payload_length_bytes () const -{ - switch (type) - { - case nano::message_type::bulk_pull: - { - return nano::bulk_pull::size + (bulk_pull_is_count_present () ? nano::bulk_pull::extended_parameters_size : 0); - } - case nano::message_type::bulk_push: - case nano::message_type::telemetry_req: - { - // These don't have a payload - return 0; - } - case nano::message_type::frontier_req: - { - return nano::frontier_req::size; - } - case nano::message_type::bulk_pull_account: - { - return nano::bulk_pull_account::size; - } - case nano::message_type::keepalive: - { - return nano::keepalive::size; - } - case nano::message_type::publish: - { - return nano::block::size (block_type ()); - } - case nano::message_type::confirm_ack: - { - return nano::confirm_ack::size (count_get ()); - } - case nano::message_type::confirm_req: - { - return nano::confirm_req::size (block_type (), count_get ()); - } - case nano::message_type::node_id_handshake: - { - return nano::node_id_handshake::size (*this); - } - case nano::message_type::telemetry_ack: - { - return nano::telemetry_ack::size (*this); - } - default: - { - debug_assert (false); - return 0; - } - } -} - -bool nano::message_header::is_valid_message_type () const -{ - switch (type) - { - case nano::message_type::bulk_pull: - case nano::message_type::bulk_push: - case nano::message_type::telemetry_req: - case nano::message_type::frontier_req: - case nano::message_type::bulk_pull_account: - case nano::message_type::keepalive: - case nano::message_type::publish: - case nano::message_type::confirm_ack: - case nano::message_type::confirm_req: - case nano::message_type::node_id_handshake: - case nano::message_type::telemetry_ack: - { - return true; - } - default: - { - return false; - } - } -} - -// MTU - IP header - UDP header -std::size_t const nano::message_parser::max_safe_udp_message_size = 508; - -std::string nano::message_parser::status_string () -{ - switch (status) - { - case nano::message_parser::parse_status::success: - { - return "success"; - } - case nano::message_parser::parse_status::insufficient_work: - { - return "insufficient_work"; - } - case nano::message_parser::parse_status::invalid_header: - { - return "invalid_header"; - } - case nano::message_parser::parse_status::invalid_message_type: - { - return "invalid_message_type"; - } - case nano::message_parser::parse_status::invalid_keepalive_message: - { - return "invalid_keepalive_message"; - } - case nano::message_parser::parse_status::invalid_publish_message: - { - return "invalid_publish_message"; - } - case nano::message_parser::parse_status::invalid_confirm_req_message: - { - return "invalid_confirm_req_message"; - } - case nano::message_parser::parse_status::invalid_confirm_ack_message: - { - return "invalid_confirm_ack_message"; - } - case nano::message_parser::parse_status::invalid_node_id_handshake_message: - { - return "invalid_node_id_handshake_message"; - } - case nano::message_parser::parse_status::invalid_telemetry_req_message: - { - return "invalid_telemetry_req_message"; - } - case nano::message_parser::parse_status::invalid_telemetry_ack_message: - { - return "invalid_telemetry_ack_message"; - } - case nano::message_parser::parse_status::outdated_version: - { - return "outdated_version"; - } - case nano::message_parser::parse_status::duplicate_publish_message: - { - return "duplicate_publish_message"; - } - } - - debug_assert (false); - - 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::network_constants const & network) : - 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), - network{ network } -{ -} - -void nano::message_parser::deserialize_buffer (uint8_t const * buffer_a, std::size_t size_a) -{ - status = parse_status::success; - auto error (false); - if (size_a <= max_safe_udp_message_size) - { - // Guaranteed to be deliverable - nano::bufferstream stream (buffer_a, size_a); - nano::message_header header (error, stream); - if (!error) - { - if (header.network != network.current_network) - { - status = parse_status::invalid_header; - return; - } - - if (header.version_using < network.protocol_version_min) - { - status = parse_status::outdated_version; - } - else - { - switch (header.type) - { - case nano::message_type::keepalive: - { - deserialize_keepalive (stream, header); - break; - } - case nano::message_type::publish: - { - nano::uint128_t digest; - if (!publish_filter.apply (buffer_a + header.size, size_a - header.size, &digest)) - { - deserialize_publish (stream, header, digest); - } - else - { - status = parse_status::duplicate_publish_message; - } - break; - } - case nano::message_type::confirm_req: - { - deserialize_confirm_req (stream, header); - break; - } - case nano::message_type::confirm_ack: - { - deserialize_confirm_ack (stream, header); - break; - } - case nano::message_type::node_id_handshake: - { - deserialize_node_id_handshake (stream, header); - break; - } - case nano::message_type::telemetry_req: - { - deserialize_telemetry_req (stream, header); - break; - } - case nano::message_type::telemetry_ack: - { - deserialize_telemetry_ack (stream, header); - break; - } - default: - { - status = parse_status::invalid_message_type; - break; - } - } - } - } - else - { - status = parse_status::invalid_header; - } - } -} - -void nano::message_parser::deserialize_keepalive (nano::stream & stream_a, nano::message_header const & header_a) -{ - auto error (false); - nano::keepalive incoming (error, stream_a, header_a); - if (!error && at_end (stream_a)) - { - visitor.keepalive (incoming); - } - else - { - status = parse_status::invalid_keepalive_message; - } -} - -void nano::message_parser::deserialize_publish (nano::stream & stream_a, nano::message_header const & header_a, nano::uint128_t const & digest_a) -{ - auto error (false); - nano::publish incoming (error, stream_a, header_a, digest_a, &block_uniquer); - if (!error && at_end (stream_a)) - { - if (!network.work.validate_entry (*incoming.block)) - { - visitor.publish (incoming); - } - else - { - status = parse_status::insufficient_work; - } - } - else - { - status = parse_status::invalid_publish_message; - } -} - -void nano::message_parser::deserialize_confirm_req (nano::stream & stream_a, nano::message_header const & header_a) -{ - auto error (false); - nano::confirm_req incoming (error, stream_a, header_a, &block_uniquer); - if (!error && at_end (stream_a)) - { - if (incoming.block == nullptr || !network.work.validate_entry (*incoming.block)) - { - visitor.confirm_req (incoming); - } - else - { - status = parse_status::insufficient_work; - } - } - else - { - status = parse_status::invalid_confirm_req_message; - } -} - -void nano::message_parser::deserialize_confirm_ack (nano::stream & stream_a, nano::message_header const & header_a) -{ - auto error (false); - nano::confirm_ack incoming (error, stream_a, header_a, &vote_uniquer); - if (!error && at_end (stream_a)) - { - visitor.confirm_ack (incoming); - } - else - { - status = parse_status::invalid_confirm_ack_message; - } -} - -void nano::message_parser::deserialize_node_id_handshake (nano::stream & stream_a, nano::message_header const & header_a) -{ - bool error_l (false); - nano::node_id_handshake incoming (error_l, stream_a, header_a); - if (!error_l && at_end (stream_a)) - { - visitor.node_id_handshake (incoming); - } - else - { - status = parse_status::invalid_node_id_handshake_message; - } -} - -void nano::message_parser::deserialize_telemetry_req (nano::stream & stream_a, nano::message_header const & header_a) -{ - nano::telemetry_req incoming (header_a); - if (at_end (stream_a)) - { - visitor.telemetry_req (incoming); - } - else - { - status = parse_status::invalid_telemetry_req_message; - } -} - -void nano::message_parser::deserialize_telemetry_ack (nano::stream & stream_a, nano::message_header const & header_a) -{ - bool error_l (false); - nano::telemetry_ack incoming (error_l, stream_a, header_a); - // Intentionally not checking if at the end of stream, because these messages support backwards/forwards compatibility - if (!error_l) - { - visitor.telemetry_ack (incoming); - } - else - { - status = parse_status::invalid_telemetry_ack_message; - } -} - -bool nano::message_parser::at_end (nano::stream & stream_a) -{ - uint8_t junk; - auto end (nano::try_read (stream_a, junk)); - return end; -} - -nano::keepalive::keepalive (nano::network_constants const & constants) : - message (constants, nano::message_type::keepalive) -{ - nano::endpoint endpoint (boost::asio::ip::address_v6{}, 0); - for (auto i (peers.begin ()), n (peers.end ()); i != n; ++i) - { - *i = endpoint; - } -} - -nano::keepalive::keepalive (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : - message (header_a) -{ - if (!error_a) - { - error_a = deserialize (stream_a); - } -} - -void nano::keepalive::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.keepalive (*this); -} - -void nano::keepalive::serialize (nano::stream & stream_a) const -{ - header.serialize (stream_a); - for (auto i (peers.begin ()), j (peers.end ()); i != j; ++i) - { - debug_assert (i->address ().is_v6 ()); - auto bytes (i->address ().to_v6 ().to_bytes ()); - write (stream_a, bytes); - write (stream_a, i->port ()); - } -} - -bool nano::keepalive::deserialize (nano::stream & stream_a) -{ - debug_assert (header.type == nano::message_type::keepalive); - auto error (false); - for (auto i (peers.begin ()), j (peers.end ()); i != j && !error; ++i) - { - std::array address; - uint16_t port; - if (!try_read (stream_a, address) && !try_read (stream_a, port)) - { - *i = nano::endpoint (boost::asio::ip::address_v6 (address), port); - } - else - { - error = true; - } - } - return error; -} - -bool nano::keepalive::operator== (nano::keepalive const & other_a) const -{ - return peers == other_a.peers; -} - -nano::publish::publish (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::uint128_t const & digest_a, nano::block_uniquer * uniquer_a) : - message (header_a), - digest (digest_a) -{ - if (!error_a) - { - error_a = deserialize (stream_a, uniquer_a); - } -} - -nano::publish::publish (nano::network_constants const & constants, std::shared_ptr const & block_a) : - message (constants, nano::message_type::publish), - block (block_a) -{ - header.block_type_set (block->type ()); -} - -void nano::publish::serialize (nano::stream & stream_a) const -{ - debug_assert (block != nullptr); - header.serialize (stream_a); - block->serialize (stream_a); -} - -bool nano::publish::deserialize (nano::stream & stream_a, nano::block_uniquer * uniquer_a) -{ - debug_assert (header.type == nano::message_type::publish); - block = nano::deserialize_block (stream_a, header.block_type (), uniquer_a); - auto result (block == nullptr); - return result; -} - -void nano::publish::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.publish (*this); -} - -bool nano::publish::operator== (nano::publish const & other_a) const -{ - return *block == *other_a.block; -} - -nano::confirm_req::confirm_req (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::block_uniquer * uniquer_a) : - message (header_a) -{ - if (!error_a) - { - error_a = deserialize (stream_a, uniquer_a); - } -} - -nano::confirm_req::confirm_req (nano::network_constants const & constants, std::shared_ptr const & block_a) : - message (constants, nano::message_type::confirm_req), - block (block_a) -{ - header.block_type_set (block->type ()); -} - -nano::confirm_req::confirm_req (nano::network_constants const & constants, std::vector> const & roots_hashes_a) : - message (constants, nano::message_type::confirm_req), - roots_hashes (roots_hashes_a) -{ - // not_a_block (1) block type for hashes + roots request - header.block_type_set (nano::block_type::not_a_block); - debug_assert (roots_hashes.size () < 16); - header.count_set (static_cast (roots_hashes.size ())); -} - -nano::confirm_req::confirm_req (nano::network_constants const & constants, nano::block_hash const & hash_a, nano::root const & root_a) : - message (constants, nano::message_type::confirm_req), - roots_hashes (std::vector> (1, std::make_pair (hash_a, root_a))) -{ - debug_assert (!roots_hashes.empty ()); - // not_a_block (1) block type for hashes + roots request - header.block_type_set (nano::block_type::not_a_block); - debug_assert (roots_hashes.size () < 16); - header.count_set (static_cast (roots_hashes.size ())); -} - -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 -{ - header.serialize (stream_a); - if (header.block_type () == nano::block_type::not_a_block) - { - debug_assert (!roots_hashes.empty ()); - // Write hashes & roots - for (auto & root_hash : roots_hashes) - { - write (stream_a, root_hash.first); - write (stream_a, root_hash.second); - } - } - else - { - debug_assert (block != nullptr); - block->serialize (stream_a); - } -} - -bool nano::confirm_req::deserialize (nano::stream & stream_a, nano::block_uniquer * uniquer_a) -{ - bool result (false); - debug_assert (header.type == nano::message_type::confirm_req); - try - { - if (header.block_type () == nano::block_type::not_a_block) - { - uint8_t count (header.count_get ()); - for (auto i (0); i != count && !result; ++i) - { - nano::block_hash block_hash (0); - nano::block_hash root (0); - read (stream_a, block_hash); - read (stream_a, root); - if (!block_hash.is_zero () || !root.is_zero ()) - { - roots_hashes.emplace_back (block_hash, root); - } - } - - result = roots_hashes.empty () || (roots_hashes.size () != count); - } - else - { - block = nano::deserialize_block (stream_a, header.block_type (), uniquer_a); - result = block == nullptr; - } - } - catch (std::runtime_error const &) - { - result = true; - } - - return result; -} - -bool nano::confirm_req::operator== (nano::confirm_req const & other_a) const -{ - bool equal (false); - if (block != nullptr && other_a.block != nullptr) - { - equal = *block == *other_a.block; - } - else if (!roots_hashes.empty () && !other_a.roots_hashes.empty ()) - { - equal = roots_hashes == other_a.roots_hashes; - } - return equal; -} - -std::string nano::confirm_req::roots_string () const -{ - std::string result; - for (auto & root_hash : roots_hashes) - { - result += root_hash.first.to_string (); - result += ":"; - result += root_hash.second.to_string (); - result += ", "; - } - return result; -} - -std::size_t nano::confirm_req::size (nano::block_type type_a, std::size_t count) -{ - std::size_t result (0); - if (type_a != nano::block_type::invalid && type_a != nano::block_type::not_a_block) - { - result = nano::block::size (type_a); - } - else if (type_a == nano::block_type::not_a_block) - { - result = count * (sizeof (nano::uint256_union) + sizeof (nano::block_hash)); - } - return result; -} - -nano::confirm_ack::confirm_ack (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::vote_uniquer * uniquer_a) : - message (header_a), - vote (nano::make_shared (error_a, stream_a)) -{ - if (!error_a && uniquer_a) - { - vote = uniquer_a->unique (vote); - } -} - -nano::confirm_ack::confirm_ack (nano::network_constants const & constants, std::shared_ptr const & vote_a) : - message (constants, nano::message_type::confirm_ack), - vote (vote_a) -{ - header.block_type_set (nano::block_type::not_a_block); - debug_assert (vote_a->hashes.size () < 16); - header.count_set (static_cast (vote_a->hashes.size ())); -} - -void nano::confirm_ack::serialize (nano::stream & stream_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); - vote->serialize (stream_a); -} - -bool nano::confirm_ack::operator== (nano::confirm_ack const & other_a) const -{ - auto result (*vote == *other_a.vote); - return result; -} - -void nano::confirm_ack::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.confirm_ack (*this); -} - -std::size_t nano::confirm_ack::size (std::size_t count) -{ - std::size_t result = sizeof (nano::account) + sizeof (nano::signature) + sizeof (uint64_t) + count * sizeof (nano::block_hash); - return result; -} - -nano::frontier_req::frontier_req (nano::network_constants const & constants) : - message (constants, nano::message_type::frontier_req) -{ -} - -nano::frontier_req::frontier_req (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : - message (header_a) -{ - if (!error_a) - { - error_a = deserialize (stream_a); - } -} - -void nano::frontier_req::serialize (nano::stream & stream_a) const -{ - header.serialize (stream_a); - write (stream_a, start.bytes); - write (stream_a, age); - write (stream_a, count); -} - -bool nano::frontier_req::deserialize (nano::stream & stream_a) -{ - debug_assert (header.type == nano::message_type::frontier_req); - auto error (false); - try - { - nano::read (stream_a, start.bytes); - nano::read (stream_a, age); - nano::read (stream_a, count); - } - catch (std::runtime_error const &) - { - error = true; - } - - return error; -} - -void nano::frontier_req::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.frontier_req (*this); -} - -bool nano::frontier_req::operator== (nano::frontier_req const & other_a) const -{ - return start == other_a.start && age == other_a.age && count == other_a.count; -} - -nano::bulk_pull::bulk_pull (nano::network_constants const & constants) : - message (constants, nano::message_type::bulk_pull) -{ -} - -nano::bulk_pull::bulk_pull (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : - message (header_a) -{ - if (!error_a) - { - error_a = deserialize (stream_a); - } -} - -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 -{ - /* - * Ensure the "count_present" flag is set if there - * is a limit specifed. Additionally, do not allow - * the "count_present" flag with a value of 0, since - * that is a sentinel which we use to mean "all blocks" - * and that is the behavior of not having the flag set - * so it is wasteful to do this. - */ - debug_assert ((count == 0 && !is_count_present ()) || (count != 0 && is_count_present ())); - - header.serialize (stream_a); - write (stream_a, start); - write (stream_a, end); - - if (is_count_present ()) - { - std::array count_buffer{ { 0 } }; - decltype (count) count_little_endian; - static_assert (sizeof (count_little_endian) < (count_buffer.size () - 1), "count must fit within buffer"); - - count_little_endian = boost::endian::native_to_little (count); - memcpy (count_buffer.data () + 1, &count_little_endian, sizeof (count_little_endian)); - - write (stream_a, count_buffer); - } -} - -bool nano::bulk_pull::deserialize (nano::stream & stream_a) -{ - debug_assert (header.type == nano::message_type::bulk_pull); - auto error (false); - try - { - nano::read (stream_a, start); - nano::read (stream_a, end); - - if (is_count_present ()) - { - std::array extended_parameters_buffers; - static_assert (sizeof (count) < (extended_parameters_buffers.size () - 1), "count must fit within buffer"); - - nano::read (stream_a, extended_parameters_buffers); - if (extended_parameters_buffers.front () != 0) - { - error = true; - } - else - { - memcpy (&count, extended_parameters_buffers.data () + 1, sizeof (count)); - boost::endian::little_to_native_inplace (count); - } - } - else - { - count = 0; - } - } - catch (std::runtime_error const &) - { - error = true; - } - - return error; -} - -bool nano::bulk_pull::is_count_present () const -{ - return header.extensions.test (count_present_flag); -} - -void nano::bulk_pull::set_count_present (bool value_a) -{ - header.extensions.set (count_present_flag, value_a); -} - -nano::bulk_pull_account::bulk_pull_account (nano::network_constants const & constants) : - message (constants, nano::message_type::bulk_pull_account) -{ -} - -nano::bulk_pull_account::bulk_pull_account (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : - message (header_a) -{ - if (!error_a) - { - error_a = deserialize (stream_a); - } -} - -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 -{ - header.serialize (stream_a); - write (stream_a, account); - write (stream_a, minimum_amount); - write (stream_a, flags); -} - -bool nano::bulk_pull_account::deserialize (nano::stream & stream_a) -{ - debug_assert (header.type == nano::message_type::bulk_pull_account); - auto error (false); - try - { - nano::read (stream_a, account); - nano::read (stream_a, minimum_amount); - nano::read (stream_a, flags); - } - catch (std::runtime_error const &) - { - error = true; - } - - return error; -} - -nano::bulk_push::bulk_push (nano::network_constants const & constants) : - message (constants, nano::message_type::bulk_push) -{ -} - -nano::bulk_push::bulk_push (nano::message_header const & header_a) : - message (header_a) -{ -} - -bool nano::bulk_push::deserialize (nano::stream & stream_a) -{ - debug_assert (header.type == nano::message_type::bulk_push); - return false; -} - -void nano::bulk_push::serialize (nano::stream & stream_a) const -{ - header.serialize (stream_a); -} - -void nano::bulk_push::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.bulk_push (*this); -} - -nano::telemetry_req::telemetry_req (nano::network_constants const & constants) : - message (constants, nano::message_type::telemetry_req) -{ -} - -nano::telemetry_req::telemetry_req (nano::message_header const & header_a) : - message (header_a) -{ -} - -bool nano::telemetry_req::deserialize (nano::stream & stream_a) -{ - debug_assert (header.type == nano::message_type::telemetry_req); - return false; -} - -void nano::telemetry_req::serialize (nano::stream & stream_a) const -{ - header.serialize (stream_a); -} - -void nano::telemetry_req::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.telemetry_req (*this); -} - -nano::telemetry_ack::telemetry_ack (nano::network_constants const & constants) : - message (constants, nano::message_type::telemetry_ack) -{ -} - -nano::telemetry_ack::telemetry_ack (bool & error_a, nano::stream & stream_a, nano::message_header const & message_header) : - message (message_header) -{ - if (!error_a) - { - error_a = deserialize (stream_a); - } -} - -nano::telemetry_ack::telemetry_ack (nano::network_constants const & constants, nano::telemetry_data const & telemetry_data_a) : - message (constants, nano::message_type::telemetry_ack), - data (telemetry_data_a) -{ - debug_assert (telemetry_data::size + telemetry_data_a.unknown_data.size () <= message_header::telemetry_size_mask.to_ulong ()); // Maximum size the mask allows - header.extensions &= ~message_header::telemetry_size_mask; - header.extensions |= std::bitset<16> (static_cast (telemetry_data::size) + telemetry_data_a.unknown_data.size ()); -} - -void nano::telemetry_ack::serialize (nano::stream & stream_a) const -{ - header.serialize (stream_a); - if (!is_empty_payload ()) - { - data.serialize (stream_a); - } -} - -bool nano::telemetry_ack::deserialize (nano::stream & stream_a) -{ - auto error (false); - debug_assert (header.type == nano::message_type::telemetry_ack); - try - { - if (!is_empty_payload ()) - { - data.deserialize (stream_a, nano::narrow_cast (header.extensions.to_ulong ())); - } - } - catch (std::runtime_error const &) - { - error = true; - } - - return error; -} - -void nano::telemetry_ack::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.telemetry_ack (*this); -} - -uint16_t nano::telemetry_ack::size () const -{ - return size (header); -} - -uint16_t nano::telemetry_ack::size (nano::message_header const & message_header_a) -{ - return static_cast ((message_header_a.extensions & message_header::telemetry_size_mask).to_ullong ()); -} - -bool nano::telemetry_ack::is_empty_payload () const -{ - return size () == 0; -} - -void nano::telemetry_data::deserialize (nano::stream & stream_a, uint16_t payload_length_a) -{ - read (stream_a, signature); - read (stream_a, node_id); - read (stream_a, block_count); - boost::endian::big_to_native_inplace (block_count); - read (stream_a, cemented_count); - boost::endian::big_to_native_inplace (cemented_count); - read (stream_a, unchecked_count); - boost::endian::big_to_native_inplace (unchecked_count); - read (stream_a, account_count); - boost::endian::big_to_native_inplace (account_count); - read (stream_a, bandwidth_cap); - boost::endian::big_to_native_inplace (bandwidth_cap); - read (stream_a, peer_count); - boost::endian::big_to_native_inplace (peer_count); - read (stream_a, protocol_version); - read (stream_a, uptime); - boost::endian::big_to_native_inplace (uptime); - read (stream_a, genesis_block.bytes); - read (stream_a, major_version); - read (stream_a, minor_version); - read (stream_a, patch_version); - read (stream_a, pre_release_version); - read (stream_a, maker); - - uint64_t timestamp_l; - read (stream_a, timestamp_l); - boost::endian::big_to_native_inplace (timestamp_l); - timestamp = std::chrono::system_clock::time_point (std::chrono::milliseconds (timestamp_l)); - read (stream_a, active_difficulty); - boost::endian::big_to_native_inplace (active_difficulty); - if (payload_length_a > latest_size) - { - read (stream_a, unknown_data, payload_length_a - latest_size); - } -} - -void nano::telemetry_data::serialize_without_signature (nano::stream & stream_a) const -{ - // All values should be serialized in big endian - write (stream_a, node_id); - write (stream_a, boost::endian::native_to_big (block_count)); - write (stream_a, boost::endian::native_to_big (cemented_count)); - write (stream_a, boost::endian::native_to_big (unchecked_count)); - write (stream_a, boost::endian::native_to_big (account_count)); - write (stream_a, boost::endian::native_to_big (bandwidth_cap)); - write (stream_a, boost::endian::native_to_big (peer_count)); - write (stream_a, protocol_version); - write (stream_a, boost::endian::native_to_big (uptime)); - write (stream_a, genesis_block.bytes); - write (stream_a, major_version); - write (stream_a, minor_version); - write (stream_a, patch_version); - write (stream_a, pre_release_version); - write (stream_a, maker); - write (stream_a, boost::endian::native_to_big (std::chrono::duration_cast (timestamp.time_since_epoch ()).count ())); - write (stream_a, boost::endian::native_to_big (active_difficulty)); - write (stream_a, unknown_data); -} - -void nano::telemetry_data::serialize (nano::stream & stream_a) const -{ - write (stream_a, signature); - serialize_without_signature (stream_a); -} - -nano::error nano::telemetry_data::serialize_json (nano::jsonconfig & json, bool ignore_identification_metrics_a) const -{ - json.put ("block_count", block_count); - json.put ("cemented_count", cemented_count); - json.put ("unchecked_count", unchecked_count); - json.put ("account_count", account_count); - json.put ("bandwidth_cap", bandwidth_cap); - json.put ("peer_count", peer_count); - json.put ("protocol_version", protocol_version); - json.put ("uptime", uptime); - json.put ("genesis_block", genesis_block.to_string ()); - json.put ("major_version", major_version); - json.put ("minor_version", minor_version); - json.put ("patch_version", patch_version); - json.put ("pre_release_version", pre_release_version); - json.put ("maker", maker); - json.put ("timestamp", std::chrono::duration_cast (timestamp.time_since_epoch ()).count ()); - json.put ("active_difficulty", nano::to_string_hex (active_difficulty)); - // Keep these last for UI purposes - if (!ignore_identification_metrics_a) - { - json.put ("node_id", node_id.to_node_id ()); - json.put ("signature", signature.to_string ()); - } - return json.get_error (); -} - -nano::error nano::telemetry_data::deserialize_json (nano::jsonconfig & json, bool ignore_identification_metrics_a) -{ - if (!ignore_identification_metrics_a) - { - std::string signature_l; - json.get ("signature", signature_l); - if (!json.get_error ()) - { - if (signature.decode_hex (signature_l)) - { - json.get_error ().set ("Could not deserialize signature"); - } - } - - std::string node_id_l; - json.get ("node_id", node_id_l); - if (!json.get_error ()) - { - if (node_id.decode_node_id (node_id_l)) - { - json.get_error ().set ("Could not deserialize node id"); - } - } - } - - json.get ("block_count", block_count); - json.get ("cemented_count", cemented_count); - json.get ("unchecked_count", unchecked_count); - json.get ("account_count", account_count); - json.get ("bandwidth_cap", bandwidth_cap); - json.get ("peer_count", peer_count); - json.get ("protocol_version", protocol_version); - json.get ("uptime", uptime); - std::string genesis_block_l; - json.get ("genesis_block", genesis_block_l); - if (!json.get_error ()) - { - if (genesis_block.decode_hex (genesis_block_l)) - { - json.get_error ().set ("Could not deserialize genesis block"); - } - } - json.get ("major_version", major_version); - json.get ("minor_version", minor_version); - json.get ("patch_version", patch_version); - json.get ("pre_release_version", pre_release_version); - json.get ("maker", maker); - auto timestamp_l = json.get ("timestamp"); - timestamp = std::chrono::system_clock::time_point (std::chrono::milliseconds (timestamp_l)); - auto current_active_difficulty_text = json.get ("active_difficulty"); - auto ec = nano::from_string_hex (current_active_difficulty_text, active_difficulty); - debug_assert (!ec); - return json.get_error (); -} - -std::string nano::telemetry_data::to_string () const -{ - nano::jsonconfig jc; - serialize_json (jc, true); - std::stringstream ss; - jc.write (ss); - return ss.str (); -} - -bool nano::telemetry_data::operator== (nano::telemetry_data const & data_a) const -{ - return (signature == data_a.signature && node_id == data_a.node_id && block_count == data_a.block_count && cemented_count == data_a.cemented_count && unchecked_count == data_a.unchecked_count && account_count == data_a.account_count && bandwidth_cap == data_a.bandwidth_cap && uptime == data_a.uptime && peer_count == data_a.peer_count && protocol_version == data_a.protocol_version && genesis_block == data_a.genesis_block && major_version == data_a.major_version && minor_version == data_a.minor_version && patch_version == data_a.patch_version && pre_release_version == data_a.pre_release_version && maker == data_a.maker && timestamp == data_a.timestamp && active_difficulty == data_a.active_difficulty && unknown_data == data_a.unknown_data); -} - -bool nano::telemetry_data::operator!= (nano::telemetry_data const & data_a) const -{ - return !(*this == data_a); -} - -void nano::telemetry_data::sign (nano::keypair const & node_id_a) -{ - debug_assert (node_id == node_id_a.pub); - std::vector bytes; - { - nano::vectorstream stream (bytes); - serialize_without_signature (stream); - } - - signature = nano::sign_message (node_id_a.prv, node_id_a.pub, bytes.data (), bytes.size ()); -} - -bool nano::telemetry_data::validate_signature () const -{ - std::vector bytes; - { - nano::vectorstream stream (bytes); - serialize_without_signature (stream); - } - - return nano::validate_message (node_id, bytes.data (), bytes.size (), signature); -} - -nano::node_id_handshake::node_id_handshake (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : - message (header_a), - query (boost::none), - response (boost::none) -{ - error_a = deserialize (stream_a); -} - -nano::node_id_handshake::node_id_handshake (nano::network_constants const & constants, boost::optional query, boost::optional> response) : - message (constants, nano::message_type::node_id_handshake), - query (query), - response (response) -{ - if (query) - { - header.flag_set (nano::message_header::node_id_handshake_query_flag); - } - if (response) - { - header.flag_set (nano::message_header::node_id_handshake_response_flag); - } -} - -void nano::node_id_handshake::serialize (nano::stream & stream_a) const -{ - header.serialize (stream_a); - if (query) - { - write (stream_a, *query); - } - if (response) - { - write (stream_a, response->first); - write (stream_a, response->second); - } -} - -bool nano::node_id_handshake::deserialize (nano::stream & stream_a) -{ - debug_assert (header.type == nano::message_type::node_id_handshake); - auto error (false); - try - { - if (header.node_id_handshake_is_query ()) - { - nano::uint256_union query_hash; - read (stream_a, query_hash); - query = query_hash; - } - - if (header.node_id_handshake_is_response ()) - { - nano::account response_account; - read (stream_a, response_account); - nano::signature response_signature; - read (stream_a, response_signature); - response = std::make_pair (response_account, response_signature); - } - } - catch (std::runtime_error const &) - { - error = true; - } - - return error; -} - -bool nano::node_id_handshake::operator== (nano::node_id_handshake const & other_a) const -{ - auto result (*query == *other_a.query && *response == *other_a.response); - return result; -} - -void nano::node_id_handshake::visit (nano::message_visitor & visitor_a) const -{ - visitor_a.node_id_handshake (*this); -} - -std::size_t nano::node_id_handshake::size () const -{ - return size (header); -} - -std::size_t nano::node_id_handshake::size (nano::message_header const & header_a) -{ - std::size_t result (0); - if (header_a.node_id_handshake_is_query ()) - { - result = sizeof (nano::uint256_union); - } - if (header_a.node_id_handshake_is_response ()) - { - result += sizeof (nano::account) + sizeof (nano::signature); - } - return result; -} - -nano::message_visitor::~message_visitor () -{ -} - bool nano::parse_port (std::string const & string_a, uint16_t & port_a) { bool result = false; diff --git a/nano/node/common.hpp b/nano/node/common.hpp index 4a7384d77..f3290dc46 100644 --- a/nano/node/common.hpp +++ b/nano/node/common.hpp @@ -168,388 +168,6 @@ struct hash namespace nano { -/** - * Message types are serialized to the network and existing values must thus never change as - * types are added, removed and reordered in the enum. - */ -enum class message_type : uint8_t -{ - invalid = 0x0, - not_a_type = 0x1, - keepalive = 0x2, - publish = 0x3, - confirm_req = 0x4, - confirm_ack = 0x5, - bulk_pull = 0x6, - bulk_push = 0x7, - frontier_req = 0x8, - /* deleted 0x9 */ - node_id_handshake = 0x0a, - bulk_pull_account = 0x0b, - telemetry_req = 0x0c, - telemetry_ack = 0x0d -}; - -std::string message_type_to_string (message_type); -stat::detail message_type_to_stat_detail (message_type); - -enum class bulk_pull_account_flags : uint8_t -{ - pending_hash_and_amount = 0x0, - pending_address_only = 0x1, - pending_hash_amount_and_address = 0x2 -}; - -class message_visitor; -class message_header final -{ -public: - message_header (nano::network_constants const &, nano::message_type); - message_header (bool &, nano::stream &); - void serialize (nano::stream &) const; - bool deserialize (nano::stream &); - nano::block_type block_type () const; - void block_type_set (nano::block_type); - uint8_t count_get () const; - void count_set (uint8_t); - nano::networks network; - uint8_t version_max; - uint8_t version_using; - uint8_t version_min; - std::string to_string (); - -public: - nano::message_type type; - std::bitset<16> extensions; - static std::size_t constexpr size = sizeof (nano::networks) + sizeof (version_max) + sizeof (version_using) + sizeof (version_min) + sizeof (type) + sizeof (/* extensions */ uint16_t); - - void flag_set (uint8_t); - static uint8_t constexpr bulk_pull_count_present_flag = 0; - static uint8_t constexpr bulk_pull_ascending_flag = 1; - bool bulk_pull_is_count_present () const; - bool bulk_pull_ascending () const; - static uint8_t constexpr frontier_req_only_confirmed = 1; - bool frontier_req_is_only_confirmed_present () const; - static uint8_t constexpr node_id_handshake_query_flag = 0; - static uint8_t constexpr node_id_handshake_response_flag = 1; - bool node_id_handshake_is_query () const; - bool node_id_handshake_is_response () const; - - /** Size of the payload in bytes. For some messages, the payload size is based on header flags. */ - std::size_t payload_length_bytes () const; - bool is_valid_message_type () const; - - static std::bitset<16> constexpr block_type_mask{ 0x0f00 }; - static std::bitset<16> constexpr count_mask{ 0xf000 }; - static std::bitset<16> constexpr telemetry_size_mask{ 0x3ff }; -}; - -class message -{ -public: - explicit message (nano::network_constants const &, nano::message_type); - explicit message (nano::message_header const &); - virtual ~message () = default; - virtual void serialize (nano::stream &) 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; - - nano::message_header header; -}; - -class work_pool; -class network_constants; -class message_parser final -{ -public: - enum class parse_status - { - success, - insufficient_work, - invalid_header, - invalid_message_type, - invalid_keepalive_message, - invalid_publish_message, - invalid_confirm_req_message, - invalid_confirm_ack_message, - invalid_node_id_handshake_message, - invalid_telemetry_req_message, - invalid_telemetry_ack_message, - outdated_version, - duplicate_publish_message - }; - message_parser (nano::network_filter &, nano::block_uniquer &, nano::vote_uniquer &, nano::message_visitor &, nano::work_pool &, nano::network_constants const & protocol); - void deserialize_buffer (uint8_t const *, std::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); - void deserialize_confirm_req (nano::stream &, nano::message_header const &); - void deserialize_confirm_ack (nano::stream &, nano::message_header const &); - void deserialize_node_id_handshake (nano::stream &, nano::message_header const &); - void deserialize_telemetry_req (nano::stream &, nano::message_header const &); - void deserialize_telemetry_ack (nano::stream &, nano::message_header const &); - bool at_end (nano::stream &); - nano::network_filter & publish_filter; - nano::block_uniquer & block_uniquer; - nano::vote_uniquer & vote_uniquer; - nano::message_visitor & visitor; - nano::work_pool & pool; - parse_status status; - nano::network_constants const & network; - std::string status_string (); - static std::size_t const max_safe_udp_message_size; -}; - -class keepalive final : public message -{ -public: - explicit keepalive (nano::network_constants const & constants); - keepalive (bool &, nano::stream &, nano::message_header const &); - void visit (nano::message_visitor &) const override; - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &); - bool operator== (nano::keepalive const &) const; - std::array peers; - static std::size_t constexpr size = 8 * (16 + 2); -}; - -class publish final : public message -{ -public: - publish (bool &, nano::stream &, nano::message_header const &, nano::uint128_t const & = 0, nano::block_uniquer * = nullptr); - publish (nano::network_constants const & constants, std::shared_ptr const &); - void visit (nano::message_visitor &) const override; - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &, nano::block_uniquer * = nullptr); - bool operator== (nano::publish const &) const; - std::shared_ptr block; - nano::uint128_t digest{ 0 }; -}; - -class confirm_req final : public message -{ -public: - confirm_req (bool &, nano::stream &, nano::message_header const &, nano::block_uniquer * = nullptr); - confirm_req (nano::network_constants const & constants, std::shared_ptr const &); - confirm_req (nano::network_constants const & constants, std::vector> const &); - confirm_req (nano::network_constants const & constants, nano::block_hash const &, nano::root const &); - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &, nano::block_uniquer * = nullptr); - void visit (nano::message_visitor &) const override; - bool operator== (nano::confirm_req const &) const; - std::shared_ptr block; - std::vector> roots_hashes; - std::string roots_string () const; - static std::size_t size (nano::block_type, std::size_t = 0); -}; - -class confirm_ack final : public message -{ -public: - confirm_ack (bool &, nano::stream &, nano::message_header const &, nano::vote_uniquer * = nullptr); - confirm_ack (nano::network_constants const & constants, std::shared_ptr const &); - void serialize (nano::stream &) const override; - void visit (nano::message_visitor &) const override; - bool operator== (nano::confirm_ack const &) const; - std::shared_ptr vote; - static std::size_t size (std::size_t count); -}; - -class frontier_req final : public message -{ -public: - explicit frontier_req (nano::network_constants const & constants); - frontier_req (bool &, nano::stream &, nano::message_header const &); - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &); - void visit (nano::message_visitor &) const override; - bool operator== (nano::frontier_req const &) const; - nano::account start; - uint32_t age; - uint32_t count; - static std::size_t constexpr size = sizeof (start) + sizeof (age) + sizeof (count); -}; - -enum class telemetry_maker : uint8_t -{ - nf_node = 0, - nf_pruned_node = 1 -}; - -class telemetry_data -{ -public: - nano::signature signature{ 0 }; - nano::account node_id{}; - uint64_t block_count{ 0 }; - uint64_t cemented_count{ 0 }; - uint64_t unchecked_count{ 0 }; - uint64_t account_count{ 0 }; - uint64_t bandwidth_cap{ 0 }; - uint64_t uptime{ 0 }; - uint32_t peer_count{ 0 }; - uint8_t protocol_version{ 0 }; - nano::block_hash genesis_block{ 0 }; - uint8_t major_version{ 0 }; - uint8_t minor_version{ 0 }; - uint8_t patch_version{ 0 }; - uint8_t pre_release_version{ 0 }; - uint8_t maker{ static_cast> (telemetry_maker::nf_node) }; // Where this telemetry information originated - std::chrono::system_clock::time_point timestamp; - uint64_t active_difficulty{ 0 }; - std::vector unknown_data; - - void serialize (nano::stream &) const; - void deserialize (nano::stream &, uint16_t); - nano::error serialize_json (nano::jsonconfig &, bool) const; - nano::error deserialize_json (nano::jsonconfig &, bool); - void sign (nano::keypair const &); - bool validate_signature () const; - bool operator== (nano::telemetry_data const &) const; - bool operator!= (nano::telemetry_data const &) const; - std::string to_string () const; - - // Size does not include unknown_data - static auto constexpr size = sizeof (signature) + sizeof (node_id) + sizeof (block_count) + sizeof (cemented_count) + sizeof (unchecked_count) + sizeof (account_count) + sizeof (bandwidth_cap) + sizeof (peer_count) + sizeof (protocol_version) + sizeof (uptime) + sizeof (genesis_block) + sizeof (major_version) + sizeof (minor_version) + sizeof (patch_version) + sizeof (pre_release_version) + sizeof (maker) + sizeof (uint64_t) + sizeof (active_difficulty); - static auto constexpr latest_size = size; // This needs to be updated for each new telemetry version -private: - void serialize_without_signature (nano::stream &) const; -}; - -class telemetry_req final : public message -{ -public: - explicit telemetry_req (nano::network_constants const & constants); - explicit telemetry_req (nano::message_header const &); - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &); - void visit (nano::message_visitor &) const override; -}; - -class telemetry_ack final : public message -{ -public: - explicit telemetry_ack (nano::network_constants const & constants); - telemetry_ack (bool &, nano::stream &, nano::message_header const &); - telemetry_ack (nano::network_constants const & constants, telemetry_data const &); - void serialize (nano::stream &) const override; - void visit (nano::message_visitor &) const override; - bool deserialize (nano::stream &); - uint16_t size () const; - bool is_empty_payload () const; - static uint16_t size (nano::message_header const &); - nano::telemetry_data data; -}; - -class bulk_pull final : public message -{ -public: - using count_t = uint32_t; - explicit bulk_pull (nano::network_constants const & constants); - bulk_pull (bool &, nano::stream &, nano::message_header const &); - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &); - void visit (nano::message_visitor &) const override; - nano::hash_or_account start{ 0 }; - nano::block_hash end{ 0 }; - count_t count{ 0 }; - bool is_count_present () const; - void set_count_present (bool); - static std::size_t constexpr count_present_flag = nano::message_header::bulk_pull_count_present_flag; - static std::size_t constexpr extended_parameters_size = 8; - static std::size_t constexpr size = sizeof (start) + sizeof (end); -}; - -class bulk_pull_account final : public message -{ -public: - explicit bulk_pull_account (nano::network_constants const & constants); - bulk_pull_account (bool &, nano::stream &, nano::message_header const &); - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &); - void visit (nano::message_visitor &) const override; - nano::account account; - nano::amount minimum_amount; - bulk_pull_account_flags flags; - static std::size_t constexpr size = sizeof (account) + sizeof (minimum_amount) + sizeof (bulk_pull_account_flags); -}; - -class bulk_push final : public message -{ -public: - explicit bulk_push (nano::network_constants const & constants); - explicit bulk_push (nano::message_header const &); - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &); - void visit (nano::message_visitor &) const override; -}; - -class node_id_handshake final : public message -{ -public: - node_id_handshake (bool &, nano::stream &, nano::message_header const &); - node_id_handshake (nano::network_constants const & constants, boost::optional, boost::optional>); - void serialize (nano::stream &) const override; - bool deserialize (nano::stream &); - void visit (nano::message_visitor &) const override; - bool operator== (nano::node_id_handshake const &) const; - boost::optional query; - boost::optional> response; - std::size_t size () const; - static std::size_t size (nano::message_header const &); -}; - -class message_visitor -{ -public: - virtual void keepalive (nano::keepalive const & message) - { - default_handler (message); - }; - virtual void publish (nano::publish const & message) - { - default_handler (message); - } - virtual void confirm_req (nano::confirm_req const & message) - { - default_handler (message); - } - virtual void confirm_ack (nano::confirm_ack const & message) - { - default_handler (message); - } - virtual void bulk_pull (nano::bulk_pull const & message) - { - default_handler (message); - } - virtual void bulk_pull_account (nano::bulk_pull_account const & message) - { - default_handler (message); - } - virtual void bulk_push (nano::bulk_push const & message) - { - default_handler (message); - } - virtual void frontier_req (nano::frontier_req const & message) - { - default_handler (message); - } - virtual void node_id_handshake (nano::node_id_handshake const & message) - { - default_handler (message); - } - virtual void telemetry_req (nano::telemetry_req const & message) - { - default_handler (message); - } - virtual void telemetry_ack (nano::telemetry_ack const & message) - { - default_handler (message); - } - virtual void default_handler (nano::message const &){}; - virtual ~message_visitor (); -}; - class telemetry_cache_cutoffs { public: diff --git a/nano/node/messages.cpp b/nano/node/messages.cpp new file mode 100644 index 000000000..55e06cfc7 --- /dev/null +++ b/nano/node/messages.cpp @@ -0,0 +1,1584 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +/* + * message_header + */ + +nano::message_header::message_header (nano::network_constants const & constants, nano::message_type type_a) : + network{ constants.current_network }, + version_max{ constants.protocol_version }, + version_using{ constants.protocol_version }, + version_min{ constants.protocol_version_min }, + type (type_a) +{ +} + +nano::message_header::message_header (bool & error_a, nano::stream & stream_a) +{ + if (!error_a) + { + error_a = deserialize (stream_a); + } +} + +void nano::message_header::serialize (nano::stream & stream_a) const +{ + nano::write (stream_a, boost::endian::native_to_big (static_cast (network))); + nano::write (stream_a, version_max); + nano::write (stream_a, version_using); + nano::write (stream_a, version_min); + nano::write (stream_a, type); + nano::write (stream_a, static_cast (extensions.to_ullong ())); +} + +bool nano::message_header::deserialize (nano::stream & stream_a) +{ + auto error (false); + try + { + uint16_t network_bytes; + nano::read (stream_a, network_bytes); + network = static_cast (boost::endian::big_to_native (network_bytes)); + nano::read (stream_a, version_max); + nano::read (stream_a, version_using); + nano::read (stream_a, version_min); + nano::read (stream_a, type); + uint16_t extensions_l; + nano::read (stream_a, extensions_l); + extensions = extensions_l; + } + catch (std::runtime_error const &) + { + error = true; + } + + return error; +} + +std::string nano::to_string (nano::message_type message_type_l) +{ + switch (message_type_l) + { + case nano::message_type::invalid: + return "invalid"; + case nano::message_type::not_a_type: + return "not_a_type"; + case nano::message_type::keepalive: + return "keepalive"; + case nano::message_type::publish: + return "publish"; + case nano::message_type::confirm_req: + return "confirm_req"; + case nano::message_type::confirm_ack: + return "confirm_ack"; + case nano::message_type::bulk_pull: + return "bulk_pull"; + case nano::message_type::bulk_push: + return "bulk_push"; + case nano::message_type::frontier_req: + return "frontier_req"; + case nano::message_type::node_id_handshake: + return "node_id_handshake"; + case nano::message_type::bulk_pull_account: + return "bulk_pull_account"; + case nano::message_type::telemetry_req: + return "telemetry_req"; + case nano::message_type::telemetry_ack: + return "telemetry_ack"; + // default case intentionally omitted to cause warnings for unhandled enums + } + + return "n/a"; +} + +nano::stat::detail nano::to_stat_detail (nano::message_type message_type) +{ + switch (message_type) + { + case nano::message_type::invalid: + return nano::stat::detail::invalid; + case nano::message_type::not_a_type: + return nano::stat::detail::not_a_type; + case nano::message_type::keepalive: + return nano::stat::detail::keepalive; + case nano::message_type::publish: + return nano::stat::detail::publish; + case nano::message_type::confirm_req: + return nano::stat::detail::confirm_req; + case nano::message_type::confirm_ack: + return nano::stat::detail::confirm_ack; + case nano::message_type::bulk_pull: + return nano::stat::detail::bulk_pull; + case nano::message_type::bulk_push: + return nano::stat::detail::bulk_push; + case nano::message_type::frontier_req: + return nano::stat::detail::frontier_req; + case nano::message_type::node_id_handshake: + return nano::stat::detail::node_id_handshake; + case nano::message_type::bulk_pull_account: + return nano::stat::detail::bulk_pull_account; + case nano::message_type::telemetry_req: + return nano::stat::detail::telemetry_req; + case nano::message_type::telemetry_ack: + return nano::stat::detail::telemetry_ack; + // default case intentionally omitted to cause warnings for unhandled enums + } + debug_assert (false); + return {}; +} + +std::string nano::message_header::to_string () +{ + // Cast to uint16_t to get integer value since uint8_t is treated as an unsigned char in string formatting. + uint16_t type_l = static_cast (type); + uint16_t version_max_l = static_cast (version_max); + uint16_t version_using_l = static_cast (version_using); + uint16_t version_min_l = static_cast (version_min); + std::string type_text = nano::to_string (type); + + std::stringstream stream; + + stream << boost::format ("NetID: %1%(%2%), ") % nano::to_string_hex (static_cast (network)) % nano::network::to_string (network); + stream << boost::format ("VerMaxUsingMin: %1%/%2%/%3%, ") % version_max_l % version_using_l % version_min_l; + stream << boost::format ("MsgType: %1%(%2%), ") % type_l % type_text; + stream << boost::format ("Extensions: %1%") % nano::to_string_hex (static_cast (extensions.to_ulong ())); + + return stream.str (); +} + +nano::block_type nano::message_header::block_type () const +{ + return static_cast (((extensions & block_type_mask) >> 8).to_ullong ()); +} + +void nano::message_header::block_type_set (nano::block_type type_a) +{ + extensions &= ~block_type_mask; + extensions |= std::bitset<16> (static_cast (type_a) << 8); +} + +uint8_t nano::message_header::count_get () const +{ + return static_cast (((extensions & count_mask) >> 12).to_ullong ()); +} + +void nano::message_header::count_set (uint8_t count_a) +{ + debug_assert (count_a < 16); + extensions &= ~count_mask; + extensions |= std::bitset<16> (static_cast (count_a) << 12); +} + +void nano::message_header::flag_set (uint8_t flag_a) +{ + // Flags from 8 are block_type & count + debug_assert (flag_a < 8); + extensions.set (flag_a, true); +} + +bool nano::message_header::bulk_pull_is_count_present () const +{ + auto result (false); + if (type == nano::message_type::bulk_pull) + { + if (extensions.test (bulk_pull_count_present_flag)) + { + result = true; + } + } + return result; +} + +bool nano::message_header::bulk_pull_ascending () const +{ + auto result (false); + if (type == nano::message_type::bulk_pull) + { + if (extensions.test (bulk_pull_ascending_flag)) + { + result = true; + } + } + return result; +} + +bool nano::message_header::frontier_req_is_only_confirmed_present () const +{ + auto result (false); + if (type == nano::message_type::frontier_req) + { + if (extensions.test (frontier_req_only_confirmed)) + { + result = true; + } + } + return result; +} + +bool nano::message_header::node_id_handshake_is_query () const +{ + auto result (false); + if (type == nano::message_type::node_id_handshake) + { + if (extensions.test (node_id_handshake_query_flag)) + { + result = true; + } + } + return result; +} + +bool nano::message_header::node_id_handshake_is_response () const +{ + auto result (false); + if (type == nano::message_type::node_id_handshake) + { + if (extensions.test (node_id_handshake_response_flag)) + { + result = true; + } + } + return result; +} + +std::size_t nano::message_header::payload_length_bytes () const +{ + switch (type) + { + case nano::message_type::bulk_pull: + { + return nano::bulk_pull::size + (bulk_pull_is_count_present () ? nano::bulk_pull::extended_parameters_size : 0); + } + case nano::message_type::bulk_push: + case nano::message_type::telemetry_req: + { + // These don't have a payload + return 0; + } + case nano::message_type::frontier_req: + { + return nano::frontier_req::size; + } + case nano::message_type::bulk_pull_account: + { + return nano::bulk_pull_account::size; + } + case nano::message_type::keepalive: + { + return nano::keepalive::size; + } + case nano::message_type::publish: + { + return nano::block::size (block_type ()); + } + case nano::message_type::confirm_ack: + { + return nano::confirm_ack::size (count_get ()); + } + case nano::message_type::confirm_req: + { + return nano::confirm_req::size (block_type (), count_get ()); + } + case nano::message_type::node_id_handshake: + { + return nano::node_id_handshake::size (*this); + } + case nano::message_type::telemetry_ack: + { + return nano::telemetry_ack::size (*this); + } + default: + { + debug_assert (false); + return 0; + } + } +} + +bool nano::message_header::is_valid_message_type () const +{ + switch (type) + { + case nano::message_type::bulk_pull: + case nano::message_type::bulk_push: + case nano::message_type::telemetry_req: + case nano::message_type::frontier_req: + case nano::message_type::bulk_pull_account: + case nano::message_type::keepalive: + case nano::message_type::publish: + case nano::message_type::confirm_ack: + case nano::message_type::confirm_req: + case nano::message_type::node_id_handshake: + case nano::message_type::telemetry_ack: + { + return true; + } + default: + { + return false; + } + } +} + +/* + * message + */ + +nano::message::message (nano::network_constants const & constants, nano::message_type type_a) : + header (constants, type_a) +{ +} + +nano::message::message (nano::message_header const & header_a) : + header (header_a) +{ +} + +std::shared_ptr> nano::message::to_bytes () const +{ + auto bytes = std::make_shared> (); + nano::vectorstream stream (*bytes); + serialize (stream); + return bytes; +} + +nano::shared_const_buffer nano::message::to_shared_const_buffer () const +{ + return shared_const_buffer (to_bytes ()); +} + +/* + * message_parser + */ + +// MTU - IP header - UDP header +std::size_t const nano::message_parser::max_safe_udp_message_size = 508; + +std::string nano::message_parser::status_string () +{ + switch (status) + { + case nano::message_parser::parse_status::success: + { + return "success"; + } + case nano::message_parser::parse_status::insufficient_work: + { + return "insufficient_work"; + } + case nano::message_parser::parse_status::invalid_header: + { + return "invalid_header"; + } + case nano::message_parser::parse_status::invalid_message_type: + { + return "invalid_message_type"; + } + case nano::message_parser::parse_status::invalid_keepalive_message: + { + return "invalid_keepalive_message"; + } + case nano::message_parser::parse_status::invalid_publish_message: + { + return "invalid_publish_message"; + } + case nano::message_parser::parse_status::invalid_confirm_req_message: + { + return "invalid_confirm_req_message"; + } + case nano::message_parser::parse_status::invalid_confirm_ack_message: + { + return "invalid_confirm_ack_message"; + } + case nano::message_parser::parse_status::invalid_node_id_handshake_message: + { + return "invalid_node_id_handshake_message"; + } + case nano::message_parser::parse_status::invalid_telemetry_req_message: + { + return "invalid_telemetry_req_message"; + } + case nano::message_parser::parse_status::invalid_telemetry_ack_message: + { + return "invalid_telemetry_ack_message"; + } + case nano::message_parser::parse_status::outdated_version: + { + return "outdated_version"; + } + case nano::message_parser::parse_status::duplicate_publish_message: + { + return "duplicate_publish_message"; + } + } + + debug_assert (false); + + 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::network_constants const & network) : + 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), + network{ network } +{ +} + +void nano::message_parser::deserialize_buffer (uint8_t const * buffer_a, std::size_t size_a) +{ + status = parse_status::success; + auto error (false); + if (size_a <= max_safe_udp_message_size) + { + // Guaranteed to be deliverable + nano::bufferstream stream (buffer_a, size_a); + nano::message_header header (error, stream); + if (!error) + { + if (header.network != network.current_network) + { + status = parse_status::invalid_header; + return; + } + + if (header.version_using < network.protocol_version_min) + { + status = parse_status::outdated_version; + } + else + { + switch (header.type) + { + case nano::message_type::keepalive: + { + deserialize_keepalive (stream, header); + break; + } + case nano::message_type::publish: + { + nano::uint128_t digest; + if (!publish_filter.apply (buffer_a + header.size, size_a - header.size, &digest)) + { + deserialize_publish (stream, header, digest); + } + else + { + status = parse_status::duplicate_publish_message; + } + break; + } + case nano::message_type::confirm_req: + { + deserialize_confirm_req (stream, header); + break; + } + case nano::message_type::confirm_ack: + { + deserialize_confirm_ack (stream, header); + break; + } + case nano::message_type::node_id_handshake: + { + deserialize_node_id_handshake (stream, header); + break; + } + case nano::message_type::telemetry_req: + { + deserialize_telemetry_req (stream, header); + break; + } + case nano::message_type::telemetry_ack: + { + deserialize_telemetry_ack (stream, header); + break; + } + default: + { + status = parse_status::invalid_message_type; + break; + } + } + } + } + else + { + status = parse_status::invalid_header; + } + } +} + +void nano::message_parser::deserialize_keepalive (nano::stream & stream_a, nano::message_header const & header_a) +{ + auto error (false); + nano::keepalive incoming (error, stream_a, header_a); + if (!error && at_end (stream_a)) + { + visitor.keepalive (incoming); + } + else + { + status = parse_status::invalid_keepalive_message; + } +} + +void nano::message_parser::deserialize_publish (nano::stream & stream_a, nano::message_header const & header_a, nano::uint128_t const & digest_a) +{ + auto error (false); + nano::publish incoming (error, stream_a, header_a, digest_a, &block_uniquer); + if (!error && at_end (stream_a)) + { + if (!network.work.validate_entry (*incoming.block)) + { + visitor.publish (incoming); + } + else + { + status = parse_status::insufficient_work; + } + } + else + { + status = parse_status::invalid_publish_message; + } +} + +void nano::message_parser::deserialize_confirm_req (nano::stream & stream_a, nano::message_header const & header_a) +{ + auto error (false); + nano::confirm_req incoming (error, stream_a, header_a, &block_uniquer); + if (!error && at_end (stream_a)) + { + if (incoming.block == nullptr || !network.work.validate_entry (*incoming.block)) + { + visitor.confirm_req (incoming); + } + else + { + status = parse_status::insufficient_work; + } + } + else + { + status = parse_status::invalid_confirm_req_message; + } +} + +void nano::message_parser::deserialize_confirm_ack (nano::stream & stream_a, nano::message_header const & header_a) +{ + auto error (false); + nano::confirm_ack incoming (error, stream_a, header_a, &vote_uniquer); + if (!error && at_end (stream_a)) + { + visitor.confirm_ack (incoming); + } + else + { + status = parse_status::invalid_confirm_ack_message; + } +} + +void nano::message_parser::deserialize_node_id_handshake (nano::stream & stream_a, nano::message_header const & header_a) +{ + bool error_l (false); + nano::node_id_handshake incoming (error_l, stream_a, header_a); + if (!error_l && at_end (stream_a)) + { + visitor.node_id_handshake (incoming); + } + else + { + status = parse_status::invalid_node_id_handshake_message; + } +} + +void nano::message_parser::deserialize_telemetry_req (nano::stream & stream_a, nano::message_header const & header_a) +{ + nano::telemetry_req incoming (header_a); + if (at_end (stream_a)) + { + visitor.telemetry_req (incoming); + } + else + { + status = parse_status::invalid_telemetry_req_message; + } +} + +void nano::message_parser::deserialize_telemetry_ack (nano::stream & stream_a, nano::message_header const & header_a) +{ + bool error_l (false); + nano::telemetry_ack incoming (error_l, stream_a, header_a); + // Intentionally not checking if at the end of stream, because these messages support backwards/forwards compatibility + if (!error_l) + { + visitor.telemetry_ack (incoming); + } + else + { + status = parse_status::invalid_telemetry_ack_message; + } +} + +bool nano::message_parser::at_end (nano::stream & stream_a) +{ + uint8_t junk; + auto end (nano::try_read (stream_a, junk)); + return end; +} + +/* + * keepalive + */ + +nano::keepalive::keepalive (nano::network_constants const & constants) : + message (constants, nano::message_type::keepalive) +{ + nano::endpoint endpoint (boost::asio::ip::address_v6{}, 0); + for (auto i (peers.begin ()), n (peers.end ()); i != n; ++i) + { + *i = endpoint; + } +} + +nano::keepalive::keepalive (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : + message (header_a) +{ + if (!error_a) + { + error_a = deserialize (stream_a); + } +} + +void nano::keepalive::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.keepalive (*this); +} + +void nano::keepalive::serialize (nano::stream & stream_a) const +{ + header.serialize (stream_a); + for (auto i (peers.begin ()), j (peers.end ()); i != j; ++i) + { + debug_assert (i->address ().is_v6 ()); + auto bytes (i->address ().to_v6 ().to_bytes ()); + write (stream_a, bytes); + write (stream_a, i->port ()); + } +} + +bool nano::keepalive::deserialize (nano::stream & stream_a) +{ + debug_assert (header.type == nano::message_type::keepalive); + auto error (false); + for (auto i (peers.begin ()), j (peers.end ()); i != j && !error; ++i) + { + std::array address; + uint16_t port; + if (!try_read (stream_a, address) && !try_read (stream_a, port)) + { + *i = nano::endpoint (boost::asio::ip::address_v6 (address), port); + } + else + { + error = true; + } + } + return error; +} + +bool nano::keepalive::operator== (nano::keepalive const & other_a) const +{ + return peers == other_a.peers; +} + +/* + * publish + */ + +nano::publish::publish (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::uint128_t const & digest_a, nano::block_uniquer * uniquer_a) : + message (header_a), + digest (digest_a) +{ + if (!error_a) + { + error_a = deserialize (stream_a, uniquer_a); + } +} + +nano::publish::publish (nano::network_constants const & constants, std::shared_ptr const & block_a) : + message (constants, nano::message_type::publish), + block (block_a) +{ + header.block_type_set (block->type ()); +} + +void nano::publish::serialize (nano::stream & stream_a) const +{ + debug_assert (block != nullptr); + header.serialize (stream_a); + block->serialize (stream_a); +} + +bool nano::publish::deserialize (nano::stream & stream_a, nano::block_uniquer * uniquer_a) +{ + debug_assert (header.type == nano::message_type::publish); + block = nano::deserialize_block (stream_a, header.block_type (), uniquer_a); + auto result (block == nullptr); + return result; +} + +void nano::publish::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.publish (*this); +} + +bool nano::publish::operator== (nano::publish const & other_a) const +{ + return *block == *other_a.block; +} + +/* + * confirm_req + */ + +nano::confirm_req::confirm_req (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::block_uniquer * uniquer_a) : + message (header_a) +{ + if (!error_a) + { + error_a = deserialize (stream_a, uniquer_a); + } +} + +nano::confirm_req::confirm_req (nano::network_constants const & constants, std::shared_ptr const & block_a) : + message (constants, nano::message_type::confirm_req), + block (block_a) +{ + header.block_type_set (block->type ()); +} + +nano::confirm_req::confirm_req (nano::network_constants const & constants, std::vector> const & roots_hashes_a) : + message (constants, nano::message_type::confirm_req), + roots_hashes (roots_hashes_a) +{ + // not_a_block (1) block type for hashes + roots request + header.block_type_set (nano::block_type::not_a_block); + debug_assert (roots_hashes.size () < 16); + header.count_set (static_cast (roots_hashes.size ())); +} + +nano::confirm_req::confirm_req (nano::network_constants const & constants, nano::block_hash const & hash_a, nano::root const & root_a) : + message (constants, nano::message_type::confirm_req), + roots_hashes (std::vector> (1, std::make_pair (hash_a, root_a))) +{ + debug_assert (!roots_hashes.empty ()); + // not_a_block (1) block type for hashes + roots request + header.block_type_set (nano::block_type::not_a_block); + debug_assert (roots_hashes.size () < 16); + header.count_set (static_cast (roots_hashes.size ())); +} + +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 +{ + header.serialize (stream_a); + if (header.block_type () == nano::block_type::not_a_block) + { + debug_assert (!roots_hashes.empty ()); + // Write hashes & roots + for (auto & root_hash : roots_hashes) + { + write (stream_a, root_hash.first); + write (stream_a, root_hash.second); + } + } + else + { + debug_assert (block != nullptr); + block->serialize (stream_a); + } +} + +bool nano::confirm_req::deserialize (nano::stream & stream_a, nano::block_uniquer * uniquer_a) +{ + bool result (false); + debug_assert (header.type == nano::message_type::confirm_req); + try + { + if (header.block_type () == nano::block_type::not_a_block) + { + uint8_t count (header.count_get ()); + for (auto i (0); i != count && !result; ++i) + { + nano::block_hash block_hash (0); + nano::block_hash root (0); + read (stream_a, block_hash); + read (stream_a, root); + if (!block_hash.is_zero () || !root.is_zero ()) + { + roots_hashes.emplace_back (block_hash, root); + } + } + + result = roots_hashes.empty () || (roots_hashes.size () != count); + } + else + { + block = nano::deserialize_block (stream_a, header.block_type (), uniquer_a); + result = block == nullptr; + } + } + catch (std::runtime_error const &) + { + result = true; + } + + return result; +} + +bool nano::confirm_req::operator== (nano::confirm_req const & other_a) const +{ + bool equal (false); + if (block != nullptr && other_a.block != nullptr) + { + equal = *block == *other_a.block; + } + else if (!roots_hashes.empty () && !other_a.roots_hashes.empty ()) + { + equal = roots_hashes == other_a.roots_hashes; + } + return equal; +} + +std::string nano::confirm_req::roots_string () const +{ + std::string result; + for (auto & root_hash : roots_hashes) + { + result += root_hash.first.to_string (); + result += ":"; + result += root_hash.second.to_string (); + result += ", "; + } + return result; +} + +std::size_t nano::confirm_req::size (nano::block_type type_a, std::size_t count) +{ + std::size_t result (0); + if (type_a != nano::block_type::invalid && type_a != nano::block_type::not_a_block) + { + result = nano::block::size (type_a); + } + else if (type_a == nano::block_type::not_a_block) + { + result = count * (sizeof (nano::uint256_union) + sizeof (nano::block_hash)); + } + return result; +} + +/* + * confirm_ack + */ + +nano::confirm_ack::confirm_ack (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a, nano::vote_uniquer * uniquer_a) : + message (header_a), + vote (nano::make_shared (error_a, stream_a)) +{ + if (!error_a && uniquer_a) + { + vote = uniquer_a->unique (vote); + } +} + +nano::confirm_ack::confirm_ack (nano::network_constants const & constants, std::shared_ptr const & vote_a) : + message (constants, nano::message_type::confirm_ack), + vote (vote_a) +{ + header.block_type_set (nano::block_type::not_a_block); + debug_assert (vote_a->hashes.size () < 16); + header.count_set (static_cast (vote_a->hashes.size ())); +} + +void nano::confirm_ack::serialize (nano::stream & stream_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); + vote->serialize (stream_a); +} + +bool nano::confirm_ack::operator== (nano::confirm_ack const & other_a) const +{ + auto result (*vote == *other_a.vote); + return result; +} + +void nano::confirm_ack::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.confirm_ack (*this); +} + +std::size_t nano::confirm_ack::size (std::size_t count) +{ + std::size_t result = sizeof (nano::account) + sizeof (nano::signature) + sizeof (uint64_t) + count * sizeof (nano::block_hash); + return result; +} + +/* + * frontier_req + */ + +nano::frontier_req::frontier_req (nano::network_constants const & constants) : + message (constants, nano::message_type::frontier_req) +{ +} + +nano::frontier_req::frontier_req (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : + message (header_a) +{ + if (!error_a) + { + error_a = deserialize (stream_a); + } +} + +void nano::frontier_req::serialize (nano::stream & stream_a) const +{ + header.serialize (stream_a); + write (stream_a, start.bytes); + write (stream_a, age); + write (stream_a, count); +} + +bool nano::frontier_req::deserialize (nano::stream & stream_a) +{ + debug_assert (header.type == nano::message_type::frontier_req); + auto error (false); + try + { + nano::read (stream_a, start.bytes); + nano::read (stream_a, age); + nano::read (stream_a, count); + } + catch (std::runtime_error const &) + { + error = true; + } + + return error; +} + +void nano::frontier_req::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.frontier_req (*this); +} + +bool nano::frontier_req::operator== (nano::frontier_req const & other_a) const +{ + return start == other_a.start && age == other_a.age && count == other_a.count; +} + +/* + * bulk_pull + */ + +nano::bulk_pull::bulk_pull (nano::network_constants const & constants) : + message (constants, nano::message_type::bulk_pull) +{ +} + +nano::bulk_pull::bulk_pull (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : + message (header_a) +{ + if (!error_a) + { + error_a = deserialize (stream_a); + } +} + +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 +{ + /* + * Ensure the "count_present" flag is set if there + * is a limit specifed. Additionally, do not allow + * the "count_present" flag with a value of 0, since + * that is a sentinel which we use to mean "all blocks" + * and that is the behavior of not having the flag set + * so it is wasteful to do this. + */ + debug_assert ((count == 0 && !is_count_present ()) || (count != 0 && is_count_present ())); + + header.serialize (stream_a); + write (stream_a, start); + write (stream_a, end); + + if (is_count_present ()) + { + std::array count_buffer{ { 0 } }; + decltype (count) count_little_endian; + static_assert (sizeof (count_little_endian) < (count_buffer.size () - 1), "count must fit within buffer"); + + count_little_endian = boost::endian::native_to_little (count); + memcpy (count_buffer.data () + 1, &count_little_endian, sizeof (count_little_endian)); + + write (stream_a, count_buffer); + } +} + +bool nano::bulk_pull::deserialize (nano::stream & stream_a) +{ + debug_assert (header.type == nano::message_type::bulk_pull); + auto error (false); + try + { + nano::read (stream_a, start); + nano::read (stream_a, end); + + if (is_count_present ()) + { + std::array extended_parameters_buffers; + static_assert (sizeof (count) < (extended_parameters_buffers.size () - 1), "count must fit within buffer"); + + nano::read (stream_a, extended_parameters_buffers); + if (extended_parameters_buffers.front () != 0) + { + error = true; + } + else + { + memcpy (&count, extended_parameters_buffers.data () + 1, sizeof (count)); + boost::endian::little_to_native_inplace (count); + } + } + else + { + count = 0; + } + } + catch (std::runtime_error const &) + { + error = true; + } + + return error; +} + +bool nano::bulk_pull::is_count_present () const +{ + return header.extensions.test (count_present_flag); +} + +void nano::bulk_pull::set_count_present (bool value_a) +{ + header.extensions.set (count_present_flag, value_a); +} + +/* + * bulk_pull_account + */ + +nano::bulk_pull_account::bulk_pull_account (nano::network_constants const & constants) : + message (constants, nano::message_type::bulk_pull_account) +{ +} + +nano::bulk_pull_account::bulk_pull_account (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : + message (header_a) +{ + if (!error_a) + { + error_a = deserialize (stream_a); + } +} + +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 +{ + header.serialize (stream_a); + write (stream_a, account); + write (stream_a, minimum_amount); + write (stream_a, flags); +} + +bool nano::bulk_pull_account::deserialize (nano::stream & stream_a) +{ + debug_assert (header.type == nano::message_type::bulk_pull_account); + auto error (false); + try + { + nano::read (stream_a, account); + nano::read (stream_a, minimum_amount); + nano::read (stream_a, flags); + } + catch (std::runtime_error const &) + { + error = true; + } + + return error; +} + +/* + * bulk_push + */ + +nano::bulk_push::bulk_push (nano::network_constants const & constants) : + message (constants, nano::message_type::bulk_push) +{ +} + +nano::bulk_push::bulk_push (nano::message_header const & header_a) : + message (header_a) +{ +} + +bool nano::bulk_push::deserialize (nano::stream & stream_a) +{ + debug_assert (header.type == nano::message_type::bulk_push); + return false; +} + +void nano::bulk_push::serialize (nano::stream & stream_a) const +{ + header.serialize (stream_a); +} + +void nano::bulk_push::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.bulk_push (*this); +} + +/* + * telemetry_req + */ + +nano::telemetry_req::telemetry_req (nano::network_constants const & constants) : + message (constants, nano::message_type::telemetry_req) +{ +} + +nano::telemetry_req::telemetry_req (nano::message_header const & header_a) : + message (header_a) +{ +} + +bool nano::telemetry_req::deserialize (nano::stream & stream_a) +{ + debug_assert (header.type == nano::message_type::telemetry_req); + return false; +} + +void nano::telemetry_req::serialize (nano::stream & stream_a) const +{ + header.serialize (stream_a); +} + +void nano::telemetry_req::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.telemetry_req (*this); +} + +/* + * telemetry_ack + */ + +nano::telemetry_ack::telemetry_ack (nano::network_constants const & constants) : + message (constants, nano::message_type::telemetry_ack) +{ +} + +nano::telemetry_ack::telemetry_ack (bool & error_a, nano::stream & stream_a, nano::message_header const & message_header) : + message (message_header) +{ + if (!error_a) + { + error_a = deserialize (stream_a); + } +} + +nano::telemetry_ack::telemetry_ack (nano::network_constants const & constants, nano::telemetry_data const & telemetry_data_a) : + message (constants, nano::message_type::telemetry_ack), + data (telemetry_data_a) +{ + debug_assert (telemetry_data::size + telemetry_data_a.unknown_data.size () <= message_header::telemetry_size_mask.to_ulong ()); // Maximum size the mask allows + header.extensions &= ~message_header::telemetry_size_mask; + header.extensions |= std::bitset<16> (static_cast (telemetry_data::size) + telemetry_data_a.unknown_data.size ()); +} + +void nano::telemetry_ack::serialize (nano::stream & stream_a) const +{ + header.serialize (stream_a); + if (!is_empty_payload ()) + { + data.serialize (stream_a); + } +} + +bool nano::telemetry_ack::deserialize (nano::stream & stream_a) +{ + auto error (false); + debug_assert (header.type == nano::message_type::telemetry_ack); + try + { + if (!is_empty_payload ()) + { + data.deserialize (stream_a, nano::narrow_cast (header.extensions.to_ulong ())); + } + } + catch (std::runtime_error const &) + { + error = true; + } + + return error; +} + +void nano::telemetry_ack::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.telemetry_ack (*this); +} + +uint16_t nano::telemetry_ack::size () const +{ + return size (header); +} + +uint16_t nano::telemetry_ack::size (nano::message_header const & message_header_a) +{ + return static_cast ((message_header_a.extensions & message_header::telemetry_size_mask).to_ullong ()); +} + +bool nano::telemetry_ack::is_empty_payload () const +{ + return size () == 0; +} + +/* + * telemetry_data + */ + +void nano::telemetry_data::deserialize (nano::stream & stream_a, uint16_t payload_length_a) +{ + read (stream_a, signature); + read (stream_a, node_id); + read (stream_a, block_count); + boost::endian::big_to_native_inplace (block_count); + read (stream_a, cemented_count); + boost::endian::big_to_native_inplace (cemented_count); + read (stream_a, unchecked_count); + boost::endian::big_to_native_inplace (unchecked_count); + read (stream_a, account_count); + boost::endian::big_to_native_inplace (account_count); + read (stream_a, bandwidth_cap); + boost::endian::big_to_native_inplace (bandwidth_cap); + read (stream_a, peer_count); + boost::endian::big_to_native_inplace (peer_count); + read (stream_a, protocol_version); + read (stream_a, uptime); + boost::endian::big_to_native_inplace (uptime); + read (stream_a, genesis_block.bytes); + read (stream_a, major_version); + read (stream_a, minor_version); + read (stream_a, patch_version); + read (stream_a, pre_release_version); + read (stream_a, maker); + + uint64_t timestamp_l; + read (stream_a, timestamp_l); + boost::endian::big_to_native_inplace (timestamp_l); + timestamp = std::chrono::system_clock::time_point (std::chrono::milliseconds (timestamp_l)); + read (stream_a, active_difficulty); + boost::endian::big_to_native_inplace (active_difficulty); + if (payload_length_a > latest_size) + { + read (stream_a, unknown_data, payload_length_a - latest_size); + } +} + +void nano::telemetry_data::serialize_without_signature (nano::stream & stream_a) const +{ + // All values should be serialized in big endian + write (stream_a, node_id); + write (stream_a, boost::endian::native_to_big (block_count)); + write (stream_a, boost::endian::native_to_big (cemented_count)); + write (stream_a, boost::endian::native_to_big (unchecked_count)); + write (stream_a, boost::endian::native_to_big (account_count)); + write (stream_a, boost::endian::native_to_big (bandwidth_cap)); + write (stream_a, boost::endian::native_to_big (peer_count)); + write (stream_a, protocol_version); + write (stream_a, boost::endian::native_to_big (uptime)); + write (stream_a, genesis_block.bytes); + write (stream_a, major_version); + write (stream_a, minor_version); + write (stream_a, patch_version); + write (stream_a, pre_release_version); + write (stream_a, maker); + write (stream_a, boost::endian::native_to_big (std::chrono::duration_cast (timestamp.time_since_epoch ()).count ())); + write (stream_a, boost::endian::native_to_big (active_difficulty)); + write (stream_a, unknown_data); +} + +void nano::telemetry_data::serialize (nano::stream & stream_a) const +{ + write (stream_a, signature); + serialize_without_signature (stream_a); +} + +nano::error nano::telemetry_data::serialize_json (nano::jsonconfig & json, bool ignore_identification_metrics_a) const +{ + json.put ("block_count", block_count); + json.put ("cemented_count", cemented_count); + json.put ("unchecked_count", unchecked_count); + json.put ("account_count", account_count); + json.put ("bandwidth_cap", bandwidth_cap); + json.put ("peer_count", peer_count); + json.put ("protocol_version", protocol_version); + json.put ("uptime", uptime); + json.put ("genesis_block", genesis_block.to_string ()); + json.put ("major_version", major_version); + json.put ("minor_version", minor_version); + json.put ("patch_version", patch_version); + json.put ("pre_release_version", pre_release_version); + json.put ("maker", maker); + json.put ("timestamp", std::chrono::duration_cast (timestamp.time_since_epoch ()).count ()); + json.put ("active_difficulty", nano::to_string_hex (active_difficulty)); + // Keep these last for UI purposes + if (!ignore_identification_metrics_a) + { + json.put ("node_id", node_id.to_node_id ()); + json.put ("signature", signature.to_string ()); + } + return json.get_error (); +} + +nano::error nano::telemetry_data::deserialize_json (nano::jsonconfig & json, bool ignore_identification_metrics_a) +{ + if (!ignore_identification_metrics_a) + { + std::string signature_l; + json.get ("signature", signature_l); + if (!json.get_error ()) + { + if (signature.decode_hex (signature_l)) + { + json.get_error ().set ("Could not deserialize signature"); + } + } + + std::string node_id_l; + json.get ("node_id", node_id_l); + if (!json.get_error ()) + { + if (node_id.decode_node_id (node_id_l)) + { + json.get_error ().set ("Could not deserialize node id"); + } + } + } + + json.get ("block_count", block_count); + json.get ("cemented_count", cemented_count); + json.get ("unchecked_count", unchecked_count); + json.get ("account_count", account_count); + json.get ("bandwidth_cap", bandwidth_cap); + json.get ("peer_count", peer_count); + json.get ("protocol_version", protocol_version); + json.get ("uptime", uptime); + std::string genesis_block_l; + json.get ("genesis_block", genesis_block_l); + if (!json.get_error ()) + { + if (genesis_block.decode_hex (genesis_block_l)) + { + json.get_error ().set ("Could not deserialize genesis block"); + } + } + json.get ("major_version", major_version); + json.get ("minor_version", minor_version); + json.get ("patch_version", patch_version); + json.get ("pre_release_version", pre_release_version); + json.get ("maker", maker); + auto timestamp_l = json.get ("timestamp"); + timestamp = std::chrono::system_clock::time_point (std::chrono::milliseconds (timestamp_l)); + auto current_active_difficulty_text = json.get ("active_difficulty"); + auto ec = nano::from_string_hex (current_active_difficulty_text, active_difficulty); + debug_assert (!ec); + return json.get_error (); +} + +std::string nano::telemetry_data::to_string () const +{ + nano::jsonconfig jc; + serialize_json (jc, true); + std::stringstream ss; + jc.write (ss); + return ss.str (); +} + +bool nano::telemetry_data::operator== (nano::telemetry_data const & data_a) const +{ + return (signature == data_a.signature && node_id == data_a.node_id && block_count == data_a.block_count && cemented_count == data_a.cemented_count && unchecked_count == data_a.unchecked_count && account_count == data_a.account_count && bandwidth_cap == data_a.bandwidth_cap && uptime == data_a.uptime && peer_count == data_a.peer_count && protocol_version == data_a.protocol_version && genesis_block == data_a.genesis_block && major_version == data_a.major_version && minor_version == data_a.minor_version && patch_version == data_a.patch_version && pre_release_version == data_a.pre_release_version && maker == data_a.maker && timestamp == data_a.timestamp && active_difficulty == data_a.active_difficulty && unknown_data == data_a.unknown_data); +} + +bool nano::telemetry_data::operator!= (nano::telemetry_data const & data_a) const +{ + return !(*this == data_a); +} + +void nano::telemetry_data::sign (nano::keypair const & node_id_a) +{ + debug_assert (node_id == node_id_a.pub); + std::vector bytes; + { + nano::vectorstream stream (bytes); + serialize_without_signature (stream); + } + + signature = nano::sign_message (node_id_a.prv, node_id_a.pub, bytes.data (), bytes.size ()); +} + +bool nano::telemetry_data::validate_signature () const +{ + std::vector bytes; + { + nano::vectorstream stream (bytes); + serialize_without_signature (stream); + } + + return nano::validate_message (node_id, bytes.data (), bytes.size (), signature); +} + +/* + * node_id_handshake + */ + +nano::node_id_handshake::node_id_handshake (bool & error_a, nano::stream & stream_a, nano::message_header const & header_a) : + message (header_a), + query (boost::none), + response (boost::none) +{ + error_a = deserialize (stream_a); +} + +nano::node_id_handshake::node_id_handshake (nano::network_constants const & constants, boost::optional query, boost::optional> response) : + message (constants, nano::message_type::node_id_handshake), + query (query), + response (response) +{ + if (query) + { + header.flag_set (nano::message_header::node_id_handshake_query_flag); + } + if (response) + { + header.flag_set (nano::message_header::node_id_handshake_response_flag); + } +} + +void nano::node_id_handshake::serialize (nano::stream & stream_a) const +{ + header.serialize (stream_a); + if (query) + { + write (stream_a, *query); + } + if (response) + { + write (stream_a, response->first); + write (stream_a, response->second); + } +} + +bool nano::node_id_handshake::deserialize (nano::stream & stream_a) +{ + debug_assert (header.type == nano::message_type::node_id_handshake); + auto error (false); + try + { + if (header.node_id_handshake_is_query ()) + { + nano::uint256_union query_hash; + read (stream_a, query_hash); + query = query_hash; + } + + if (header.node_id_handshake_is_response ()) + { + nano::account response_account; + read (stream_a, response_account); + nano::signature response_signature; + read (stream_a, response_signature); + response = std::make_pair (response_account, response_signature); + } + } + catch (std::runtime_error const &) + { + error = true; + } + + return error; +} + +bool nano::node_id_handshake::operator== (nano::node_id_handshake const & other_a) const +{ + auto result (*query == *other_a.query && *response == *other_a.response); + return result; +} + +void nano::node_id_handshake::visit (nano::message_visitor & visitor_a) const +{ + visitor_a.node_id_handshake (*this); +} + +std::size_t nano::node_id_handshake::size () const +{ + return size (header); +} + +std::size_t nano::node_id_handshake::size (nano::message_header const & header_a) +{ + std::size_t result (0); + if (header_a.node_id_handshake_is_query ()) + { + result = sizeof (nano::uint256_union); + } + if (header_a.node_id_handshake_is_response ()) + { + result += sizeof (nano::account) + sizeof (nano::signature); + } + return result; +} \ No newline at end of file diff --git a/nano/node/messages.hpp b/nano/node/messages.hpp new file mode 100644 index 000000000..e351b3d76 --- /dev/null +++ b/nano/node/messages.hpp @@ -0,0 +1,402 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace nano +{ +/** + * Message types are serialized to the network and existing values must thus never change as + * types are added, removed and reordered in the enum. + */ +enum class message_type : uint8_t +{ + invalid = 0x0, + not_a_type = 0x1, + keepalive = 0x2, + publish = 0x3, + confirm_req = 0x4, + confirm_ack = 0x5, + bulk_pull = 0x6, + bulk_push = 0x7, + frontier_req = 0x8, + /* deleted 0x9 */ + node_id_handshake = 0x0a, + bulk_pull_account = 0x0b, + telemetry_req = 0x0c, + telemetry_ack = 0x0d +}; + +std::string to_string (message_type); +stat::detail to_stat_detail (message_type); + +enum class bulk_pull_account_flags : uint8_t +{ + pending_hash_and_amount = 0x0, + pending_address_only = 0x1, + pending_hash_amount_and_address = 0x2 +}; + +class message_visitor; + +class message_header final +{ +public: + message_header (nano::network_constants const &, nano::message_type); + message_header (bool &, nano::stream &); + void serialize (nano::stream &) const; + bool deserialize (nano::stream &); + nano::block_type block_type () const; + void block_type_set (nano::block_type); + uint8_t count_get () const; + void count_set (uint8_t); + nano::networks network; + uint8_t version_max; + uint8_t version_using; + uint8_t version_min; + std::string to_string (); + +public: + nano::message_type type; + std::bitset<16> extensions; + static std::size_t constexpr size = sizeof (nano::networks) + sizeof (version_max) + sizeof (version_using) + sizeof (version_min) + sizeof (type) + sizeof (/* extensions */ uint16_t); + + void flag_set (uint8_t); + static uint8_t constexpr bulk_pull_count_present_flag = 0; + static uint8_t constexpr bulk_pull_ascending_flag = 1; + bool bulk_pull_is_count_present () const; + bool bulk_pull_ascending () const; + static uint8_t constexpr frontier_req_only_confirmed = 1; + bool frontier_req_is_only_confirmed_present () const; + static uint8_t constexpr node_id_handshake_query_flag = 0; + static uint8_t constexpr node_id_handshake_response_flag = 1; + bool node_id_handshake_is_query () const; + bool node_id_handshake_is_response () const; + + /** Size of the payload in bytes. For some messages, the payload size is based on header flags. */ + std::size_t payload_length_bytes () const; + bool is_valid_message_type () const; + + static std::bitset<16> constexpr block_type_mask{ 0x0f00 }; + static std::bitset<16> constexpr count_mask{ 0xf000 }; + static std::bitset<16> constexpr telemetry_size_mask{ 0x3ff }; +}; + +class message +{ +public: + explicit message (nano::network_constants const &, nano::message_type); + explicit message (nano::message_header const &); + virtual ~message () = default; + virtual void serialize (nano::stream &) 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; + + nano::message_header header; +}; + +class work_pool; +class network_constants; + +class message_parser final +{ +public: + enum class parse_status + { + success, + insufficient_work, + invalid_header, + invalid_message_type, + invalid_keepalive_message, + invalid_publish_message, + invalid_confirm_req_message, + invalid_confirm_ack_message, + invalid_node_id_handshake_message, + invalid_telemetry_req_message, + invalid_telemetry_ack_message, + outdated_version, + duplicate_publish_message + }; + message_parser (nano::network_filter &, nano::block_uniquer &, nano::vote_uniquer &, nano::message_visitor &, nano::work_pool &, nano::network_constants const & protocol); + void deserialize_buffer (uint8_t const *, std::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); + void deserialize_confirm_req (nano::stream &, nano::message_header const &); + void deserialize_confirm_ack (nano::stream &, nano::message_header const &); + void deserialize_node_id_handshake (nano::stream &, nano::message_header const &); + void deserialize_telemetry_req (nano::stream &, nano::message_header const &); + void deserialize_telemetry_ack (nano::stream &, nano::message_header const &); + bool at_end (nano::stream &); + nano::network_filter & publish_filter; + nano::block_uniquer & block_uniquer; + nano::vote_uniquer & vote_uniquer; + nano::message_visitor & visitor; + nano::work_pool & pool; + parse_status status; + nano::network_constants const & network; + std::string status_string (); + static std::size_t const max_safe_udp_message_size; +}; + +class keepalive final : public message +{ +public: + explicit keepalive (nano::network_constants const & constants); + keepalive (bool &, nano::stream &, nano::message_header const &); + void visit (nano::message_visitor &) const override; + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &); + bool operator== (nano::keepalive const &) const; + std::array peers; + static std::size_t constexpr size = 8 * (16 + 2); +}; + +class publish final : public message +{ +public: + publish (bool &, nano::stream &, nano::message_header const &, nano::uint128_t const & = 0, nano::block_uniquer * = nullptr); + publish (nano::network_constants const & constants, std::shared_ptr const &); + void visit (nano::message_visitor &) const override; + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &, nano::block_uniquer * = nullptr); + bool operator== (nano::publish const &) const; + std::shared_ptr block; + nano::uint128_t digest{ 0 }; +}; + +class confirm_req final : public message +{ +public: + confirm_req (bool &, nano::stream &, nano::message_header const &, nano::block_uniquer * = nullptr); + confirm_req (nano::network_constants const & constants, std::shared_ptr const &); + confirm_req (nano::network_constants const & constants, std::vector> const &); + confirm_req (nano::network_constants const & constants, nano::block_hash const &, nano::root const &); + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &, nano::block_uniquer * = nullptr); + void visit (nano::message_visitor &) const override; + bool operator== (nano::confirm_req const &) const; + std::shared_ptr block; + std::vector> roots_hashes; + std::string roots_string () const; + static std::size_t size (nano::block_type, std::size_t = 0); +}; + +class confirm_ack final : public message +{ +public: + confirm_ack (bool &, nano::stream &, nano::message_header const &, nano::vote_uniquer * = nullptr); + confirm_ack (nano::network_constants const & constants, std::shared_ptr const &); + void serialize (nano::stream &) const override; + void visit (nano::message_visitor &) const override; + bool operator== (nano::confirm_ack const &) const; + std::shared_ptr vote; + static std::size_t size (std::size_t count); +}; + +class frontier_req final : public message +{ +public: + explicit frontier_req (nano::network_constants const & constants); + frontier_req (bool &, nano::stream &, nano::message_header const &); + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &); + void visit (nano::message_visitor &) const override; + bool operator== (nano::frontier_req const &) const; + nano::account start; + uint32_t age; + uint32_t count; + static std::size_t constexpr size = sizeof (start) + sizeof (age) + sizeof (count); +}; + +enum class telemetry_maker : uint8_t +{ + nf_node = 0, + nf_pruned_node = 1 +}; + +class telemetry_data +{ +public: + nano::signature signature{ 0 }; + nano::account node_id{}; + uint64_t block_count{ 0 }; + uint64_t cemented_count{ 0 }; + uint64_t unchecked_count{ 0 }; + uint64_t account_count{ 0 }; + uint64_t bandwidth_cap{ 0 }; + uint64_t uptime{ 0 }; + uint32_t peer_count{ 0 }; + uint8_t protocol_version{ 0 }; + nano::block_hash genesis_block{ 0 }; + uint8_t major_version{ 0 }; + uint8_t minor_version{ 0 }; + uint8_t patch_version{ 0 }; + uint8_t pre_release_version{ 0 }; + uint8_t maker{ static_cast> (telemetry_maker::nf_node) }; // Where this telemetry information originated + std::chrono::system_clock::time_point timestamp; + uint64_t active_difficulty{ 0 }; + std::vector unknown_data; + + void serialize (nano::stream &) const; + void deserialize (nano::stream &, uint16_t); + nano::error serialize_json (nano::jsonconfig &, bool) const; + nano::error deserialize_json (nano::jsonconfig &, bool); + void sign (nano::keypair const &); + bool validate_signature () const; + bool operator== (nano::telemetry_data const &) const; + bool operator!= (nano::telemetry_data const &) const; + std::string to_string () const; + + // Size does not include unknown_data + static auto constexpr size = sizeof (signature) + sizeof (node_id) + sizeof (block_count) + sizeof (cemented_count) + sizeof (unchecked_count) + sizeof (account_count) + sizeof (bandwidth_cap) + sizeof (peer_count) + sizeof (protocol_version) + sizeof (uptime) + sizeof (genesis_block) + sizeof (major_version) + sizeof (minor_version) + sizeof (patch_version) + sizeof (pre_release_version) + sizeof (maker) + sizeof (uint64_t) + sizeof (active_difficulty); + static auto constexpr latest_size = size; // This needs to be updated for each new telemetry version +private: + void serialize_without_signature (nano::stream &) const; +}; + +class telemetry_req final : public message +{ +public: + explicit telemetry_req (nano::network_constants const & constants); + explicit telemetry_req (nano::message_header const &); + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &); + void visit (nano::message_visitor &) const override; +}; + +class telemetry_ack final : public message +{ +public: + explicit telemetry_ack (nano::network_constants const & constants); + telemetry_ack (bool &, nano::stream &, nano::message_header const &); + telemetry_ack (nano::network_constants const & constants, telemetry_data const &); + void serialize (nano::stream &) const override; + void visit (nano::message_visitor &) const override; + bool deserialize (nano::stream &); + uint16_t size () const; + bool is_empty_payload () const; + static uint16_t size (nano::message_header const &); + nano::telemetry_data data; +}; + +class bulk_pull final : public message +{ +public: + using count_t = uint32_t; + explicit bulk_pull (nano::network_constants const & constants); + bulk_pull (bool &, nano::stream &, nano::message_header const &); + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &); + void visit (nano::message_visitor &) const override; + nano::hash_or_account start{ 0 }; + nano::block_hash end{ 0 }; + count_t count{ 0 }; + bool is_count_present () const; + void set_count_present (bool); + static std::size_t constexpr count_present_flag = nano::message_header::bulk_pull_count_present_flag; + static std::size_t constexpr extended_parameters_size = 8; + static std::size_t constexpr size = sizeof (start) + sizeof (end); +}; + +class bulk_pull_account final : public message +{ +public: + explicit bulk_pull_account (nano::network_constants const & constants); + bulk_pull_account (bool &, nano::stream &, nano::message_header const &); + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &); + void visit (nano::message_visitor &) const override; + nano::account account; + nano::amount minimum_amount; + bulk_pull_account_flags flags; + static std::size_t constexpr size = sizeof (account) + sizeof (minimum_amount) + sizeof (bulk_pull_account_flags); +}; + +class bulk_push final : public message +{ +public: + explicit bulk_push (nano::network_constants const & constants); + explicit bulk_push (nano::message_header const &); + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &); + void visit (nano::message_visitor &) const override; +}; + +class node_id_handshake final : public message +{ +public: + node_id_handshake (bool &, nano::stream &, nano::message_header const &); + node_id_handshake (nano::network_constants const & constants, boost::optional, boost::optional>); + void serialize (nano::stream &) const override; + bool deserialize (nano::stream &); + void visit (nano::message_visitor &) const override; + bool operator== (nano::node_id_handshake const &) const; + boost::optional query; + boost::optional> response; + std::size_t size () const; + static std::size_t size (nano::message_header const &); +}; + +class message_visitor +{ +public: + virtual ~message_visitor () = default; + + virtual void keepalive (nano::keepalive const & message) + { + default_handler (message); + }; + virtual void publish (nano::publish const & message) + { + default_handler (message); + } + virtual void confirm_req (nano::confirm_req const & message) + { + default_handler (message); + } + virtual void confirm_ack (nano::confirm_ack const & message) + { + default_handler (message); + } + virtual void bulk_pull (nano::bulk_pull const & message) + { + default_handler (message); + } + virtual void bulk_pull_account (nano::bulk_pull_account const & message) + { + default_handler (message); + } + virtual void bulk_push (nano::bulk_push const & message) + { + default_handler (message); + } + virtual void frontier_req (nano::frontier_req const & message) + { + default_handler (message); + } + virtual void node_id_handshake (nano::node_id_handshake const & message) + { + default_handler (message); + } + virtual void telemetry_req (nano::telemetry_req const & message) + { + default_handler (message); + } + virtual void telemetry_ack (nano::telemetry_ack const & message) + { + default_handler (message); + } + virtual void default_handler (nano::message const &){}; +}; +} \ No newline at end of file diff --git a/nano/node/telemetry.hpp b/nano/node/telemetry.hpp index 4e81fa5d6..02dac1aff 100644 --- a/nano/node/telemetry.hpp +++ b/nano/node/telemetry.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -77,7 +78,7 @@ public: /* * This makes a telemetry request to the specific channel. - * Error is set for: no response received, no payload received, invalid signature or unsound metrics in message (e.g different genesis block) + * Error is set for: no response received, no payload received, invalid signature or unsound metrics in message (e.g different genesis block) */ void get_metrics_single_peer_async (std::shared_ptr const &, std::function const &); diff --git a/nano/node/transport/transport.cpp b/nano/node/transport/transport.cpp index 7c342b87d..8337934cc 100644 --- a/nano/node/transport/transport.cpp +++ b/nano/node/transport/transport.cpp @@ -54,7 +54,7 @@ nano::transport::channel::channel (nano::node & node_a) : void nano::transport::channel::send (nano::message & message_a, std::function const & callback_a, nano::buffer_drop_policy drop_policy_a) { auto buffer (message_a.to_shared_const_buffer ()); - auto detail = nano::message_type_to_stat_detail (message_a.header.type); + auto detail = nano::to_stat_detail (message_a.header.type); auto is_droppable_by_limiter = drop_policy_a == nano::buffer_drop_policy::limiter; auto should_drop (node.network.limiter.should_drop (buffer.size ())); if (!is_droppable_by_limiter || !should_drop) diff --git a/nano/node/transport/transport.hpp b/nano/node/transport/transport.hpp index 9084f58a4..a1ee6e493 100644 --- a/nano/node/transport/transport.hpp +++ b/nano/node/transport/transport.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/nano/test_common/telemetry.cpp b/nano/test_common/telemetry.cpp index 85f894645..77cd32c7f 100644 --- a/nano/test_common/telemetry.cpp +++ b/nano/test_common/telemetry.cpp @@ -1,4 +1,5 @@ #include +#include #include #include