diff --git a/nano/core_test/bootstrap_server.cpp b/nano/core_test/bootstrap_server.cpp index 5ca4a460cb..984cb58567 100644 --- a/nano/core_test/bootstrap_server.cpp +++ b/nano/core_test/bootstrap_server.cpp @@ -5,6 +5,7 @@ #include #include +#include using namespace std::chrono_literals; @@ -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 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 (response.payload)); + + ASSERT_EQ (response_payload.frontiers.size (), chains.size () + 1); // +1 for genesis + + // Ensure frontiers match what we expect + std::map 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::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); +} \ No newline at end of file