Adding forked votes to inactive vote cache. (#2463)

There's no guarantee that the first block we observe is the winning fork. This change processes all votes for a particular election instead of just the ones for thee first block observed.
This commit is contained in:
clemahieu 2020-01-08 16:06:38 -03:00 committed by GitHub
commit 97e164f10f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 31 deletions

View file

@ -411,6 +411,37 @@ TEST (active_transactions, inactive_votes_cache)
ASSERT_EQ (1, system.nodes[0]->stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
}
TEST (active_transactions, inactive_votes_cache_fork)
{
nano::system system (1);
nano::block_hash latest (system.nodes[0]->latest (nano::test_genesis_key.pub));
nano::keypair key;
auto send1 (std::make_shared<nano::send_block> (latest, key.pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (latest)));
auto send2 (std::make_shared<nano::send_block> (latest, key.pub, nano::genesis_amount - 200, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (latest)));
auto vote (std::make_shared<nano::vote> (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, std::vector<nano::block_hash> (1, send1->hash ())));
system.nodes[0]->vote_processor.vote (vote, std::make_shared<nano::transport::channel_udp> (system.nodes[0]->network.udp_channels, system.nodes[0]->network.endpoint (), system.nodes[0]->network_params.protocol.protocol_version));
auto channel1 (system.nodes [0]->network.udp_channels.create (system.nodes [0]->network.endpoint ()));
system.deadline_set (5s);
while (system.nodes[0]->active.inactive_votes_cache_size () != 1)
{
ASSERT_NO_ERROR (system.poll ());
}
system.nodes[0]->network.process_message (nano::publish (send2), channel1);
system.nodes[0]->block_processor.flush ();
ASSERT_NE (nullptr, system.nodes[0]->block (send2->hash ()));
system.nodes[0]->network.process_message (nano::publish (send1), channel1);
system.nodes[0]->block_processor.flush ();
bool confirmed (false);
system.deadline_set (5s);
while (!confirmed)
{
auto transaction (system.nodes[0]->store.tx_begin_read ());
confirmed = system.nodes[0]->block (send1->hash ()) != nullptr && system.nodes[0]->ledger.block_confirmed (transaction, send1->hash ());
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (1, system.nodes[0]->stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
}
TEST (active_transactions, inactive_votes_cache_existing_vote)
{
nano::system system;
@ -457,7 +488,7 @@ TEST (active_transactions, inactive_votes_cache_existing_vote)
// Attempt to change vote with inactive_votes_cache
node->active.add_inactive_votes_cache (send->hash (), key.pub);
ASSERT_EQ (1, node->active.find_inactive_votes_cache (send->hash ()).voters.size ());
election->insert_inactive_votes_cache ();
election->insert_inactive_votes_cache (send->hash ());
// Check that election data is not changed
ASSERT_EQ (2, election->last_votes.size ());
auto last_vote2 (election->last_votes[key.pub]);

View file

@ -523,7 +523,7 @@ bool nano::active_transactions::add (std::shared_ptr<nano::block> block_a, bool
roots.get<tag_root> ().emplace (nano::conflict_info{ root, difficulty, difficulty, election });
blocks.insert (std::make_pair (hash, election));
adjust_difficulty (hash);
election->insert_inactive_votes_cache ();
election->insert_inactive_votes_cache (hash);
}
}
return error;

View file

@ -218,6 +218,7 @@ bool nano::election::publish (std::shared_ptr<nano::block> block_a)
if (blocks.find (block_a->hash ()) == blocks.end ())
{
blocks.insert (std::make_pair (block_a->hash (), block_a));
insert_inactive_votes_cache (block_a->hash ());
confirm_if_quorum ();
node.network.flood_block (block_a, false);
}
@ -296,13 +297,12 @@ void nano::election::clear_blocks ()
}
}
void nano::election::insert_inactive_votes_cache ()
void nano::election::insert_inactive_votes_cache (nano::block_hash const & hash_a)
{
auto winner_hash (status.winner->hash ());
auto cache (node.active.find_inactive_votes_cache (winner_hash));
auto cache (node.active.find_inactive_votes_cache (hash_a));
for (auto & rep : cache.voters)
{
auto inserted (last_votes.emplace (rep, nano::vote_info{ std::chrono::steady_clock::time_point::min (), 0, winner_hash }));
auto inserted (last_votes.emplace (rep, nano::vote_info{ std::chrono::steady_clock::time_point::min (), 0, hash_a }));
if (inserted.second)
{
node.stats.inc (nano::stat::type::election, nano::stat::detail::vote_cached);

View file

@ -67,7 +67,7 @@ public:
void update_dependent ();
void clear_dependent ();
void clear_blocks ();
void insert_inactive_votes_cache ();
void insert_inactive_votes_cache (nano::block_hash const &);
void stop ();
nano::node & node;
std::unordered_map<nano::account, nano::vote_info> last_votes;

View file

@ -551,32 +551,26 @@ void nano::node::process_fork (nano::transaction const & transaction_a, std::sha
std::shared_ptr<nano::block> ledger_block (ledger.forked_block (transaction_a, *block_a));
if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ()))
{
// Clear inactive votes cache for forks
{
nano::lock_guard<std::mutex> lock (active.mutex);
active.erase_inactive_votes_cache (ledger_block->hash ());
active.erase_inactive_votes_cache (block_a->hash ());
}
std::weak_ptr<nano::node> this_w (shared_from_this ());
if (!active.start (ledger_block, false, [this_w, root](std::shared_ptr<nano::block>) {
if (auto this_l = this_w.lock ())
{
auto attempt (this_l->bootstrap_initiator.current_attempt ());
if (attempt && attempt->mode == nano::bootstrap_mode::legacy)
{
auto transaction (this_l->store.tx_begin_read ());
auto account (this_l->ledger.store.frontier_get (transaction, root));
if (!account.is_zero ())
{
attempt->requeue_pull (nano::pull_info (account, root, root));
}
else if (this_l->ledger.store.account_exists (transaction, root))
{
attempt->requeue_pull (nano::pull_info (root, nano::block_hash (0), nano::block_hash (0)));
}
}
}
}))
if (auto this_l = this_w.lock ())
{
auto attempt (this_l->bootstrap_initiator.current_attempt ());
if (attempt && attempt->mode == nano::bootstrap_mode::legacy)
{
auto transaction (this_l->store.tx_begin_read ());
auto account (this_l->ledger.store.frontier_get (transaction, root));
if (!account.is_zero ())
{
attempt->requeue_pull (nano::pull_info (account, root, root));
}
else if (this_l->ledger.store.account_exists (transaction, root))
{
attempt->requeue_pull (nano::pull_info (root, nano::block_hash (0), nano::block_hash (0)));
}
}
}
}))
{
logger.always_log (boost::str (boost::format ("Resolving fork between our block: %1% and block %2% both with root %3%") % ledger_block->hash ().to_string () % block_a->hash ().to_string () % block_a->root ().to_string ()));
network.broadcast_confirm_req (ledger_block);