Introduce frontier info payload message

This commit is contained in:
Piotr Wójcik 2023-11-06 18:51:44 +01:00
commit 9d7782ca0b
9 changed files with 152 additions and 32 deletions

View file

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

View file

@ -93,6 +93,8 @@ public:
std::array<uint64_t, 4> qwords;
std::array<uint128_union, 2> owords;
};
static const uint256_union zero;
};
inline bool operator== (nano::uint256_union const & lhs, nano::uint256_union const & rhs)
{

View file

@ -242,6 +242,8 @@ enum class detail : uint8_t
response_blocks,
response_account_info,
channel_full,
response_frontiers,
frontiers,
// backlog
activated,

View file

@ -1,3 +1,4 @@
#include <nano/lib/utility.hpp>
#include <nano/node/bootstrap/bootstrap_server.hpp>
#include <nano/node/transport/channel.hpp>
#include <nano/node/transport/transport.hpp>
@ -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<std::shared_ptr<nano::block>> 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;
}

View file

@ -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<std::shared_ptr<nano::block>> 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_t> request_queue;
nano::processing_queue<request_t> 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;
};
}

View file

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

View file

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

View file

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

View file

@ -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<empty_payload, blocks_payload, account_info_payload> payload;
std::variant<empty_payload, blocks_payload, account_info_payload, frontiers_payload> 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<std::shared_ptr<nano::block>> blocks{};
std::vector<std::shared_ptr<nano::block>> 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<std::pair<nano::account, nano::block_hash>> 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<empty_payload, blocks_payload, account_info_payload> payload;
std::variant<empty_payload, blocks_payload, account_info_payload, frontiers_payload> payload;
public:
/** Size of message without payload */