Improve representative crawler (#1803)
* Improve rep crawling * Add count-check to test * Move tags and template alias into class, improve modification of items via non-unique index
This commit is contained in:
parent
c9ecb0cf59
commit
b8cde9f286
10 changed files with 464 additions and 271 deletions
|
|
@ -1213,7 +1213,7 @@ TEST (node, DISABLED_fork_stale)
|
|||
auto & node1 (*system1.nodes[0]);
|
||||
auto & node2 (*system2.nodes[0]);
|
||||
node2.bootstrap_initiator.bootstrap (node1.network.endpoint ());
|
||||
node2.peers.rep_response (node1.network.endpoint (), nano::test_genesis_key.pub, nano::genesis_amount);
|
||||
node2.rep_crawler.response (node1.network.endpoint (), nano::test_genesis_key.pub, nano::genesis_amount);
|
||||
nano::genesis genesis;
|
||||
nano::keypair key1;
|
||||
nano::keypair key2;
|
||||
|
|
@ -1501,17 +1501,17 @@ TEST (node, rep_list)
|
|||
nano::keypair key1;
|
||||
// Broadcast a confirm so others should know this is a rep node
|
||||
wallet0->send_action (nano::test_genesis_key.pub, key1.pub, nano::Mxrb_ratio);
|
||||
ASSERT_EQ (0, node1.peers.representatives (1).size ());
|
||||
ASSERT_EQ (0, node1.rep_crawler.representatives (1).size ());
|
||||
system.deadline_set (10s);
|
||||
auto done (false);
|
||||
while (!done)
|
||||
{
|
||||
auto reps (node1.peers.representatives (1));
|
||||
auto reps (node1.rep_crawler.representatives (1));
|
||||
if (!reps.empty ())
|
||||
{
|
||||
if (reps[0].endpoint == node0.network.endpoint ())
|
||||
{
|
||||
if (!reps[0].rep_weight.is_zero ())
|
||||
if (!reps[0].weight.is_zero ())
|
||||
{
|
||||
done = true;
|
||||
}
|
||||
|
|
@ -1521,6 +1521,34 @@ TEST (node, rep_list)
|
|||
}
|
||||
}
|
||||
|
||||
TEST (node, rep_weight)
|
||||
{
|
||||
nano::system system (24000, 1);
|
||||
auto & node (*system.nodes[0]);
|
||||
|
||||
node.peers.insert (nano::endpoint (boost::asio::ip::address_v6::loopback (), 24001), 0);
|
||||
ASSERT_TRUE (node.rep_crawler.representatives (1).empty ());
|
||||
nano::endpoint endpoint0 (boost::asio::ip::address_v6::loopback (), 24000);
|
||||
nano::endpoint endpoint1 (boost::asio::ip::address_v6::loopback (), 24002);
|
||||
nano::endpoint endpoint2 (boost::asio::ip::address_v6::loopback (), 24003);
|
||||
nano::amount amount100 (100);
|
||||
nano::amount amount50 (50);
|
||||
node.peers.insert (endpoint2, nano::protocol_version);
|
||||
node.peers.insert (endpoint0, nano::protocol_version);
|
||||
node.peers.insert (endpoint1, nano::protocol_version);
|
||||
nano::keypair keypair1;
|
||||
nano::keypair keypair2;
|
||||
node.rep_crawler.response (endpoint0, keypair1.pub, amount100);
|
||||
node.rep_crawler.response (endpoint1, keypair2.pub, amount50);
|
||||
ASSERT_EQ (2, node.rep_crawler.representative_count ());
|
||||
// Make sure we get the rep with the most weight first
|
||||
auto reps (node.rep_crawler.representatives (1));
|
||||
ASSERT_EQ (1, reps.size ());
|
||||
ASSERT_EQ (100, reps[0].weight.number ());
|
||||
ASSERT_EQ (keypair1.pub, reps[0].account);
|
||||
ASSERT_EQ (endpoint0, reps[0].endpoint);
|
||||
}
|
||||
|
||||
// Test that nodes can disable representative voting
|
||||
TEST (node, no_voting)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -121,27 +121,6 @@ TEST (peer_container, list_fanout)
|
|||
ASSERT_EQ (32, list2.size ());
|
||||
}
|
||||
|
||||
TEST (peer_container, rep_weight)
|
||||
{
|
||||
nano::peer_container peers (nano::endpoint{});
|
||||
peers.insert (nano::endpoint (boost::asio::ip::address_v6::loopback (), 24001), 0);
|
||||
ASSERT_TRUE (peers.representatives (1).empty ());
|
||||
nano::endpoint endpoint0 (boost::asio::ip::address_v6::loopback (), 24000);
|
||||
nano::endpoint endpoint1 (boost::asio::ip::address_v6::loopback (), 24002);
|
||||
nano::endpoint endpoint2 (boost::asio::ip::address_v6::loopback (), 24003);
|
||||
nano::amount amount (100);
|
||||
peers.insert (endpoint2, nano::protocol_version);
|
||||
peers.insert (endpoint0, nano::protocol_version);
|
||||
peers.insert (endpoint1, nano::protocol_version);
|
||||
nano::keypair keypair;
|
||||
peers.rep_response (endpoint0, keypair.pub, amount);
|
||||
auto reps (peers.representatives (1));
|
||||
ASSERT_EQ (1, reps.size ());
|
||||
ASSERT_EQ (100, reps[0].rep_weight.number ());
|
||||
ASSERT_EQ (keypair.pub, reps[0].probable_rep_account);
|
||||
ASSERT_EQ (endpoint0, reps[0].endpoint);
|
||||
}
|
||||
|
||||
// Test to make sure we don't repeatedly send keepalive messages to nodes that aren't responding
|
||||
TEST (peer_container, reachout)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ add_library (node
|
|||
peers.hpp
|
||||
portmapping.hpp
|
||||
portmapping.cpp
|
||||
repcrawler.hpp
|
||||
repcrawler.cpp
|
||||
rpc.hpp
|
||||
rpc.cpp
|
||||
rpcconfig.hpp
|
||||
|
|
|
|||
|
|
@ -420,11 +420,16 @@ void nano::network::republish_vote (std::shared_ptr<nano::vote> vote_a)
|
|||
|
||||
void nano::network::broadcast_confirm_req (std::shared_ptr<nano::block> block_a)
|
||||
{
|
||||
auto list (std::make_shared<std::vector<nano::peer_information>> (node.peers.representatives (std::numeric_limits<size_t>::max ())));
|
||||
if (list->empty () || node.peers.total_weight () < node.config.online_weight_minimum.number ())
|
||||
auto list (std::make_shared<std::vector<nano::endpoint>> (node.rep_crawler.representative_endpoints (std::numeric_limits<size_t>::max ())));
|
||||
if (list->empty () || node.rep_crawler.total_weight () < node.config.online_weight_minimum.number ())
|
||||
{
|
||||
// broadcast request to all peers (with max limit 2 * sqrt (peers count))
|
||||
list = std::make_shared<std::vector<nano::peer_information>> (node.peers.list_vector (std::min (static_cast<size_t> (100), 2 * node.peers.size_sqrt ())));
|
||||
auto peers (node.peers.list_vector (std::min (static_cast<size_t> (100), 2 * node.peers.size_sqrt ())));
|
||||
list->clear ();
|
||||
for (auto & peer : peers)
|
||||
{
|
||||
list->push_back (peer.endpoint);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -444,7 +449,7 @@ void nano::network::broadcast_confirm_req (std::shared_ptr<nano::block> block_a)
|
|||
broadcast_confirm_req_base (block_a, list, 0);
|
||||
}
|
||||
|
||||
void nano::network::broadcast_confirm_req_base (std::shared_ptr<nano::block> block_a, std::shared_ptr<std::vector<nano::peer_information>> endpoints_a, unsigned delay_a, bool resumption)
|
||||
void nano::network::broadcast_confirm_req_base (std::shared_ptr<nano::block> block_a, std::shared_ptr<std::vector<nano::endpoint>> endpoints_a, unsigned delay_a, bool resumption)
|
||||
{
|
||||
const size_t max_reps = 10;
|
||||
if (!resumption && node.config.logging.network_logging ())
|
||||
|
|
@ -454,7 +459,7 @@ void nano::network::broadcast_confirm_req_base (std::shared_ptr<nano::block> blo
|
|||
auto count (0);
|
||||
while (!endpoints_a->empty () && count < max_reps)
|
||||
{
|
||||
send_confirm_req (endpoints_a->back ().endpoint, block_a);
|
||||
send_confirm_req (endpoints_a->back (), block_a);
|
||||
endpoints_a->pop_back ();
|
||||
count++;
|
||||
}
|
||||
|
|
@ -509,7 +514,7 @@ void nano::network::broadcast_confirm_req_batch (std::unordered_map<nano::endpoi
|
|||
}
|
||||
}
|
||||
|
||||
void nano::network::broadcast_confirm_req_batch (std::deque<std::pair<std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::peer_information>>>> deque_a, unsigned delay_a)
|
||||
void nano::network::broadcast_confirm_req_batch (std::deque<std::pair<std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::endpoint>>>> deque_a, unsigned delay_a)
|
||||
{
|
||||
auto pair (deque_a.front ());
|
||||
deque_a.pop_front ();
|
||||
|
|
@ -576,34 +581,6 @@ void nano::network::send_confirm_req_hashes (nano::endpoint const & endpoint_a,
|
|||
});
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void rep_query (nano::node & node_a, T const & peers_a)
|
||||
{
|
||||
auto transaction (node_a.store.tx_begin_read ());
|
||||
std::shared_ptr<nano::block> block (node_a.store.block_random (transaction));
|
||||
auto hash (block->hash ());
|
||||
node_a.rep_crawler.add (hash);
|
||||
for (auto i (peers_a.begin ()), n (peers_a.end ()); i != n; ++i)
|
||||
{
|
||||
node_a.peers.rep_request (*i);
|
||||
node_a.network.send_confirm_req (*i, block);
|
||||
}
|
||||
std::weak_ptr<nano::node> node_w (node_a.shared ());
|
||||
node_a.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [node_w, hash]() {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->rep_crawler.remove (hash);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void rep_query (nano::node & node_a, nano::endpoint const & peers_a)
|
||||
{
|
||||
std::array<nano::endpoint, 1> peers;
|
||||
peers[0] = peers_a;
|
||||
rep_query (node_a, peers);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
class network_message_visitor : public nano::message_visitor
|
||||
|
|
@ -1320,33 +1297,12 @@ std::unique_ptr<seq_con_info_component> collect_seq_con_info (vote_processor & v
|
|||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "representatives_3", representatives_3_count, sizeof (decltype (vote_processor.representatives_3)::value_type) }));
|
||||
return composite;
|
||||
}
|
||||
}
|
||||
|
||||
void nano::rep_crawler::add (nano::block_hash const & hash_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
active.insert (hash_a);
|
||||
}
|
||||
|
||||
void nano::rep_crawler::remove (nano::block_hash const & hash_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
active.erase (hash_a);
|
||||
}
|
||||
|
||||
bool nano::rep_crawler::exists (nano::block_hash const & hash_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
return active.count (hash_a) != 0;
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
std::unique_ptr<seq_con_info_component> collect_seq_con_info (rep_crawler & rep_crawler, const std::string & name)
|
||||
{
|
||||
size_t count = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard (rep_crawler.mutex);
|
||||
std::lock_guard<std::mutex> guard (rep_crawler.active_mutex);
|
||||
count = rep_crawler.active.size ();
|
||||
}
|
||||
|
||||
|
|
@ -1355,10 +1311,7 @@ std::unique_ptr<seq_con_info_component> collect_seq_con_info (rep_crawler & rep_
|
|||
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "active", count, sizeof_element }));
|
||||
return composite;
|
||||
}
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
std::unique_ptr<seq_con_info_component> collect_seq_con_info (block_processor & block_processor, const std::string & name)
|
||||
{
|
||||
size_t state_blocks_count = 0;
|
||||
|
|
@ -1415,6 +1368,7 @@ wallets (init_a.wallet_init, *this),
|
|||
port_mapping (*this),
|
||||
checker (config.signature_checker_threads),
|
||||
vote_processor (*this),
|
||||
rep_crawler (*this),
|
||||
warmed_up (0),
|
||||
block_processor (*this),
|
||||
block_processor_thread ([this]() {
|
||||
|
|
@ -1498,7 +1452,6 @@ startup_time (std::chrono::steady_clock::now ())
|
|||
}
|
||||
observers.endpoint.add ([this](nano::endpoint const & endpoint_a) {
|
||||
this->network.send_keepalive (endpoint_a);
|
||||
rep_query (*this, endpoint_a);
|
||||
});
|
||||
observers.vote.add ([this](nano::transaction const & transaction, std::shared_ptr<nano::vote> vote_a, nano::endpoint const & endpoint_a) {
|
||||
assert (endpoint_a.address ().is_v6 ());
|
||||
|
|
@ -1524,7 +1477,7 @@ startup_time (std::chrono::steady_clock::now ())
|
|||
if (rep_crawler_exists)
|
||||
{
|
||||
// We see a valid non-replay vote for a block we requested, this node is probably a representative
|
||||
if (this->peers.rep_response (endpoint_a, vote_a->account, rep_weight))
|
||||
if (this->rep_crawler.response (endpoint_a, vote_a->account, rep_weight))
|
||||
{
|
||||
logger.try_log (boost::str (boost::format ("Found a representative at %1%") % endpoint_a));
|
||||
// Rebroadcasting all active votes to new representative
|
||||
|
|
@ -1572,7 +1525,6 @@ startup_time (std::chrono::steady_clock::now ())
|
|||
node_id = nano::keypair (store.get_node_id (transaction));
|
||||
logger.always_log ("Node ID: ", node_id.pub.to_account ());
|
||||
}
|
||||
peers.online_weight_minimum = config.online_weight_minimum.number ();
|
||||
if (nano::is_live_network || nano::is_beta_network)
|
||||
{
|
||||
nano::bufferstream weight_stream ((const uint8_t *)nano_bootstrap_weights, nano_bootstrap_weights_size);
|
||||
|
|
@ -1915,7 +1867,7 @@ void nano::node::start ()
|
|||
ongoing_unchecked_cleanup ();
|
||||
}
|
||||
ongoing_store_flush ();
|
||||
ongoing_rep_crawl ();
|
||||
rep_crawler.start ();
|
||||
ongoing_rep_calculation ();
|
||||
ongoing_peer_store ();
|
||||
ongoing_online_weight_calculation_queue ();
|
||||
|
|
@ -2039,23 +1991,6 @@ void nano::node::ongoing_syn_cookie_cleanup ()
|
|||
});
|
||||
}
|
||||
|
||||
void nano::node::ongoing_rep_crawl ()
|
||||
{
|
||||
auto now (std::chrono::steady_clock::now ());
|
||||
auto peers_l (peers.rep_crawl ());
|
||||
rep_query (*this, peers_l);
|
||||
if (network.on)
|
||||
{
|
||||
std::weak_ptr<nano::node> node_w (shared_from_this ());
|
||||
alarm.add (now + std::chrono::seconds (4), [node_w]() {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->ongoing_rep_crawl ();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void nano::node::ongoing_rep_calculation ()
|
||||
{
|
||||
auto now (std::chrono::steady_clock::now ());
|
||||
|
|
@ -2556,6 +2491,7 @@ void nano::node::add_initial_peers ()
|
|||
if (!peers.reachout (endpoint, config.allow_local_peers))
|
||||
{
|
||||
send_keepalive (endpoint);
|
||||
rep_crawler.query (endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3300,7 +3236,7 @@ void nano::active_transactions::request_confirm (std::unique_lock<std::mutex> &
|
|||
unsigned unconfirmed_announcements (0);
|
||||
std::unordered_map<nano::endpoint, std::vector<std::pair<nano::block_hash, nano::block_hash>>> requests_bundle;
|
||||
std::deque<std::shared_ptr<nano::block>> rebroadcast_bundle;
|
||||
std::deque<std::pair<std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::peer_information>>>> confirm_req_bundle;
|
||||
std::deque<std::pair<std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::endpoint>>>> confirm_req_bundle;
|
||||
|
||||
auto roots_size (roots.size ());
|
||||
for (auto i (roots.get<1> ().begin ()), n (roots.get<1> ().end ()); i != n; ++i)
|
||||
|
|
@ -3382,55 +3318,42 @@ void nano::active_transactions::request_confirm (std::unique_lock<std::mutex> &
|
|||
}
|
||||
if (election_l->announcements % 4 == 1)
|
||||
{
|
||||
auto reps (std::make_shared<std::vector<nano::peer_information>> (node.peers.representatives (std::numeric_limits<size_t>::max ())));
|
||||
std::unordered_set<nano::account> probable_reps;
|
||||
nano::uint128_t total_weight (0);
|
||||
for (auto j (reps->begin ()), m (reps->end ()); j != m;)
|
||||
{
|
||||
auto & rep_votes (election_l->last_votes);
|
||||
auto rep_acct (j->probable_rep_account);
|
||||
// Calculate if representative isn't recorded for several IP addresses
|
||||
if (probable_reps.find (rep_acct) == probable_reps.end ())
|
||||
{
|
||||
total_weight = total_weight + j->rep_weight.number ();
|
||||
probable_reps.insert (rep_acct);
|
||||
}
|
||||
if (rep_votes.find (rep_acct) != rep_votes.end ())
|
||||
{
|
||||
if (j + 1 == reps->end ())
|
||||
{
|
||||
reps->pop_back ();
|
||||
break;
|
||||
}
|
||||
auto rep_endpoints (std::make_shared<std::vector<nano::endpoint>> ());
|
||||
auto reps (node.rep_crawler.representatives (std::numeric_limits<size_t>::max ()));
|
||||
|
||||
std::swap (*j, reps->back ());
|
||||
reps->pop_back ();
|
||||
m = reps->end ();
|
||||
}
|
||||
else
|
||||
// Add all rep endpoints that haven't already voted. We use a set since multiple
|
||||
// reps may exist on an endpoint.
|
||||
std::unordered_set<nano::endpoint> endpoints;
|
||||
for (auto & rep : reps)
|
||||
{
|
||||
if (election_l->last_votes.find (rep.account) == election_l->last_votes.end ())
|
||||
{
|
||||
++j;
|
||||
endpoints.insert (rep.endpoint);
|
||||
|
||||
if (node.config.logging.vote_logging ())
|
||||
{
|
||||
node.logger.try_log ("Representative did not respond to confirm_req, retrying: ", rep_acct.to_account ());
|
||||
node.logger.try_log ("Representative did not respond to confirm_req, retrying: ", rep.account.to_account ());
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((!reps->empty () && total_weight > node.config.online_weight_minimum.number ()) || roots_size > 5)
|
||||
|
||||
rep_endpoints->insert (rep_endpoints->end (), endpoints.begin (), endpoints.end ());
|
||||
|
||||
if ((!rep_endpoints->empty () && node.rep_crawler.total_weight () > node.config.online_weight_minimum.number ()) || roots_size > 5)
|
||||
{
|
||||
// broadcast_confirm_req_base modifies reps, so we clone it once to avoid aliasing
|
||||
if (!nano::is_test_network)
|
||||
{
|
||||
if (confirm_req_bundle.size () < max_broadcast_queue)
|
||||
{
|
||||
confirm_req_bundle.push_back (std::make_pair (election_l->status.winner, reps));
|
||||
confirm_req_bundle.push_back (std::make_pair (election_l->status.winner, rep_endpoints));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto & rep : *reps)
|
||||
for (auto & rep : *rep_endpoints)
|
||||
{
|
||||
auto rep_request (requests_bundle.find (rep.endpoint));
|
||||
auto rep_request (requests_bundle.find (rep));
|
||||
auto block (election_l->status.winner);
|
||||
auto root_hash (std::make_pair (block->hash (), block->root ()));
|
||||
if (rep_request == requests_bundle.end ())
|
||||
|
|
@ -3438,7 +3361,7 @@ void nano::active_transactions::request_confirm (std::unique_lock<std::mutex> &
|
|||
if (requests_bundle.size () < max_broadcast_queue)
|
||||
{
|
||||
std::vector<std::pair<nano::block_hash, nano::block_hash>> insert_vector = { root_hash };
|
||||
requests_bundle.insert (std::make_pair (rep.endpoint, insert_vector));
|
||||
requests_bundle.insert (std::make_pair (rep, insert_vector));
|
||||
}
|
||||
}
|
||||
else if (rep_request->second.size () < max_broadcast_queue * nano::network::confirm_req_hashes_max)
|
||||
|
|
@ -3452,19 +3375,21 @@ void nano::active_transactions::request_confirm (std::unique_lock<std::mutex> &
|
|||
{
|
||||
if (!nano::is_test_network)
|
||||
{
|
||||
confirm_req_bundle.push_back (std::make_pair (election_l->status.winner, std::make_shared<std::vector<nano::peer_information>> (node.peers.list_vector (100))));
|
||||
auto deque_l (node.peers.list (100));
|
||||
std::vector<nano::endpoint> vec ({ deque_l.begin (), deque_l.end () });
|
||||
confirm_req_bundle.push_back (std::make_pair (election_l->status.winner, std::make_shared<std::vector<nano::endpoint>> (vec)));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto & rep : *reps)
|
||||
for (auto & rep : *rep_endpoints)
|
||||
{
|
||||
auto rep_request (requests_bundle.find (rep.endpoint));
|
||||
auto rep_request (requests_bundle.find (rep));
|
||||
auto block (election_l->status.winner);
|
||||
auto root_hash (std::make_pair (block->hash (), block->root ()));
|
||||
if (rep_request == requests_bundle.end ())
|
||||
{
|
||||
std::vector<std::pair<nano::block_hash, nano::block_hash>> insert_vector = { root_hash };
|
||||
requests_bundle.insert (std::make_pair (rep.endpoint, insert_vector));
|
||||
requests_bundle.insert (std::make_pair (rep, insert_vector));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <nano/node/nodeconfig.hpp>
|
||||
#include <nano/node/peers.hpp>
|
||||
#include <nano/node/portmapping.hpp>
|
||||
#include <nano/node/repcrawler.hpp>
|
||||
#include <nano/node/signatures.hpp>
|
||||
#include <nano/node/stats.hpp>
|
||||
#include <nano/node/wallet.hpp>
|
||||
|
|
@ -342,9 +343,9 @@ public:
|
|||
void send_keepalive (nano::endpoint const &);
|
||||
void send_node_id_handshake (nano::endpoint const &, boost::optional<nano::uint256_union> const & query, boost::optional<nano::uint256_union> const & respond_to);
|
||||
void broadcast_confirm_req (std::shared_ptr<nano::block>);
|
||||
void broadcast_confirm_req_base (std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::peer_information>>, unsigned, bool = false);
|
||||
void broadcast_confirm_req_base (std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::endpoint>>, unsigned, bool = false);
|
||||
void broadcast_confirm_req_batch (std::unordered_map<nano::endpoint, std::vector<std::pair<nano::block_hash, nano::block_hash>>>, unsigned = broadcast_interval_ms, bool = false);
|
||||
void broadcast_confirm_req_batch (std::deque<std::pair<std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::peer_information>>>>, unsigned = broadcast_interval_ms);
|
||||
void broadcast_confirm_req_batch (std::deque<std::pair<std::shared_ptr<nano::block>, std::shared_ptr<std::vector<nano::endpoint>>>>, unsigned = broadcast_interval_ms);
|
||||
void send_confirm_req (nano::endpoint const &, std::shared_ptr<nano::block>);
|
||||
void send_confirm_req_hashes (nano::endpoint const &, std::vector<std::pair<nano::block_hash, nano::block_hash>> const &);
|
||||
void confirm_hashes (nano::transaction const &, nano::endpoint const &, std::vector<nano::block_hash>);
|
||||
|
|
@ -416,18 +417,6 @@ private:
|
|||
};
|
||||
|
||||
std::unique_ptr<seq_con_info_component> collect_seq_con_info (vote_processor & vote_processor, const std::string & name);
|
||||
|
||||
// The network is crawled for representatives by occasionally sending a unicast confirm_req for a specific block and watching to see if it's acknowledged with a vote.
|
||||
class rep_crawler
|
||||
{
|
||||
public:
|
||||
void add (nano::block_hash const &);
|
||||
void remove (nano::block_hash const &);
|
||||
bool exists (nano::block_hash const &);
|
||||
std::mutex mutex;
|
||||
std::unordered_set<nano::block_hash> active;
|
||||
};
|
||||
|
||||
std::unique_ptr<seq_con_info_component> collect_seq_con_info (rep_crawler & rep_crawler, const std::string & name);
|
||||
std::unique_ptr<seq_con_info_component> collect_seq_con_info (block_processor & block_processor, const std::string & name);
|
||||
|
||||
|
|
@ -462,7 +451,6 @@ public:
|
|||
nano::account representative (nano::account const &);
|
||||
void ongoing_keepalive ();
|
||||
void ongoing_syn_cookie_cleanup ();
|
||||
void ongoing_rep_crawl ();
|
||||
void ongoing_rep_calculation ();
|
||||
void ongoing_bootstrap ();
|
||||
void ongoing_store_flush ();
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ std::deque<nano::endpoint> nano::peer_container::list_fanout ()
|
|||
return result;
|
||||
}
|
||||
|
||||
std::deque<nano::endpoint> nano::peer_container::list ()
|
||||
std::deque<nano::endpoint> nano::peer_container::list (size_t count_a)
|
||||
{
|
||||
std::deque<nano::endpoint> result;
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
|
|
@ -99,6 +99,10 @@ std::deque<nano::endpoint> nano::peer_container::list ()
|
|||
result.push_back (i->endpoint);
|
||||
}
|
||||
nano::random_pool::shuffle (result.begin (), result.end ());
|
||||
if (result.size () > count_a)
|
||||
{
|
||||
result.resize (count_a);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -229,22 +233,6 @@ void nano::peer_container::random_fill (std::array<nano::endpoint, 8> & target_a
|
|||
}
|
||||
}
|
||||
|
||||
// Request a list of the top known representatives
|
||||
std::vector<nano::peer_information> nano::peer_container::representatives (size_t count_a)
|
||||
{
|
||||
std::vector<peer_information> result;
|
||||
result.reserve (std::min (count_a, size_t (16)));
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
for (auto i (peers.get<6> ().begin ()), n (peers.get<6> ().end ()); i != n && result.size () < count_a; ++i)
|
||||
{
|
||||
if (!i->rep_weight.is_zero ())
|
||||
{
|
||||
result.push_back (*i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::peer_container::purge_syn_cookies (std::chrono::steady_clock::time_point const & cutoff)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (syn_cookie_mutex);
|
||||
|
|
@ -297,21 +285,6 @@ std::vector<nano::peer_information> nano::peer_container::purge_list (std::chron
|
|||
return result;
|
||||
}
|
||||
|
||||
std::vector<nano::endpoint> nano::peer_container::rep_crawl ()
|
||||
{
|
||||
std::vector<nano::endpoint> result;
|
||||
// If there is enough observed peers weight, crawl 10 peers. Otherwise - 40
|
||||
uint16_t max_count = (total_weight () > online_weight_minimum) ? 10 : 40;
|
||||
result.reserve (max_count);
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
uint16_t count (0);
|
||||
for (auto i (peers.get<5> ().begin ()), n (peers.get<5> ().end ()); i != n && count < max_count; ++i, ++count)
|
||||
{
|
||||
result.push_back (i->endpoint);
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t nano::peer_container::size ()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
|
|
@ -323,36 +296,6 @@ size_t nano::peer_container::size_sqrt ()
|
|||
return (static_cast<size_t> (std::ceil (std::sqrt (size ()))));
|
||||
}
|
||||
|
||||
std::vector<nano::peer_information> nano::peer_container::list_probable_rep_weights ()
|
||||
{
|
||||
std::vector<nano::peer_information> result;
|
||||
std::unordered_set<nano::account> probable_reps;
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
for (auto i (peers.get<6> ().begin ()), n (peers.get<6> ().end ()); i != n; ++i)
|
||||
{
|
||||
// Calculate if representative isn't recorded for several IP addresses
|
||||
if (probable_reps.find (i->probable_rep_account) == probable_reps.end ())
|
||||
{
|
||||
if (!i->rep_weight.number ().is_zero ())
|
||||
{
|
||||
result.push_back (*i);
|
||||
}
|
||||
probable_reps.insert (i->probable_rep_account);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nano::uint128_t nano::peer_container::total_weight ()
|
||||
{
|
||||
nano::uint128_t result (0);
|
||||
for (auto & entry : list_probable_rep_weights ())
|
||||
{
|
||||
result = result + entry.rep_weight.number ();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool nano::peer_container::empty ()
|
||||
{
|
||||
return size () == 0;
|
||||
|
|
@ -376,39 +319,6 @@ bool nano::peer_container::not_a_peer (nano::endpoint const & endpoint_a, bool a
|
|||
return result;
|
||||
}
|
||||
|
||||
bool nano::peer_container::rep_response (nano::endpoint const & endpoint_a, nano::account const & rep_account_a, nano::amount const & weight_a)
|
||||
{
|
||||
assert (endpoint_a.address ().is_v6 ());
|
||||
auto updated (false);
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
auto existing (peers.find (endpoint_a));
|
||||
if (existing != peers.end ())
|
||||
{
|
||||
peers.modify (existing, [weight_a, &updated, rep_account_a](nano::peer_information & info) {
|
||||
info.last_rep_response = std::chrono::steady_clock::now ();
|
||||
if (info.rep_weight < weight_a)
|
||||
{
|
||||
updated = true;
|
||||
info.rep_weight = weight_a;
|
||||
info.probable_rep_account = rep_account_a;
|
||||
}
|
||||
});
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
void nano::peer_container::rep_request (nano::endpoint const & endpoint_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (mutex);
|
||||
auto existing (peers.find (endpoint_a));
|
||||
if (existing != peers.end ())
|
||||
{
|
||||
peers.modify (existing, [](nano::peer_information & info) {
|
||||
info.last_rep_request = std::chrono::steady_clock::now ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::peer_container::reachout (nano::endpoint const & endpoint_a, bool allow_local_peers)
|
||||
{
|
||||
// Don't contact invalid IPs
|
||||
|
|
|
|||
|
|
@ -51,10 +51,6 @@ public:
|
|||
std::chrono::steady_clock::time_point last_contact;
|
||||
std::chrono::steady_clock::time_point last_attempt;
|
||||
std::chrono::steady_clock::time_point last_bootstrap_attempt{ std::chrono::steady_clock::time_point () };
|
||||
std::chrono::steady_clock::time_point last_rep_request{ std::chrono::steady_clock::time_point () };
|
||||
std::chrono::steady_clock::time_point last_rep_response{ std::chrono::steady_clock::time_point () };
|
||||
nano::amount rep_weight{ 0 };
|
||||
nano::account probable_rep_account{ 0 };
|
||||
unsigned network_version{ nano::protocol_version };
|
||||
boost::optional<nano::account> node_id;
|
||||
bool operator< (nano::peer_information const &) const;
|
||||
|
|
@ -76,10 +72,8 @@ public:
|
|||
bool insert (nano::endpoint const &, unsigned, bool = false, boost::optional<nano::account> = boost::none);
|
||||
std::unordered_set<nano::endpoint> random_set (size_t);
|
||||
void random_fill (std::array<nano::endpoint, 8> &);
|
||||
// Request a list of the top known representatives
|
||||
std::vector<peer_information> representatives (size_t);
|
||||
// List of all peers
|
||||
std::deque<nano::endpoint> list ();
|
||||
std::deque<nano::endpoint> list (size_t count_a = std::numeric_limits<size_t>::max ());
|
||||
std::vector<peer_information> list_vector (size_t);
|
||||
// A list of random peers sized for the configured rebroadcast fanout
|
||||
std::deque<nano::endpoint> list_fanout ();
|
||||
|
|
@ -90,9 +84,6 @@ public:
|
|||
// Purge any peer where last_contact < time_point and return what was left
|
||||
std::vector<nano::peer_information> purge_list (std::chrono::steady_clock::time_point const &);
|
||||
void purge_syn_cookies (std::chrono::steady_clock::time_point const &);
|
||||
std::vector<nano::endpoint> rep_crawl ();
|
||||
bool rep_response (nano::endpoint const &, nano::account const &, nano::amount const &);
|
||||
void rep_request (nano::endpoint const &);
|
||||
// Should we reach out to this endpoint with a keepalive message
|
||||
bool reachout (nano::endpoint const &, bool = false);
|
||||
// Returns boost::none if the IP is rate capped on syn cookie requests,
|
||||
|
|
@ -103,8 +94,6 @@ public:
|
|||
bool validate_syn_cookie (nano::endpoint const &, nano::account, nano::signature);
|
||||
size_t size ();
|
||||
size_t size_sqrt ();
|
||||
nano::uint128_t total_weight ();
|
||||
nano::uint128_t online_weight_minimum;
|
||||
bool empty ();
|
||||
std::mutex mutex;
|
||||
nano::endpoint self;
|
||||
|
|
@ -116,8 +105,6 @@ public:
|
|||
boost::multi_index::ordered_non_unique<boost::multi_index::member<peer_information, std::chrono::steady_clock::time_point, &peer_information::last_attempt>, std::greater<std::chrono::steady_clock::time_point>>,
|
||||
boost::multi_index::random_access<>,
|
||||
boost::multi_index::ordered_non_unique<boost::multi_index::member<peer_information, std::chrono::steady_clock::time_point, &peer_information::last_bootstrap_attempt>>,
|
||||
boost::multi_index::ordered_non_unique<boost::multi_index::member<peer_information, std::chrono::steady_clock::time_point, &peer_information::last_rep_request>>,
|
||||
boost::multi_index::ordered_non_unique<boost::multi_index::member<peer_information, nano::amount, &peer_information::rep_weight>, std::greater<nano::amount>>,
|
||||
boost::multi_index::ordered_non_unique<boost::multi_index::tag<peer_by_ip_addr>, boost::multi_index::member<peer_information, boost::asio::ip::address, &peer_information::ip_address>>>>
|
||||
peers;
|
||||
boost::multi_index_container<
|
||||
|
|
|
|||
240
nano/node/repcrawler.cpp
Normal file
240
nano/node/repcrawler.cpp
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
#include <nano/node/node.hpp>
|
||||
#include <nano/node/repcrawler.hpp>
|
||||
|
||||
nano::rep_crawler::rep_crawler (nano::node & node_a) :
|
||||
node (node_a)
|
||||
{
|
||||
node.observers.endpoint.add ([this](nano::endpoint const & endpoint_a) {
|
||||
this->query (endpoint_a);
|
||||
});
|
||||
}
|
||||
|
||||
void nano::rep_crawler::add (nano::block_hash const & hash_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (active_mutex);
|
||||
active.insert (hash_a);
|
||||
}
|
||||
|
||||
void nano::rep_crawler::remove (nano::block_hash const & hash_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (active_mutex);
|
||||
active.erase (hash_a);
|
||||
}
|
||||
|
||||
bool nano::rep_crawler::exists (nano::block_hash const & hash_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (active_mutex);
|
||||
return active.count (hash_a) != 0;
|
||||
}
|
||||
|
||||
void nano::rep_crawler::start ()
|
||||
{
|
||||
ongoing_crawl ();
|
||||
}
|
||||
|
||||
void nano::rep_crawler::ongoing_crawl ()
|
||||
{
|
||||
auto now (std::chrono::steady_clock::now ());
|
||||
query (get_crawl_targets ());
|
||||
if (node.network.on)
|
||||
{
|
||||
// Reduce crawl frequency when there's enough total peer weight
|
||||
unsigned next_run_seconds = (total_weight_internal () > node.config.online_weight_minimum.number ()) ? 7 : 3;
|
||||
std::weak_ptr<nano::node> node_w (node.shared ());
|
||||
node.alarm.add (now + std::chrono::seconds (next_run_seconds), [node_w, this]() {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
this->ongoing_crawl ();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<nano::endpoint> nano::rep_crawler::get_crawl_targets ()
|
||||
{
|
||||
std::unordered_set<nano::endpoint> endpoints;
|
||||
constexpr size_t conservative_count = 10;
|
||||
constexpr size_t aggressive_count = 40;
|
||||
|
||||
// Crawl more aggressively if we lack sufficient total peer weight.
|
||||
bool sufficient_weight (total_weight_internal () > node.config.online_weight_minimum.number ());
|
||||
uint16_t required_peer_count = sufficient_weight ? conservative_count : aggressive_count;
|
||||
std::lock_guard<std::mutex> lock (probable_reps_mutex);
|
||||
|
||||
// First, add known rep endpoints, ordered by ascending last-requested time.
|
||||
for (auto i (probable_reps.get<tag_last_request> ().begin ()), n (probable_reps.get<tag_last_request> ().end ()); i != n && endpoints.size () < required_peer_count; ++i)
|
||||
{
|
||||
endpoints.insert (i->endpoint);
|
||||
};
|
||||
|
||||
// Add additional random peers. We do this even if we have enough weight, in order to pick up reps
|
||||
// that didn't respond when first observed. If the current total weight isn't sufficient, this
|
||||
// will be more aggressive. When the node first starts, the rep container is empty and all
|
||||
// endpoints will originate from random peers.
|
||||
required_peer_count += required_peer_count / 2;
|
||||
|
||||
// The rest of the endpoints are picked randomly
|
||||
auto random_peers (node.peers.list ());
|
||||
for (auto & peer : random_peers)
|
||||
{
|
||||
endpoints.insert (peer);
|
||||
if (endpoints.size () >= required_peer_count)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<nano::endpoint> result;
|
||||
result.insert (result.end (), endpoints.begin (), endpoints.end ());
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::rep_crawler::query (std::vector<nano::endpoint> const & endpoints_a)
|
||||
{
|
||||
auto transaction (node.store.tx_begin_read ());
|
||||
std::shared_ptr<nano::block> block (node.store.block_random (transaction));
|
||||
auto hash (block->hash ());
|
||||
add (hash);
|
||||
for (auto i (endpoints_a.begin ()), n (endpoints_a.end ()); i != n; ++i)
|
||||
{
|
||||
on_rep_request (*i);
|
||||
node.network.send_confirm_req (*i, block);
|
||||
}
|
||||
|
||||
// A representative must respond with a vote within the deadline
|
||||
std::weak_ptr<nano::node> node_w (node.shared ());
|
||||
node.alarm.add (std::chrono::steady_clock::now () + std::chrono::seconds (5), [node_w, hash]() {
|
||||
if (auto node_l = node_w.lock ())
|
||||
{
|
||||
node_l->rep_crawler.remove (hash);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void nano::rep_crawler::query (nano::endpoint const & endpoint_a)
|
||||
{
|
||||
std::vector<nano::endpoint> peers;
|
||||
peers.push_back (endpoint_a);
|
||||
query (peers);
|
||||
}
|
||||
|
||||
bool nano::rep_crawler::response (nano::endpoint const & endpoint_a, nano::account const & rep_account_a, nano::amount const & weight_a)
|
||||
{
|
||||
assert (endpoint_a.address ().is_v6 ());
|
||||
auto updated (false);
|
||||
std::lock_guard<std::mutex> lock (probable_reps_mutex);
|
||||
auto existing (probable_reps.find (rep_account_a));
|
||||
if (existing != probable_reps.end ())
|
||||
{
|
||||
probable_reps.modify (existing, [weight_a, &updated, rep_account_a, endpoint_a](nano::representative & info) {
|
||||
info.last_response = std::chrono::steady_clock::now ();
|
||||
|
||||
if (info.weight < weight_a)
|
||||
{
|
||||
updated = true;
|
||||
info.weight = weight_a;
|
||||
info.endpoint = endpoint_a;
|
||||
info.account = rep_account_a;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
probable_reps.insert (nano::representative (rep_account_a, weight_a, endpoint_a));
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
nano::uint128_t nano::rep_crawler::total_weight_internal ()
|
||||
{
|
||||
nano::uint128_t result (0);
|
||||
for (auto i (probable_reps.get<tag_weight> ().begin ()), n (probable_reps.get<tag_weight> ().end ()); i != n; ++i)
|
||||
{
|
||||
auto weight (i->weight.number ());
|
||||
if (weight > 0)
|
||||
{
|
||||
result = result + weight;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nano::uint128_t nano::rep_crawler::total_weight ()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (probable_reps_mutex);
|
||||
return total_weight_internal ();
|
||||
}
|
||||
|
||||
std::vector<nano::representative> nano::rep_crawler::representatives_by_weight ()
|
||||
{
|
||||
std::vector<nano::representative> result;
|
||||
std::lock_guard<std::mutex> lock (probable_reps_mutex);
|
||||
for (auto i (probable_reps.get<tag_weight> ().begin ()), n (probable_reps.get<tag_weight> ().end ()); i != n; ++i)
|
||||
{
|
||||
auto weight (i->weight.number ());
|
||||
if (weight > 0)
|
||||
{
|
||||
result.push_back (*i);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::rep_crawler::on_rep_request (nano::endpoint const & endpoint_a)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (probable_reps_mutex);
|
||||
|
||||
using probable_rep_itr_t = probably_rep_t::index<tag_endpoint>::type::iterator;
|
||||
probably_rep_t::index<tag_endpoint>::type & endpoint_index = probable_reps.get<tag_endpoint> ();
|
||||
|
||||
// Find and update the timestamp on all reps available on the endpoint (a single host may have multiple reps)
|
||||
std::vector<probable_rep_itr_t> view;
|
||||
auto itr_pair = probable_reps.get<tag_endpoint> ().equal_range (endpoint_a);
|
||||
for (; itr_pair.first != itr_pair.second; itr_pair.first++)
|
||||
{
|
||||
endpoint_index.modify (itr_pair.first, [](nano::representative & value_a) {
|
||||
value_a.last_request = std::chrono::steady_clock::now ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<nano::representative> nano::rep_crawler::representatives (size_t count_a)
|
||||
{
|
||||
std::vector<representative> result;
|
||||
result.reserve (std::min (count_a, size_t (16)));
|
||||
std::lock_guard<std::mutex> lock (probable_reps_mutex);
|
||||
for (auto i (probable_reps.get<tag_weight> ().begin ()), n (probable_reps.get<tag_weight> ().end ()); i != n && result.size () < count_a; ++i)
|
||||
{
|
||||
if (!i->weight.is_zero ())
|
||||
{
|
||||
result.push_back (*i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<nano::endpoint> nano::rep_crawler::representative_endpoints (size_t count_a)
|
||||
{
|
||||
std::vector<nano::endpoint> result;
|
||||
auto reps (representatives (count_a));
|
||||
for (auto rep : reps)
|
||||
{
|
||||
result.push_back (rep.endpoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Total number of representatives */
|
||||
size_t nano::rep_crawler::representative_count ()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock (probable_reps_mutex);
|
||||
return probable_reps.size ();
|
||||
}
|
||||
134
nano/node/repcrawler.hpp
Normal file
134
nano/node/repcrawler.hpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#pragma once
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/multi_index/hashed_index.hpp>
|
||||
#include <boost/multi_index/member.hpp>
|
||||
#include <boost/multi_index/ordered_index.hpp>
|
||||
#include <boost/multi_index/random_access_index.hpp>
|
||||
#include <boost/multi_index_container.hpp>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace mi = boost::multi_index;
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class node;
|
||||
|
||||
/**
|
||||
* A representative picked up during repcrawl.
|
||||
*/
|
||||
class representative
|
||||
{
|
||||
public:
|
||||
representative (nano::account account_a, nano::amount weight_a, nano::endpoint endpoint_a) :
|
||||
account (account_a), weight (weight_a), endpoint (endpoint_a)
|
||||
{
|
||||
}
|
||||
nano::account account{ 0 };
|
||||
nano::amount weight{ 0 };
|
||||
nano::endpoint endpoint;
|
||||
std::chrono::steady_clock::time_point last_request{ std::chrono::steady_clock::time_point () };
|
||||
std::chrono::steady_clock::time_point last_response{ std::chrono::steady_clock::time_point () };
|
||||
};
|
||||
|
||||
/**
|
||||
* Crawls the network for representatives. Queries are performed by requesting confirmation of a
|
||||
* random block and observing the corresponding vote.
|
||||
*/
|
||||
class rep_crawler
|
||||
{
|
||||
friend std::unique_ptr<seq_con_info_component> collect_seq_con_info (rep_crawler & rep_crawler, const std::string & name);
|
||||
|
||||
// clang-format off
|
||||
class tag_endpoint {};
|
||||
class tag_last_request {};
|
||||
class tag_account {};
|
||||
class tag_weight {};
|
||||
|
||||
using probably_rep_t = boost::multi_index_container<representative,
|
||||
mi::indexed_by<
|
||||
mi::hashed_unique<mi::member<representative, nano::account, &representative::account>>,
|
||||
mi::random_access<>,
|
||||
mi::ordered_non_unique<mi::tag<tag_last_request>,
|
||||
mi::member<representative, std::chrono::steady_clock::time_point, &representative::last_request>>,
|
||||
mi::ordered_non_unique<mi::tag<tag_weight>,
|
||||
mi::member<representative, nano::amount, &representative::weight>, std::greater<nano::amount>>,
|
||||
mi::ordered_non_unique<mi::tag<tag_endpoint>,
|
||||
mi::member<representative, nano::endpoint, &representative::endpoint>>>>;
|
||||
// clang-format on
|
||||
|
||||
public:
|
||||
rep_crawler (nano::node & node_a);
|
||||
|
||||
/** Start crawling */
|
||||
void start ();
|
||||
|
||||
/** Add block hash to list of active rep queries */
|
||||
void add (nano::block_hash const &);
|
||||
|
||||
/** Remove block hash from list of active rep queries */
|
||||
void remove (nano::block_hash const &);
|
||||
|
||||
/** Check if block hash is in the list of active rep queries */
|
||||
bool exists (nano::block_hash const &);
|
||||
|
||||
/** Attempt to determine if the peer manages one or more representative accounts */
|
||||
void query (std::vector<nano::endpoint> const & endpoints_a);
|
||||
|
||||
/** Attempt to determine if the peer manages one or more representative accounts */
|
||||
void query (nano::endpoint const & endpoint_a);
|
||||
|
||||
/**
|
||||
* Called when a non-replay vote on a block previously sent by query() is received. This indiciates
|
||||
* with high probability that the endpoint is a representative node.
|
||||
* @return True if the rep entry was updated with new information due to increase in weight.
|
||||
*/
|
||||
bool response (nano::endpoint const & endpoint_a, nano::account const & rep_account_a, nano::amount const & weight_a);
|
||||
|
||||
/** Get total available weight from representatives */
|
||||
nano::uint128_t total_weight ();
|
||||
|
||||
/** Request a list of the top \p count_a known representatives. The maximum number of reps returned is 16. */
|
||||
std::vector<representative> representatives (size_t count_a);
|
||||
|
||||
/** Request a list of the top \p count_a known representative endpoints. The maximum number of reps returned is 16. */
|
||||
std::vector<nano::endpoint> representative_endpoints (size_t count_a);
|
||||
|
||||
/** Returns all representatives registered with weight in descending order */
|
||||
std::vector<nano::representative> representatives_by_weight ();
|
||||
|
||||
/** Total number of representatives */
|
||||
size_t representative_count ();
|
||||
|
||||
private:
|
||||
nano::node & node;
|
||||
|
||||
/** Protects the active-hash container */
|
||||
std::mutex active_mutex;
|
||||
|
||||
/** We have solicted votes for these random blocks */
|
||||
std::unordered_set<nano::block_hash> active;
|
||||
|
||||
/** Called continuously to crawl for representatives */
|
||||
void ongoing_crawl ();
|
||||
|
||||
/** Returns a list of endpoints to crawl */
|
||||
std::vector<nano::endpoint> get_crawl_targets ();
|
||||
|
||||
/** When a rep request is made, this is called to update the last-request timestamp. */
|
||||
void on_rep_request (nano::endpoint const & endpoint_a);
|
||||
|
||||
/** Protects the probable_reps container */
|
||||
std::mutex probable_reps_mutex;
|
||||
|
||||
/** Get total available weight from representatives (must be called with a lock on probable_reps_mutex) */
|
||||
nano::uint128_t total_weight_internal ();
|
||||
|
||||
/** Probable representatives */
|
||||
probably_rep_t probable_reps;
|
||||
};
|
||||
}
|
||||
|
|
@ -1688,16 +1688,16 @@ void nano::rpc_handler::confirmation_quorum ()
|
|||
response_l.put ("online_weight_quorum_percent", std::to_string (node.config.online_weight_quorum));
|
||||
response_l.put ("online_weight_minimum", node.config.online_weight_minimum.to_string_dec ());
|
||||
response_l.put ("online_stake_total", node.online_reps.online_stake ().convert_to<std::string> ());
|
||||
response_l.put ("peers_stake_total", node.peers.total_weight ().convert_to<std::string> ());
|
||||
response_l.put ("peers_stake_total", node.rep_crawler.total_weight ().convert_to<std::string> ());
|
||||
if (request.get<bool> ("peer_details", false))
|
||||
{
|
||||
boost::property_tree::ptree peers;
|
||||
for (auto & peer : node.peers.list_probable_rep_weights ())
|
||||
for (auto & peer : node.rep_crawler.representatives_by_weight ())
|
||||
{
|
||||
boost::property_tree::ptree peer_node;
|
||||
peer_node.put ("account", peer.probable_rep_account.to_account ());
|
||||
peer_node.put ("ip", peer.ip_address.to_string ());
|
||||
peer_node.put ("weight", peer.rep_weight.to_string_dec ());
|
||||
peer_node.put ("account", peer.account.to_account ());
|
||||
peer_node.put ("ip", peer.endpoint.address ().to_string ());
|
||||
peer_node.put ("weight", peer.weight.to_string_dec ());
|
||||
peers.push_back (std::make_pair ("", peer_node));
|
||||
}
|
||||
response_l.add_child ("peers", peers);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue