Skip to content

Commit

Permalink
Merge pull request #4337 from pwojcikdev/stateless-frontier-server
Browse files Browse the repository at this point in the history
Support for frontier requests in stateless bootstrap server
  • Loading branch information
pwojcikdev committed Dec 1, 2023
2 parents 761db25 + 165cc91 commit 1ef520a
Show file tree
Hide file tree
Showing 10 changed files with 431 additions and 81 deletions.
176 changes: 144 additions & 32 deletions nano/core_test/bootstrap_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <gtest/gtest.h>

#include <iterator>
#include <map>

using namespace std::chrono_literals;

Expand All @@ -31,6 +32,13 @@ class responses_helper final
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;
Expand Down Expand Up @@ -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 ();
Expand All @@ -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;
Expand Down Expand Up @@ -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 ();
Expand All @@ -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;
Expand Down Expand Up @@ -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 ();
Expand All @@ -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;
Expand Down Expand Up @@ -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 ();
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand All @@ -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;
Expand Down Expand Up @@ -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 ();
Expand All @@ -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;

Expand Down Expand Up @@ -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 ();
Expand All @@ -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;

Expand Down Expand Up @@ -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);
}
Loading

0 comments on commit 1ef520a

Please sign in to comment.