diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index c759b2b00b..db3c8d975c 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -1233,6 +1233,137 @@ TEST (frontier_req, time_cutoff) ASSERT_TRUE (request2->frontier.is_zero ()); } +TEST (frontier_req, confirmed_frontier) +{ + nano::system system (1); + auto node1 = system.nodes[0]; + nano::genesis genesis; + nano::raw_key priv_key; + // Public key before genesis in accounts table + while (nano::pub_key (priv_key).number () >= nano::dev_genesis_key.pub.number ()) + { + priv_key = nano::keypair ().prv; + } + nano::keypair key_before_genesis (priv_key.to_string ()); + // Public key after genesis in accounts table + while (nano::pub_key (priv_key).number () <= nano::dev_genesis_key.pub.number ()) + { + priv_key = nano::keypair ().prv; + } + nano::keypair key_after_genesis (priv_key.to_string ()); + + auto send1 (std::make_shared (nano::dev_genesis_key.pub, genesis.hash (), nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key_before_genesis.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); + node1->work_generate_blocking (*send1); + ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code); + auto send2 (std::make_shared (nano::dev_genesis_key.pub, send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, key_after_genesis.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); + node1->work_generate_blocking (*send2); + ASSERT_EQ (nano::process_result::progress, node1->process (*send2).code); + auto receive1 (std::make_shared (key_before_genesis.pub, 0, nano::dev_genesis_key.pub, nano::Gxrb_ratio, send1->hash (), key_before_genesis.prv, key_before_genesis.pub, 0)); + node1->work_generate_blocking (*receive1); + ASSERT_EQ (nano::process_result::progress, node1->process (*receive1).code); + auto receive2 (std::make_shared (key_after_genesis.pub, 0, nano::dev_genesis_key.pub, nano::Gxrb_ratio, send2->hash (), key_after_genesis.prv, key_after_genesis.pub, 0)); + node1->work_generate_blocking (*receive2); + ASSERT_EQ (nano::process_result::progress, node1->process (*receive2).code); + + // Request for all accounts (confirmed only) + auto connection (std::make_shared (nullptr, node1)); + auto req = std::make_unique (); + req->start.clear (); + req->age = std::numeric_limitsage)>::max (); + req->count = std::numeric_limitscount)>::max (); + ASSERT_FALSE (req->header.frontier_req_is_only_confirmed_present ()); + req->header.flag_set (nano::message_header::frontier_req_only_confirmed); + ASSERT_TRUE (req->header.frontier_req_is_only_confirmed_present ()); + connection->requests.push (std::unique_ptr{}); + auto request (std::make_shared (connection, std::move (req))); + ASSERT_EQ (nano::dev_genesis_key.pub, request->current); + ASSERT_EQ (genesis.hash (), request->frontier); + + // Request starting with account before genesis (confirmed only) + auto connection2 (std::make_shared (nullptr, node1)); + auto req2 = std::make_unique (); + req2->start = key_before_genesis.pub; + req2->age = std::numeric_limitsage)>::max (); + req2->count = std::numeric_limitscount)>::max (); + ASSERT_FALSE (req2->header.frontier_req_is_only_confirmed_present ()); + req2->header.flag_set (nano::message_header::frontier_req_only_confirmed); + ASSERT_TRUE (req2->header.frontier_req_is_only_confirmed_present ()); + connection2->requests.push (std::unique_ptr{}); + auto request2 (std::make_shared (connection2, std::move (req2))); + ASSERT_EQ (nano::dev_genesis_key.pub, request2->current); + ASSERT_EQ (genesis.hash (), request2->frontier); + + // Request starting with account after genesis (confirmed only) + auto connection3 (std::make_shared (nullptr, node1)); + auto req3 = std::make_unique (); + req3->start = key_after_genesis.pub; + req3->age = std::numeric_limitsage)>::max (); + req3->count = std::numeric_limitscount)>::max (); + ASSERT_FALSE (req3->header.frontier_req_is_only_confirmed_present ()); + req3->header.flag_set (nano::message_header::frontier_req_only_confirmed); + ASSERT_TRUE (req3->header.frontier_req_is_only_confirmed_present ()); + connection3->requests.push (std::unique_ptr{}); + auto request3 (std::make_shared (connection3, std::move (req3))); + ASSERT_TRUE (request3->current.is_zero ()); + ASSERT_TRUE (request3->frontier.is_zero ()); + + // Request for all accounts (unconfirmed blocks) + auto connection4 (std::make_shared (nullptr, node1)); + auto req4 = std::make_unique (); + req4->start.clear (); + req4->age = std::numeric_limitsage)>::max (); + req4->count = std::numeric_limitscount)>::max (); + ASSERT_FALSE (req4->header.frontier_req_is_only_confirmed_present ()); + connection4->requests.push (std::unique_ptr{}); + auto request4 (std::make_shared (connection4, std::move (req4))); + ASSERT_EQ (key_before_genesis.pub, request4->current); + ASSERT_EQ (receive1->hash (), request4->frontier); + + // Request starting with account after genesis (unconfirmed blocks) + auto connection5 (std::make_shared (nullptr, node1)); + auto req5 = std::make_unique (); + req5->start = key_after_genesis.pub; + req5->age = std::numeric_limitsage)>::max (); + req5->count = std::numeric_limitscount)>::max (); + ASSERT_FALSE (req5->header.frontier_req_is_only_confirmed_present ()); + connection5->requests.push (std::unique_ptr{}); + auto request5 (std::make_shared (connection5, std::move (req5))); + ASSERT_EQ (key_after_genesis.pub, request5->current); + ASSERT_EQ (receive2->hash (), request5->frontier); + + // Confirm account before genesis (confirmed only) + nano::blocks_confirm (*node1, { send1, receive1 }, true); + ASSERT_TIMELY (5s, node1->block_confirmed (send1->hash ()) && node1->block_confirmed (receive1->hash ())); + auto connection6 (std::make_shared (nullptr, node1)); + auto req6 = std::make_unique (); + req6->start = key_before_genesis.pub; + req6->age = std::numeric_limitsage)>::max (); + req6->count = std::numeric_limitscount)>::max (); + ASSERT_FALSE (req6->header.frontier_req_is_only_confirmed_present ()); + req6->header.flag_set (nano::message_header::frontier_req_only_confirmed); + ASSERT_TRUE (req6->header.frontier_req_is_only_confirmed_present ()); + connection6->requests.push (std::unique_ptr{}); + auto request6 (std::make_shared (connection6, std::move (req6))); + ASSERT_EQ (key_before_genesis.pub, request6->current); + ASSERT_EQ (receive1->hash (), request6->frontier); + + // Confirm account after genesis (confirmed only) + nano::blocks_confirm (*node1, { send2, receive2 }, true); + ASSERT_TIMELY (5s, node1->block_confirmed (send2->hash ()) && node1->block_confirmed (receive2->hash ())); + auto connection7 (std::make_shared (nullptr, node1)); + auto req7 = std::make_unique (); + req7->start = key_after_genesis.pub; + req7->age = std::numeric_limitsage)>::max (); + req7->count = std::numeric_limitscount)>::max (); + ASSERT_FALSE (req7->header.frontier_req_is_only_confirmed_present ()); + req7->header.flag_set (nano::message_header::frontier_req_only_confirmed); + ASSERT_TRUE (req7->header.frontier_req_is_only_confirmed_present ()); + connection7->requests.push (std::unique_ptr{}); + auto request7 (std::make_shared (connection7, std::move (req7))); + ASSERT_EQ (key_after_genesis.pub, request7->current); + ASSERT_EQ (receive2->hash (), request7->frontier); +} + TEST (bulk, genesis) { nano::system system; diff --git a/nano/node/bootstrap/bootstrap_frontier.cpp b/nano/node/bootstrap/bootstrap_frontier.cpp index a1dd4b1f44..d7097ec103 100644 --- a/nano/node/bootstrap/bootstrap_frontier.cpp +++ b/nano/node/bootstrap/bootstrap_frontier.cpp @@ -319,13 +319,29 @@ void nano::frontier_req_server::next () bool disable_age_filter (request->age == std::numeric_limitsage)>::max ()); size_t max_size (128); auto transaction (connection->node->store.tx_begin_read ()); - for (auto i (connection->node->store.accounts_begin (transaction, current.number () + 1)), n (connection->node->store.accounts_end ()); i != n && accounts.size () != max_size; ++i) + if (!send_confirmed ()) { - nano::account_info const & info (i->second); - if (disable_age_filter || (now - info.modified) <= request->age) + for (auto i (connection->node->store.accounts_begin (transaction, current.number () + 1)), n (connection->node->store.accounts_end ()); i != n && accounts.size () != max_size; ++i) + { + nano::account_info const & info (i->second); + if (disable_age_filter || (now - info.modified) <= request->age) + { + nano::account const & account (i->first); + accounts.emplace_back (account, info.head); + } + } + } + else + { + for (auto i (connection->node->store.confirmation_height_begin (transaction, current.number () + 1)), n (connection->node->store.confirmation_height_end ()); i != n && accounts.size () != max_size; ++i) { - nano::account const & account (i->first); - accounts.emplace_back (account, info.head); + nano::confirmation_height_info const & info (i->second); + nano::block_hash const & confirmed_frontier (info.frontier); + if (!confirmed_frontier.is_zero ()) + { + nano::account const & account (i->first); + accounts.emplace_back (account, confirmed_frontier); + } } } /* If loop breaks before max_size, then accounts_end () is reached @@ -341,3 +357,8 @@ void nano::frontier_req_server::next () frontier = account_pair.second; accounts.pop_front (); } + +bool nano::frontier_req_server::send_confirmed () +{ + return request->header.frontier_req_is_only_confirmed_present (); +} diff --git a/nano/node/bootstrap/bootstrap_frontier.hpp b/nano/node/bootstrap/bootstrap_frontier.hpp index 742151a541..053a704e10 100644 --- a/nano/node/bootstrap/bootstrap_frontier.hpp +++ b/nano/node/bootstrap/bootstrap_frontier.hpp @@ -44,6 +44,7 @@ class frontier_req_server final : public std::enable_shared_from_this connection; nano::account current; nano::block_hash frontier; diff --git a/nano/node/common.cpp b/nano/node/common.cpp index bcb3677b17..f4fc136bb3 100644 --- a/nano/node/common.cpp +++ b/nano/node/common.cpp @@ -176,6 +176,19 @@ bool nano::message_header::bulk_pull_is_count_present () const return result; } +bool nano::message_header::frontier_req_is_only_confirmed_present () const +{ + auto result (false); + if (type == nano::message_type::frontier_req) + { + if (extensions.test (frontier_req_only_confirmed)) + { + result = true; + } + } + return result; +} + bool nano::message_header::node_id_handshake_is_query () const { auto result (false); diff --git a/nano/node/common.hpp b/nano/node/common.hpp index 775127d901..030e95df2a 100644 --- a/nano/node/common.hpp +++ b/nano/node/common.hpp @@ -209,6 +209,8 @@ class message_header final void flag_set (uint8_t); static uint8_t constexpr bulk_pull_count_present_flag = 0; bool bulk_pull_is_count_present () const; + static uint8_t constexpr frontier_req_only_confirmed = 1; + bool frontier_req_is_only_confirmed_present () const; static uint8_t constexpr node_id_handshake_query_flag = 0; static uint8_t constexpr node_id_handshake_response_flag = 1; bool node_id_handshake_is_query () const;