diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index 65cb8ac8..76c9dce4 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -143,6 +143,8 @@ bool nano::public_key::decode_account (std::string const & source_a) return error; } +nano::uint256_union const nano::uint256_union::zero{ 0 }; + nano::uint256_union::uint256_union (nano::uint256_t const & number_a) { bytes.fill (0); diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index d73310d1..bd05981f 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -93,6 +93,8 @@ public: std::array qwords; std::array owords; }; + + static const uint256_union zero; }; inline bool operator== (nano::uint256_union const & lhs, nano::uint256_union const & rhs) { diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 00bc9587..7473d85c 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -242,6 +242,8 @@ enum class detail : uint8_t response_blocks, response_account_info, channel_full, + response_frontiers, + frontiers, // backlog activated, diff --git a/nano/node/bootstrap/bootstrap_server.cpp b/nano/node/bootstrap/bootstrap_server.cpp index 5bfc9592..a11c5657 100644 --- a/nano/node/bootstrap/bootstrap_server.cpp +++ b/nano/node/bootstrap/bootstrap_server.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -21,7 +22,6 @@ nano::bootstrap_server::bootstrap_server (nano::store::component & store_a, nano nano::bootstrap_server::~bootstrap_server () { - stop (); } void nano::bootstrap_server::start () @@ -42,6 +42,7 @@ bool nano::bootstrap_server::verify_request_type (nano::asc_pull_type type) cons return false; case asc_pull_type::blocks: case asc_pull_type::account_info: + case asc_pull_type::frontiers: return true; } return false; @@ -68,6 +69,10 @@ bool nano::bootstrap_server::verify (const nano::asc_pull_req & message) const { return !pld.target.is_zero (); } + bool operator() (nano::asc_pull_req::frontiers_payload const & pld) const + { + return pld.count > 0 && pld.count <= max_frontiers && !pld.start.is_zero (); + } }; return std::visit (verify_visitor{}, message.payload); @@ -115,6 +120,11 @@ void nano::bootstrap_server::respond (nano::asc_pull_ack & response, std::shared { stats.inc (nano::stat::type::bootstrap_server, nano::stat::detail::response_account_info, nano::stat::dir::out); } + void operator() (nano::asc_pull_ack::frontiers_payload const & pld) + { + stats.inc (nano::stat::type::bootstrap_server, nano::stat::detail::response_frontiers, nano::stat::dir::out); + stats.add (nano::stat::type::bootstrap_server, nano::stat::detail::frontiers, nano::stat::dir::out, pld.frontiers.size ()); + } }; std::visit (stat_visitor{ stats }, response.payload); @@ -168,7 +178,7 @@ nano::asc_pull_ack nano::bootstrap_server::process (const store::transaction &, } /* - * Blocks response + * Blocks request */ nano::asc_pull_ack nano::bootstrap_server::process (store::transaction const & transaction, nano::asc_pull_req::id_t id, nano::asc_pull_req::blocks_payload const & request) @@ -253,7 +263,7 @@ std::vector> nano::bootstrap_server::prepare_blocks } /* - * Account info response + * Account info request */ nano::asc_pull_ack nano::bootstrap_server::process (const store::transaction & transaction, nano::asc_pull_req::id_t id, const nano::asc_pull_req::account_info_payload & request) @@ -301,3 +311,14 @@ nano::asc_pull_ack nano::bootstrap_server::process (const store::transaction & t response.update_header (); return response; } + +/* + * Frontiers request + */ + +nano::asc_pull_ack nano::bootstrap_server::process (const store::transaction & transaction, nano::asc_pull_req::id_t id, const nano::asc_pull_req::frontiers_payload & request) +{ + debug_assert (false, "not implemented"); + nano::asc_pull_ack ack{ network_constants }; + return ack; +} \ No newline at end of file diff --git a/nano/node/bootstrap/bootstrap_server.hpp b/nano/node/bootstrap/bootstrap_server.hpp index 98fd3823..e1358b4c 100644 --- a/nano/node/bootstrap/bootstrap_server.hpp +++ b/nano/node/bootstrap/bootstrap_server.hpp @@ -57,7 +57,7 @@ private: nano::asc_pull_ack process (store::transaction const &, nano::asc_pull_req::id_t id, nano::empty_payload const & request); /* - * Blocks response + * Blocks request */ nano::asc_pull_ack process (store::transaction const &, nano::asc_pull_req::id_t id, nano::asc_pull_req::blocks_payload const & request); nano::asc_pull_ack prepare_response (store::transaction const &, nano::asc_pull_req::id_t id, nano::block_hash start_block, std::size_t count); @@ -65,10 +65,15 @@ private: std::vector> prepare_blocks (store::transaction const &, nano::block_hash start_block, std::size_t count) const; /* - * Account info response + * Account info request */ nano::asc_pull_ack process (store::transaction const &, nano::asc_pull_req::id_t id, nano::asc_pull_req::account_info_payload const & request); + /* + * Frontiers request + */ + nano::asc_pull_ack process (store::transaction const &, nano::asc_pull_req::id_t id, nano::asc_pull_req::frontiers_payload const & request); + /* * Checks if the request should be dropped early on */ @@ -82,10 +87,11 @@ private: // Dependencies nano::stats & stats; private: - processing_queue request_queue; + nano::processing_queue request_queue; public: // Config /** Maximum number of blocks to send in a single response, cannot be higher than capacity of a single `asc_pull_ack` message */ constexpr static std::size_t max_blocks = nano::asc_pull_ack::blocks_payload::max_blocks; + constexpr static std::size_t max_frontiers = nano::asc_pull_ack::frontiers_payload::max_frontiers; }; } diff --git a/nano/node/bootstrap_ascending/service.cpp b/nano/node/bootstrap_ascending/service.cpp index 52b9b317..facea3f9 100644 --- a/nano/node/bootstrap_ascending/service.cpp +++ b/nano/node/bootstrap_ascending/service.cpp @@ -366,6 +366,7 @@ void nano::bootstrap_ascending::service::process (nano::asc_pull_ack const & mes on_reply.notify (tag); condition.notify_all (); + std::visit ([this, &tag] (auto && request) { return process (request, tag); }, message.payload); } else @@ -416,6 +417,11 @@ void nano::bootstrap_ascending::service::process (const nano::asc_pull_ack::acco // TODO: Make use of account info } +void nano::bootstrap_ascending::service::process (const nano::asc_pull_ack::frontiers_payload & response, const nano::bootstrap_ascending::service::async_tag & tag) +{ + // TODO: Make use of frontiers info +} + void nano::bootstrap_ascending::service::process (const nano::empty_payload & response, const nano::bootstrap_ascending::service::async_tag & tag) { // Should not happen diff --git a/nano/node/bootstrap_ascending/service.hpp b/nano/node/bootstrap_ascending/service.hpp index a518b3b3..3c7a436f 100644 --- a/nano/node/bootstrap_ascending/service.hpp +++ b/nano/node/bootstrap_ascending/service.hpp @@ -109,6 +109,7 @@ namespace bootstrap_ascending void process (nano::asc_pull_ack::blocks_payload const & response, async_tag const & tag); void process (nano::asc_pull_ack::account_info_payload const & response, async_tag const & tag); + void process (nano::asc_pull_ack::frontiers_payload const & response, async_tag const & tag); void process (nano::empty_payload const & response, async_tag const & tag); enum class verify_result diff --git a/nano/node/messages.cpp b/nano/node/messages.cpp index 0f4699c0..0837b756 100644 --- a/nano/node/messages.cpp +++ b/nano/node/messages.cpp @@ -1611,6 +1611,13 @@ void nano::asc_pull_req::deserialize_payload (nano::stream & stream) payload = pld; break; } + case asc_pull_type::frontiers: + { + frontiers_payload pld; + pld.deserialize (stream); + payload = pld; + break; + } default: throw std::runtime_error ("Unknown asc_pull_type"); } @@ -1618,12 +1625,13 @@ void nano::asc_pull_req::deserialize_payload (nano::stream & stream) void nano::asc_pull_req::update_header () { + // TODO: Avoid serializing the payload twice std::vector bytes; { nano::vectorstream payload_stream (bytes); serialize_payload (payload_stream); } - debug_assert (bytes.size () <= 65535u); // Max int16 for storing size + debug_assert (bytes.size () <= std::numeric_limits::max ()); // Max uint16 for storing size debug_assert (bytes.size () >= 1); header.extensions = std::bitset<16> (bytes.size ()); } @@ -1652,6 +1660,10 @@ bool nano::asc_pull_req::verify_consistency () const { debug_assert (type == asc_pull_type::account_info); } + void operator() (frontiers_payload) const + { + debug_assert (type == asc_pull_type::frontiers); + } }; std::visit (consistency_visitor{ type }, payload); return true; // Just for convenience of calling from asserts @@ -1720,6 +1732,22 @@ void nano::asc_pull_req::account_info_payload::deserialize (stream & stream) nano::read (stream, target_type); } +/* + * asc_pull_req::frontiers_payload + */ + +void nano::asc_pull_req::frontiers_payload::serialize (nano::stream & stream) const +{ + nano::write (stream, start); + nano::write_big_endian (stream, count); +} + +void nano::asc_pull_req::frontiers_payload::deserialize (nano::stream & stream) +{ + nano::read (stream, start); + nano::read_big_endian (stream, count); +} + /* * asc_pull_ack */ @@ -1742,7 +1770,7 @@ void nano::asc_pull_ack::visit (nano::message_visitor & visitor) const void nano::asc_pull_ack::serialize (nano::stream & stream) const { - debug_assert (header.extensions.to_ulong () > 0); // Block payload must have least `not_a_block` terminator + debug_assert (header.extensions.to_ulong () > 0); // Block payload must have at least `not_a_block` terminator header.serialize (stream); nano::write (stream, type); nano::write_big_endian (stream, id); @@ -1793,6 +1821,13 @@ void nano::asc_pull_ack::deserialize_payload (nano::stream & stream) payload = pld; break; } + case asc_pull_type::frontiers: + { + frontiers_payload pld; + pld.deserialize (stream); + payload = pld; + break; + } default: throw std::runtime_error ("Unknown asc_pull_type"); } @@ -1800,12 +1835,13 @@ void nano::asc_pull_ack::deserialize_payload (nano::stream & stream) void nano::asc_pull_ack::update_header () { + // TODO: Avoid serializing the payload twice std::vector bytes; { nano::vectorstream payload_stream (bytes); serialize_payload (payload_stream); } - debug_assert (bytes.size () <= 65535u); // Max int16 for storing size + debug_assert (bytes.size () <= std::numeric_limits::max ()); // Max uint16 for storing size debug_assert (bytes.size () >= 1); header.extensions = std::bitset<16> (bytes.size ()); } @@ -1834,6 +1870,10 @@ bool nano::asc_pull_ack::verify_consistency () const { debug_assert (type == asc_pull_type::account_info); } + void operator() (frontiers_payload) const + { + debug_assert (type == asc_pull_type::frontiers); + } }; std::visit (consistency_visitor{ type }, payload); return true; // Just for convenience of calling from asserts @@ -1927,3 +1967,34 @@ void nano::asc_pull_ack::account_info_payload::deserialize (nano::stream & strea nano::read (stream, account_conf_frontier); nano::read_big_endian (stream, account_conf_height); } + +/* + * asc_pull_ack::frontiers_payload + */ + +void nano::asc_pull_ack::frontiers_payload::serialize (nano::stream & stream) const +{ + for (auto const & [account, frontier] : frontiers) + { + nano::write (stream, account); + nano::write (stream, frontier); + } + nano::write (stream, nano::account::zero); + nano::write (stream, nano::block_hash::zero); +} + +void nano::asc_pull_ack::frontiers_payload::deserialize (nano::stream & stream) +{ + nano::account account; + nano::block_hash frontier; + while (frontiers.size () < max_frontiers) + { + nano::read (stream, account); + nano::read (stream, frontier); + if (account.is_zero () || frontier.is_zero ()) + { + break; + } + frontiers.emplace_back (account, frontier); + } +} \ No newline at end of file diff --git a/nano/node/messages.hpp b/nano/node/messages.hpp index bf4ab2b2..4bf38e12 100644 --- a/nano/node/messages.hpp +++ b/nano/node/messages.hpp @@ -389,11 +389,11 @@ enum class asc_pull_type : uint8_t invalid = 0x0, blocks = 0x1, account_info = 0x2, + frontiers = 0x3, }; -class empty_payload +struct empty_payload { -public: void serialize (nano::stream &) const { debug_assert (false); @@ -444,36 +444,40 @@ public: // Payload definitions block = 1, }; - class blocks_payload + struct blocks_payload { - public: void serialize (nano::stream &) const; void deserialize (nano::stream &); - public: nano::hash_or_account start{ 0 }; uint8_t count{ 0 }; - asc_pull_req::hash_type start_type{ 0 }; + hash_type start_type{ 0 }; }; - class account_info_payload + struct account_info_payload { - public: void serialize (nano::stream &) const; void deserialize (nano::stream &); - public: nano::hash_or_account target{ 0 }; - asc_pull_req::hash_type target_type{ 0 }; + hash_type target_type{ 0 }; + }; + + struct frontiers_payload + { + void serialize (nano::stream &) const; + void deserialize (nano::stream &); + + nano::account start{ 0 }; + uint16_t count{ 0 }; }; public: // Payload - /** Currently unused, allows extensions in the future */ asc_pull_type type{ asc_pull_type::invalid }; id_t id{ 0 }; /** Payload depends on `asc_pull_type` */ - std::variant payload; + std::variant payload; public: /** Size of message without payload */ @@ -514,27 +518,22 @@ private: // Debug bool verify_consistency () const; public: // Payload definitions - class blocks_payload + struct blocks_payload { - public: void serialize (nano::stream &) const; void deserialize (nano::stream &); - public: - std::vector> blocks{}; + std::vector> blocks; - public: - /* Header allows for 16 bit extensions; 65535 bytes / 500 bytes (block size with some future margin) ~ 131 */ + /* Header allows for 16 bit extensions; 65536 bytes / 500 bytes (block size with some future margin) ~ 131 */ constexpr static std::size_t max_blocks = 128; }; - class account_info_payload + struct account_info_payload { - public: void serialize (nano::stream &) const; void deserialize (nano::stream &); - public: nano::account account{ 0 }; nano::block_hash account_open{ 0 }; nano::block_hash account_head{ 0 }; @@ -543,13 +542,23 @@ public: // Payload definitions uint64_t account_conf_height{ 0 }; }; + struct frontiers_payload + { + void serialize (nano::stream &) const; + void deserialize (nano::stream &); + + std::vector> frontiers; + + /* Header allows for 16 bit extensions; 65536 bytes / 64 bytes (account + frontier) ~ 1024, but we need some space for null frontier terminator*/ + constexpr static std::size_t max_frontiers = 1000; + }; + public: // Payload - /** Currently unused, allows extensions in the future */ asc_pull_type type{ asc_pull_type::invalid }; id_t id{ 0 }; /** Payload depends on `asc_pull_type` */ - std::variant payload; + std::variant payload; public: /** Size of message without payload */