Merge pull request #4337 from pwojcikdev/stateless-frontier-server
Support for frontier requests in stateless bootstrap server
This commit is contained in:
commit
1ef520a258
10 changed files with 431 additions and 81 deletions
|
@ -5,6 +5,7 @@
|
|||
#include <gtest/gtest.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
@ -31,6 +32,13 @@ public:
|
|||
return responses.size ();
|
||||
}
|
||||
|
||||
void connect (nano::bootstrap_server & server)
|
||||
{
|
||||
server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
add (response);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
nano::mutex mutex;
|
||||
std::vector<nano::asc_pull_ack> responses;
|
||||
|
@ -65,9 +73,7 @@ TEST (bootstrap_server, serve_account_blocks)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 1, 128);
|
||||
auto [first_account, first_blocks] = chains.front ();
|
||||
|
@ -77,7 +83,7 @@ TEST (bootstrap_server, serve_account_blocks)
|
|||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_req::blocks_payload request_payload;
|
||||
nano::asc_pull_req::blocks_payload request_payload{};
|
||||
request_payload.start = first_account;
|
||||
request_payload.count = nano::bootstrap_server::max_blocks;
|
||||
request_payload.start_type = nano::asc_pull_req::hash_type::account;
|
||||
|
@ -109,9 +115,7 @@ TEST (bootstrap_server, serve_hash)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 1, 256);
|
||||
auto [account, blocks] = chains.front ();
|
||||
|
@ -124,7 +128,7 @@ TEST (bootstrap_server, serve_hash)
|
|||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_req::blocks_payload request_payload;
|
||||
nano::asc_pull_req::blocks_payload request_payload{};
|
||||
request_payload.start = blocks.front ()->hash ();
|
||||
request_payload.count = nano::bootstrap_server::max_blocks;
|
||||
request_payload.start_type = nano::asc_pull_req::hash_type::block;
|
||||
|
@ -156,9 +160,7 @@ TEST (bootstrap_server, serve_hash_one)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 1, 256);
|
||||
auto [account, blocks] = chains.front ();
|
||||
|
@ -171,7 +173,7 @@ TEST (bootstrap_server, serve_hash_one)
|
|||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_req::blocks_payload request_payload;
|
||||
nano::asc_pull_req::blocks_payload request_payload{};
|
||||
request_payload.start = blocks.front ()->hash ();
|
||||
request_payload.count = 1;
|
||||
request_payload.start_type = nano::asc_pull_req::hash_type::block;
|
||||
|
@ -200,9 +202,7 @@ TEST (bootstrap_server, serve_end_of_chain)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 1, 128);
|
||||
auto [account, blocks] = chains.front ();
|
||||
|
@ -212,7 +212,7 @@ TEST (bootstrap_server, serve_end_of_chain)
|
|||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_req::blocks_payload request_payload;
|
||||
nano::asc_pull_req::blocks_payload request_payload{};
|
||||
request_payload.start = blocks.back ()->hash ();
|
||||
request_payload.count = nano::bootstrap_server::max_blocks;
|
||||
request_payload.start_type = nano::asc_pull_req::hash_type::block;
|
||||
|
@ -242,9 +242,7 @@ TEST (bootstrap_server, serve_missing)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 1, 128);
|
||||
|
||||
|
@ -253,7 +251,7 @@ TEST (bootstrap_server, serve_missing)
|
|||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_req::blocks_payload request_payload;
|
||||
nano::asc_pull_req::blocks_payload request_payload{};
|
||||
request_payload.start = nano::test::random_hash ();
|
||||
request_payload.count = nano::bootstrap_server::max_blocks;
|
||||
request_payload.start_type = nano::asc_pull_req::hash_type::block;
|
||||
|
@ -282,9 +280,7 @@ TEST (bootstrap_server, serve_multiple)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 32, 16);
|
||||
|
||||
|
@ -298,7 +294,7 @@ TEST (bootstrap_server, serve_multiple)
|
|||
request.id = next_id++;
|
||||
request.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_req::blocks_payload request_payload;
|
||||
nano::asc_pull_req::blocks_payload request_payload{};
|
||||
request_payload.start = account;
|
||||
request_payload.count = nano::bootstrap_server::max_blocks;
|
||||
request_payload.start_type = nano::asc_pull_req::hash_type::account;
|
||||
|
@ -345,9 +341,7 @@ TEST (bootstrap_server, serve_account_info)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 1, 128);
|
||||
auto [account, blocks] = chains.front ();
|
||||
|
@ -357,7 +351,7 @@ TEST (bootstrap_server, serve_account_info)
|
|||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::account_info;
|
||||
|
||||
nano::asc_pull_req::account_info_payload request_payload;
|
||||
nano::asc_pull_req::account_info_payload request_payload{};
|
||||
request_payload.target = account;
|
||||
request_payload.target_type = nano::asc_pull_req::hash_type::account;
|
||||
|
||||
|
@ -393,9 +387,7 @@ TEST (bootstrap_server, serve_account_info_missing)
|
|||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
node.bootstrap_server.on_response.add ([&] (auto & response, auto & channel) {
|
||||
responses.add (response);
|
||||
});
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, 1, 128);
|
||||
auto [account, blocks] = chains.front ();
|
||||
|
@ -405,7 +397,7 @@ TEST (bootstrap_server, serve_account_info_missing)
|
|||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::account_info;
|
||||
|
||||
nano::asc_pull_req::account_info_payload request_payload;
|
||||
nano::asc_pull_req::account_info_payload request_payload{};
|
||||
request_payload.target = nano::test::random_account ();
|
||||
request_payload.target_type = nano::asc_pull_req::hash_type::account;
|
||||
|
||||
|
@ -434,3 +426,123 @@ TEST (bootstrap_server, serve_account_info_missing)
|
|||
// Ensure we don't get any unexpected responses
|
||||
ASSERT_ALWAYS (1s, responses.size () == 1);
|
||||
}
|
||||
|
||||
TEST (bootstrap_server, serve_frontiers)
|
||||
{
|
||||
nano::test::system system{};
|
||||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, /* chain count */ 32, /* block count */ 4);
|
||||
|
||||
// Request all frontiers
|
||||
nano::asc_pull_req request{ node.network_params.network };
|
||||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::frontiers;
|
||||
|
||||
nano::asc_pull_req::frontiers_payload request_payload{};
|
||||
request_payload.count = nano::bootstrap_server::max_frontiers;
|
||||
request_payload.start = 0;
|
||||
|
||||
request.payload = request_payload;
|
||||
request.update_header ();
|
||||
|
||||
node.network.inbound (request, nano::test::fake_channel (node));
|
||||
|
||||
ASSERT_TIMELY (5s, responses.size () == 1);
|
||||
|
||||
auto response = responses.get ().front ();
|
||||
// Ensure we got response exactly for what we asked for
|
||||
ASSERT_EQ (response.id, 7);
|
||||
ASSERT_EQ (response.type, nano::asc_pull_type::frontiers);
|
||||
|
||||
nano::asc_pull_ack::frontiers_payload response_payload;
|
||||
ASSERT_NO_THROW (response_payload = std::get<nano::asc_pull_ack::frontiers_payload> (response.payload));
|
||||
|
||||
ASSERT_EQ (response_payload.frontiers.size (), chains.size () + 1); // +1 for genesis
|
||||
|
||||
// Ensure frontiers match what we expect
|
||||
std::map<nano::account, nano::block_hash> expected_frontiers;
|
||||
for (auto & [account, blocks] : chains)
|
||||
{
|
||||
expected_frontiers[account] = blocks.back ()->hash ();
|
||||
}
|
||||
expected_frontiers[nano::dev::genesis_key.pub] = node.latest (nano::dev::genesis_key.pub);
|
||||
|
||||
for (auto & [account, frontier] : response_payload.frontiers)
|
||||
{
|
||||
ASSERT_EQ (frontier, expected_frontiers[account]);
|
||||
expected_frontiers.erase (account);
|
||||
}
|
||||
ASSERT_TRUE (expected_frontiers.empty ());
|
||||
}
|
||||
|
||||
TEST (bootstrap_server, serve_frontiers_invalid_count)
|
||||
{
|
||||
nano::test::system system{};
|
||||
auto & node = *system.add_node ();
|
||||
|
||||
responses_helper responses;
|
||||
responses.connect (node.bootstrap_server);
|
||||
|
||||
auto chains = nano::test::setup_chains (system, node, /* chain count */ 4, /* block count */ 4);
|
||||
|
||||
// Zero count
|
||||
{
|
||||
nano::asc_pull_req request{ node.network_params.network };
|
||||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::frontiers;
|
||||
|
||||
nano::asc_pull_req::frontiers_payload request_payload{};
|
||||
request_payload.count = 0;
|
||||
request_payload.start = 0;
|
||||
|
||||
request.payload = request_payload;
|
||||
request.update_header ();
|
||||
|
||||
node.network.inbound (request, nano::test::fake_channel (node));
|
||||
}
|
||||
|
||||
ASSERT_TIMELY_EQ (5s, node.stats.count (nano::stat::type::bootstrap_server, nano::stat::detail::invalid), 1);
|
||||
|
||||
// Count larger than allowed
|
||||
{
|
||||
nano::asc_pull_req request{ node.network_params.network };
|
||||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::frontiers;
|
||||
|
||||
nano::asc_pull_req::frontiers_payload request_payload{};
|
||||
request_payload.count = nano::bootstrap_server::max_frontiers + 1;
|
||||
request_payload.start = 0;
|
||||
|
||||
request.payload = request_payload;
|
||||
request.update_header ();
|
||||
|
||||
node.network.inbound (request, nano::test::fake_channel (node));
|
||||
}
|
||||
|
||||
ASSERT_TIMELY_EQ (5s, node.stats.count (nano::stat::type::bootstrap_server, nano::stat::detail::invalid), 2);
|
||||
|
||||
// Max numeric value
|
||||
{
|
||||
nano::asc_pull_req request{ node.network_params.network };
|
||||
request.id = 7;
|
||||
request.type = nano::asc_pull_type::frontiers;
|
||||
|
||||
nano::asc_pull_req::frontiers_payload request_payload{};
|
||||
request_payload.count = std::numeric_limits<decltype (request_payload.count)>::max ();
|
||||
request_payload.start = 0;
|
||||
|
||||
request.payload = request_payload;
|
||||
request.update_header ();
|
||||
|
||||
node.network.inbound (request, nano::test::fake_channel (node));
|
||||
}
|
||||
|
||||
ASSERT_TIMELY_EQ (5s, node.stats.count (nano::stat::type::bootstrap_server, nano::stat::detail::invalid), 3);
|
||||
|
||||
// Ensure we don't get any unexpected responses
|
||||
ASSERT_ALWAYS (1s, responses.size () == 0);
|
||||
}
|
|
@ -262,7 +262,7 @@ TEST (message, asc_pull_req_serialization_blocks)
|
|||
original.id = 7;
|
||||
original.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_req::blocks_payload original_payload;
|
||||
nano::asc_pull_req::blocks_payload original_payload{};
|
||||
original_payload.start = nano::test::random_hash ();
|
||||
original_payload.count = 111;
|
||||
|
||||
|
@ -303,7 +303,7 @@ TEST (message, asc_pull_req_serialization_account_info)
|
|||
original.id = 7;
|
||||
original.type = nano::asc_pull_type::account_info;
|
||||
|
||||
nano::asc_pull_req::account_info_payload original_payload;
|
||||
nano::asc_pull_req::account_info_payload original_payload{};
|
||||
original_payload.target = nano::test::random_hash ();
|
||||
|
||||
original.payload = original_payload;
|
||||
|
@ -336,16 +336,55 @@ TEST (message, asc_pull_req_serialization_account_info)
|
|||
ASSERT_TRUE (nano::at_end (stream));
|
||||
}
|
||||
|
||||
TEST (message, asc_pull_req_serialization_frontiers)
|
||||
{
|
||||
nano::asc_pull_req original{ nano::dev::network_params.network };
|
||||
original.id = 7;
|
||||
original.type = nano::asc_pull_type::frontiers;
|
||||
|
||||
nano::asc_pull_req::frontiers_payload original_payload{};
|
||||
original_payload.start = nano::test::random_account ();
|
||||
original_payload.count = 123;
|
||||
|
||||
original.payload = original_payload;
|
||||
original.update_header ();
|
||||
|
||||
// Serialize
|
||||
std::vector<uint8_t> bytes;
|
||||
{
|
||||
nano::vectorstream stream{ bytes };
|
||||
original.serialize (stream);
|
||||
}
|
||||
nano::bufferstream stream{ bytes.data (), bytes.size () };
|
||||
|
||||
// Header
|
||||
bool error = false;
|
||||
nano::message_header header (error, stream);
|
||||
ASSERT_FALSE (error);
|
||||
ASSERT_EQ (nano::message_type::asc_pull_req, header.type);
|
||||
|
||||
// Message
|
||||
nano::asc_pull_req message (error, stream, header);
|
||||
ASSERT_FALSE (error);
|
||||
ASSERT_EQ (original.id, message.id);
|
||||
ASSERT_EQ (original.type, message.type);
|
||||
|
||||
nano::asc_pull_req::frontiers_payload message_payload;
|
||||
ASSERT_NO_THROW (message_payload = std::get<nano::asc_pull_req::frontiers_payload> (message.payload));
|
||||
ASSERT_EQ (original_payload.start, message_payload.start);
|
||||
ASSERT_EQ (original_payload.count, message_payload.count);
|
||||
|
||||
ASSERT_TRUE (nano::at_end (stream));
|
||||
}
|
||||
|
||||
TEST (message, asc_pull_ack_serialization_blocks)
|
||||
{
|
||||
nano::asc_pull_ack original{ nano::dev::network_params.network };
|
||||
original.id = 11;
|
||||
original.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_ack::blocks_payload original_payload;
|
||||
// Generate blocks
|
||||
const int num_blocks = 128; // Maximum allowed
|
||||
for (int n = 0; n < num_blocks; ++n)
|
||||
nano::asc_pull_ack::blocks_payload original_payload{};
|
||||
for (int n = 0; n < nano::asc_pull_ack::blocks_payload::max_blocks; ++n)
|
||||
{
|
||||
original_payload.blocks.push_back (random_block ());
|
||||
}
|
||||
|
@ -391,7 +430,7 @@ TEST (message, asc_pull_ack_serialization_account_info)
|
|||
original.id = 11;
|
||||
original.type = nano::asc_pull_type::account_info;
|
||||
|
||||
nano::asc_pull_ack::account_info_payload original_payload;
|
||||
nano::asc_pull_ack::account_info_payload original_payload{};
|
||||
original_payload.account = nano::test::random_account ();
|
||||
original_payload.account_open = nano::test::random_hash ();
|
||||
original_payload.account_head = nano::test::random_hash ();
|
||||
|
@ -435,6 +474,49 @@ TEST (message, asc_pull_ack_serialization_account_info)
|
|||
ASSERT_TRUE (nano::at_end (stream));
|
||||
}
|
||||
|
||||
TEST (message, asc_pull_ack_serialization_frontiers)
|
||||
{
|
||||
nano::asc_pull_ack original{ nano::dev::network_params.network };
|
||||
original.id = 11;
|
||||
original.type = nano::asc_pull_type::frontiers;
|
||||
|
||||
nano::asc_pull_ack::frontiers_payload original_payload{};
|
||||
for (int n = 0; n < nano::asc_pull_ack::frontiers_payload::max_frontiers; ++n)
|
||||
{
|
||||
original_payload.frontiers.push_back ({ nano::test::random_account (), nano::test::random_hash () });
|
||||
}
|
||||
|
||||
original.payload = original_payload;
|
||||
original.update_header ();
|
||||
|
||||
// Serialize
|
||||
std::vector<uint8_t> bytes;
|
||||
{
|
||||
nano::vectorstream stream{ bytes };
|
||||
original.serialize (stream);
|
||||
}
|
||||
nano::bufferstream stream{ bytes.data (), bytes.size () };
|
||||
|
||||
// Header
|
||||
bool error = false;
|
||||
nano::message_header header (error, stream);
|
||||
ASSERT_FALSE (error);
|
||||
ASSERT_EQ (nano::message_type::asc_pull_ack, header.type);
|
||||
|
||||
// Message
|
||||
nano::asc_pull_ack message (error, stream, header);
|
||||
ASSERT_FALSE (error);
|
||||
ASSERT_EQ (original.id, message.id);
|
||||
ASSERT_EQ (original.type, message.type);
|
||||
|
||||
nano::asc_pull_ack::frontiers_payload message_payload;
|
||||
ASSERT_NO_THROW (message_payload = std::get<nano::asc_pull_ack::frontiers_payload> (message.payload));
|
||||
|
||||
ASSERT_EQ (original_payload.frontiers, message_payload.frontiers);
|
||||
|
||||
ASSERT_TRUE (nano::at_end (stream));
|
||||
}
|
||||
|
||||
TEST (message, node_id_handshake_query_serialization)
|
||||
{
|
||||
nano::node_id_handshake::query_payload query{};
|
||||
|
|
|
@ -372,10 +372,12 @@ public:
|
|||
|
||||
/** Initial value is ACTIVE_NETWORK compile flag, but can be overridden by a CLI flag */
|
||||
static nano::networks active_network;
|
||||
|
||||
/** Current protocol version */
|
||||
uint8_t const protocol_version = 0x13;
|
||||
uint8_t const protocol_version = 0x14;
|
||||
/** Minimum accepted protocol version */
|
||||
uint8_t const protocol_version_min = 0x12;
|
||||
|
||||
/** Minimum accepted protocol version used when bootstrapping */
|
||||
uint8_t const bootstrap_protocol_version_min = 0x13;
|
||||
};
|
||||
|
|
|
@ -242,6 +242,8 @@ enum class detail : uint8_t
|
|||
response_blocks,
|
||||
response_account_info,
|
||||
channel_full,
|
||||
response_frontiers,
|
||||
frontiers,
|
||||
|
||||
// backlog
|
||||
activated,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/node/bootstrap/bootstrap_server.hpp>
|
||||
#include <nano/node/transport/channel.hpp>
|
||||
#include <nano/node/transport/transport.hpp>
|
||||
#include <nano/secure/ledger.hpp>
|
||||
#include <nano/store/account.hpp>
|
||||
#include <nano/store/block.hpp>
|
||||
#include <nano/store/component.hpp>
|
||||
#include <nano/store/confirmation_height.hpp>
|
||||
|
@ -21,7 +23,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 +43,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 +70,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;
|
||||
}
|
||||
};
|
||||
|
||||
return std::visit (verify_visitor{}, message.payload);
|
||||
|
@ -115,6 +121,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);
|
||||
|
||||
|
@ -140,6 +151,8 @@ void nano::bootstrap_server::process_batch (std::deque<request_t> & batch)
|
|||
|
||||
for (auto & [request, channel] : batch)
|
||||
{
|
||||
transaction.refresh_if_needed ();
|
||||
|
||||
if (!channel->max (nano::transport::traffic_type::bootstrap))
|
||||
{
|
||||
auto response = process (transaction, request);
|
||||
|
@ -168,7 +181,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)
|
||||
|
@ -203,7 +216,7 @@ nano::asc_pull_ack nano::bootstrap_server::process (store::transaction const & t
|
|||
|
||||
nano::asc_pull_ack nano::bootstrap_server::prepare_response (store::transaction const & transaction, nano::asc_pull_req::id_t id, nano::block_hash start_block, std::size_t count)
|
||||
{
|
||||
debug_assert (count <= max_blocks);
|
||||
debug_assert (count <= max_blocks); // Should be filtered out earlier
|
||||
|
||||
auto blocks = prepare_blocks (transaction, start_block, count);
|
||||
debug_assert (blocks.size () <= count);
|
||||
|
@ -212,7 +225,7 @@ nano::asc_pull_ack nano::bootstrap_server::prepare_response (store::transaction
|
|||
response.id = id;
|
||||
response.type = nano::asc_pull_type::blocks;
|
||||
|
||||
nano::asc_pull_ack::blocks_payload response_payload;
|
||||
nano::asc_pull_ack::blocks_payload response_payload{};
|
||||
response_payload.blocks = blocks;
|
||||
response.payload = response_payload;
|
||||
|
||||
|
@ -235,7 +248,7 @@ nano::asc_pull_ack nano::bootstrap_server::prepare_empty_blocks_response (nano::
|
|||
|
||||
std::vector<std::shared_ptr<nano::block>> nano::bootstrap_server::prepare_blocks (store::transaction const & transaction, nano::block_hash start_block, std::size_t count) const
|
||||
{
|
||||
debug_assert (count <= max_blocks);
|
||||
debug_assert (count <= max_blocks); // Should be filtered out earlier
|
||||
|
||||
std::vector<std::shared_ptr<nano::block>> result;
|
||||
if (!start_block.is_zero ())
|
||||
|
@ -253,7 +266,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 +314,27 @@ 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 (request.count <= max_frontiers); // Should be filtered out earlier
|
||||
|
||||
nano::asc_pull_ack response{ network_constants };
|
||||
response.id = id;
|
||||
response.type = nano::asc_pull_type::frontiers;
|
||||
|
||||
nano::asc_pull_ack::frontiers_payload response_payload{};
|
||||
|
||||
for (auto it = store.account.begin (transaction, request.start), end = store.account.end (); it != end && response_payload.frontiers.size () < request.count; ++it)
|
||||
{
|
||||
response_payload.frontiers.emplace_back (it->first, it->second.head);
|
||||
}
|
||||
|
||||
response.payload = response_payload;
|
||||
response.update_header ();
|
||||
return response;
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1556,14 +1556,21 @@ void nano::asc_pull_req::deserialize_payload (nano::stream & stream)
|
|||
{
|
||||
case asc_pull_type::blocks:
|
||||
{
|
||||
blocks_payload pld;
|
||||
blocks_payload pld{};
|
||||
pld.deserialize (stream);
|
||||
payload = pld;
|
||||
break;
|
||||
}
|
||||
case asc_pull_type::account_info:
|
||||
{
|
||||
account_info_payload pld;
|
||||
account_info_payload pld{};
|
||||
pld.deserialize (stream);
|
||||
payload = pld;
|
||||
break;
|
||||
}
|
||||
case asc_pull_type::frontiers:
|
||||
{
|
||||
frontiers_payload pld{};
|
||||
pld.deserialize (stream);
|
||||
payload = pld;
|
||||
break;
|
||||
|
@ -1575,12 +1582,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 ());
|
||||
}
|
||||
|
@ -1609,6 +1617,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
|
||||
|
@ -1677,6 +1689,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
|
||||
*/
|
||||
|
@ -1699,7 +1727,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);
|
||||
|
@ -1738,14 +1766,21 @@ void nano::asc_pull_ack::deserialize_payload (nano::stream & stream)
|
|||
{
|
||||
case asc_pull_type::blocks:
|
||||
{
|
||||
blocks_payload pld;
|
||||
blocks_payload pld{};
|
||||
pld.deserialize (stream);
|
||||
payload = pld;
|
||||
break;
|
||||
}
|
||||
case asc_pull_type::account_info:
|
||||
{
|
||||
account_info_payload pld;
|
||||
account_info_payload pld{};
|
||||
pld.deserialize (stream);
|
||||
payload = pld;
|
||||
break;
|
||||
}
|
||||
case asc_pull_type::frontiers:
|
||||
{
|
||||
frontiers_payload pld{};
|
||||
pld.deserialize (stream);
|
||||
payload = pld;
|
||||
break;
|
||||
|
@ -1757,12 +1792,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 ());
|
||||
}
|
||||
|
@ -1791,6 +1827,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
|
||||
|
@ -1842,6 +1882,7 @@ std::string nano::asc_pull_ack::to_string () const
|
|||
void nano::asc_pull_ack::blocks_payload::serialize (nano::stream & stream) const
|
||||
{
|
||||
debug_assert (blocks.size () <= max_blocks);
|
||||
|
||||
for (auto & block : blocks)
|
||||
{
|
||||
debug_assert (block != nullptr);
|
||||
|
@ -1884,3 +1925,44 @@ 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_frontier (nano::stream & stream, nano::asc_pull_ack::frontiers_payload::frontier const & frontier)
|
||||
{
|
||||
auto const & [account, hash] = frontier;
|
||||
nano::write (stream, account);
|
||||
nano::write (stream, hash);
|
||||
}
|
||||
|
||||
nano::asc_pull_ack::frontiers_payload::frontier nano::asc_pull_ack::frontiers_payload::deserialize_frontier (nano::stream & stream)
|
||||
{
|
||||
nano::account account;
|
||||
nano::block_hash hash;
|
||||
nano::read (stream, account);
|
||||
nano::read (stream, hash);
|
||||
return { account, hash };
|
||||
}
|
||||
|
||||
void nano::asc_pull_ack::frontiers_payload::serialize (nano::stream & stream) const
|
||||
{
|
||||
debug_assert (frontiers.size () <= max_frontiers);
|
||||
|
||||
for (auto const & frontier : frontiers)
|
||||
{
|
||||
serialize_frontier (stream, frontier);
|
||||
}
|
||||
serialize_frontier (stream, { nano::account{ 0 }, nano::block_hash{ 0 } });
|
||||
}
|
||||
|
||||
void nano::asc_pull_ack::frontiers_payload::deserialize (nano::stream & stream)
|
||||
{
|
||||
auto current = deserialize_frontier (stream);
|
||||
while ((!current.first.is_zero () && !current.second.is_zero ()) && frontiers.size () < max_frontiers)
|
||||
{
|
||||
frontiers.push_back (current);
|
||||
current = deserialize_frontier (stream);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -390,11 +390,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);
|
||||
|
@ -445,36 +445,43 @@ public: // Payload definitions
|
|||
block = 1,
|
||||
};
|
||||
|
||||
class blocks_payload
|
||||
struct blocks_payload
|
||||
{
|
||||
public:
|
||||
void serialize (nano::stream &) const;
|
||||
void deserialize (nano::stream &);
|
||||
|
||||
public:
|
||||
// Payload
|
||||
nano::hash_or_account start{ 0 };
|
||||
uint8_t count{ 0 };
|
||||
asc_pull_req::hash_type start_type{ 0 };
|
||||
hash_type start_type{};
|
||||
};
|
||||
|
||||
class account_info_payload
|
||||
struct account_info_payload
|
||||
{
|
||||
public:
|
||||
void serialize (nano::stream &) const;
|
||||
void deserialize (nano::stream &);
|
||||
|
||||
public:
|
||||
// Payload
|
||||
nano::hash_or_account target{ 0 };
|
||||
asc_pull_req::hash_type target_type{ 0 };
|
||||
hash_type target_type{};
|
||||
};
|
||||
|
||||
struct frontiers_payload
|
||||
{
|
||||
void serialize (nano::stream &) const;
|
||||
void deserialize (nano::stream &);
|
||||
|
||||
// Payload
|
||||
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 */
|
||||
|
@ -515,27 +522,24 @@ private: // Debug
|
|||
bool verify_consistency () const;
|
||||
|
||||
public: // Payload definitions
|
||||
class blocks_payload
|
||||
struct blocks_payload
|
||||
{
|
||||
public:
|
||||
/* 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;
|
||||
|
||||
void serialize (nano::stream &) const;
|
||||
void deserialize (nano::stream &);
|
||||
|
||||
public:
|
||||
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 */
|
||||
constexpr static std::size_t max_blocks = 128;
|
||||
// Payload
|
||||
std::vector<std::shared_ptr<nano::block>> blocks;
|
||||
};
|
||||
|
||||
class account_info_payload
|
||||
struct account_info_payload
|
||||
{
|
||||
public:
|
||||
void serialize (nano::stream &) const;
|
||||
void deserialize (nano::stream &);
|
||||
|
||||
public:
|
||||
// Payload
|
||||
nano::account account{ 0 };
|
||||
nano::block_hash account_open{ 0 };
|
||||
nano::block_hash account_head{ 0 };
|
||||
|
@ -544,13 +548,29 @@ public: // Payload definitions
|
|||
uint64_t account_conf_height{ 0 };
|
||||
};
|
||||
|
||||
struct frontiers_payload
|
||||
{
|
||||
/* 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;
|
||||
|
||||
using frontier = std::pair<nano::account, nano::block_hash>;
|
||||
|
||||
void serialize (nano::stream &) const;
|
||||
void deserialize (nano::stream &);
|
||||
|
||||
static void serialize_frontier (nano::stream &, frontier const &);
|
||||
static frontier deserialize_frontier (nano::stream &);
|
||||
|
||||
// Payload
|
||||
std::vector<frontier> frontiers;
|
||||
};
|
||||
|
||||
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 */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue