Election insertion cleanup (#2890)

* Start election as passive by default, scrapping the idle state

* (Unrelated) small optimization to not run activate() twice when an account sends to itself

* Early-break with no fallthrough in election::valid_change

* Always generate votes if election inserted or ongoing

* Fix intermit failure in test due to activations
This commit is contained in:
Guilherme Lawless 2020-08-21 18:54:52 +01:00 committed by GitHub
commit 9102122820
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 36 additions and 89 deletions

View file

@ -11,11 +11,7 @@ TEST (election, construction)
auto & node = *system.nodes[0];
genesis.open->sideband_set (nano::block_sideband (nano::genesis_account, 0, nano::genesis_amount, 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false, false, false, nano::epoch::epoch_0));
auto election = node.active.insert (genesis.open).election;
ASSERT_TRUE (election->idle ());
election->transition_active ();
ASSERT_FALSE (election->idle ());
election->transition_passive ();
ASSERT_FALSE (election->idle ());
}
namespace nano

View file

@ -299,12 +299,16 @@ TEST (request_aggregator, unique)
ASSERT_TIMELY (3s, 1 == node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_generated_votes));
}
namespace nano
{
TEST (request_aggregator, cannot_vote)
{
nano::system system;
nano::node_flags flags;
flags.disable_request_loop = true;
auto & node (*system.add_node (flags));
// This prevents activation of blocks which are cemented
node.confirmation_height_processor.cemented_observers.clear ();
nano::genesis genesis;
nano::state_block_builder builder;
auto send1 = builder.make_block ()
@ -378,3 +382,4 @@ TEST (request_aggregator, cannot_vote)
ASSERT_EQ (0, node.stats.count (nano::stat::type::requests, nano::stat::detail::requests_unknown));
ASSERT_TIMELY (3s, 1 == node.stats.count (nano::stat::type::message, nano::stat::detail::confirm_ack, nano::stat::dir::out));
}
}

View file

@ -95,12 +95,17 @@ void nano::active_transactions::confirm_prioritized_frontiers (nano::transaction
{
auto block (this->node.store.block_get (transaction_a, info.head));
auto previous_balance (this->node.ledger.balance (transaction_a, block->previous ()));
auto insert_result = this->insert (block, previous_balance);
if (insert_result.inserted)
lk.lock ();
if (roots.get<tag_root> ().find (block->qualified_root ()) == roots.get<tag_root> ().end ())
{
insert_result.election->transition_active ();
++elections_count;
auto insert_result = this->insert_impl (block, previous_balance);
if (insert_result.inserted)
{
insert_result.election->transition_active ();
++elections_count;
}
}
lk.unlock ();
}
}
}
@ -191,7 +196,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::b
// Start or vote for the next unconfirmed block in the destination account
auto const & destination (node.ledger.block_destination (transaction, *block_a));
if (!destination.is_zero ())
if (!destination.is_zero () && destination != account)
{
activate (destination);
}
@ -648,6 +653,13 @@ nano::election_insertion_result nano::active_transactions::insert_impl (std::sha
{
result.election = existing->election;
}
// Votes are generated for inserted or ongoing elections if they're prioritized
// Non-priority elections generate votes when they gain priority in the future
if (result.election && result.election->prioritized ())
{
result.election->generate_votes ();
}
}
return result;
}
@ -791,17 +803,9 @@ nano::election_insertion_result nano::active_transactions::activate (nano::accou
if (node.ledger.can_vote (transaction, *block))
{
result = insert (block);
if (result.election)
if (result.inserted)
{
if (result.inserted)
{
result.election->transition_active ();
}
else if (result.election->prioritized ())
{
// Generate vote for ongoing election
result.election->generate_votes (block->hash ());
}
result.election->transition_active ();
}
}
}

View file

@ -297,15 +297,7 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std::
// Start collecting quorum on block
if (watch_work_a || node.ledger.can_vote (node.store.tx_begin_read (), *block_a))
{
auto election = node.active.insert (block_a, process_return_a.previous_balance.number ());
if (election.inserted)
{
election.election->transition_passive ();
}
else if (election.election)
{
election.election->try_generate_votes (block_a->hash ());
}
node.active.insert (block_a, process_return_a.previous_balance.number ());
}
// Announce block contents to the network

View file

@ -93,6 +93,7 @@ private:
friend class confirmation_height_many_accounts_many_confirmations_Test;
friend class confirmation_height_long_chains_Test;
friend class confirmation_height_many_accounts_single_confirmation_Test;
friend class request_aggregator_cannot_vote_Test;
};
std::unique_ptr<container_info_component> collect_container_info (confirmation_height_processor &, const std::string &);

View file

@ -34,10 +34,6 @@ root (block_a->root ())
last_votes.emplace (node.network_params.random.not_an_account, nano::vote_info{ std::chrono::steady_clock::now (), 0, block_a->hash () });
blocks.emplace (block_a->hash (), block_a);
update_dependent ();
if (prioritized_a)
{
generate_votes (block_a->hash ());
}
}
void nano::election::confirm_once (nano::election_status_type type_a)
@ -71,21 +67,9 @@ bool nano::election::valid_change (nano::election::state_t expected_a, nano::ele
bool result = false;
switch (expected_a)
{
case nano::election::state_t::idle:
switch (desired_a)
{
case nano::election::state_t::passive:
case nano::election::state_t::active:
result = true;
break;
default:
break;
}
break;
case nano::election::state_t::passive:
switch (desired_a)
{
case nano::election::state_t::idle:
case nano::election::state_t::active:
case nano::election::state_t::confirmed:
case nano::election::state_t::expired_unconfirmed:
@ -98,7 +82,6 @@ bool nano::election::valid_change (nano::election::state_t expected_a, nano::ele
case nano::election::state_t::active:
switch (desired_a)
{
case nano::election::state_t::idle:
case nano::election::state_t::broadcasting:
case nano::election::state_t::confirmed:
case nano::election::state_t::expired_unconfirmed:
@ -107,10 +90,10 @@ bool nano::election::valid_change (nano::election::state_t expected_a, nano::ele
default:
break;
}
break;
case nano::election::state_t::broadcasting:
switch (desired_a)
{
case nano::election::state_t::idle:
case nano::election::state_t::backtracking:
case nano::election::state_t::confirmed:
case nano::election::state_t::expired_unconfirmed:
@ -119,10 +102,10 @@ bool nano::election::valid_change (nano::election::state_t expected_a, nano::ele
default:
break;
}
break;
case nano::election::state_t::backtracking:
switch (desired_a)
{
case nano::election::state_t::idle:
case nano::election::state_t::confirmed:
case nano::election::state_t::expired_unconfirmed:
result = true;
@ -130,6 +113,7 @@ bool nano::election::valid_change (nano::election::state_t expected_a, nano::ele
default:
break;
}
break;
case nano::election::state_t::confirmed:
switch (desired_a)
{
@ -139,8 +123,8 @@ bool nano::election::valid_change (nano::election::state_t expected_a, nano::ele
default:
break;
}
case nano::election::state_t::expired_unconfirmed:
break;
case nano::election::state_t::expired_unconfirmed:
case nano::election::state_t::expired_confirmed:
break;
}
@ -178,17 +162,6 @@ void nano::election::send_confirm_req (nano::confirmation_solicitor & solicitor_
}
}
void nano::election::transition_passive ()
{
nano::lock_guard<std::mutex> guard (timepoints_mutex);
transition_passive_impl ();
}
void nano::election::transition_passive_impl ()
{
state_change (nano::election::state_t::idle, nano::election::state_t::passive);
}
void nano::election::transition_active ()
{
nano::lock_guard<std::mutex> guard (timepoints_mutex);
@ -197,12 +170,7 @@ void nano::election::transition_active ()
void nano::election::transition_active_impl ()
{
state_change (nano::election::state_t::idle, nano::election::state_t::active);
}
bool nano::election::idle () const
{
return state_m == nano::election::state_t::idle;
state_change (nano::election::state_t::passive, nano::election::state_t::active);
}
bool nano::election::confirmed () const
@ -234,16 +202,12 @@ bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a
bool result = false;
switch (state_m)
{
case nano::election::state_t::idle:
break;
case nano::election::state_t::passive:
{
if (base_latency () * passive_duration_factor < std::chrono::steady_clock::now () - state_start)
{
state_change (nano::election::state_t::passive, nano::election::state_t::active);
}
break;
}
case nano::election::state_t::active:
send_confirm_req (solicitor_a);
if (confirmation_request_count > active_request_count_min)
@ -579,21 +543,12 @@ void nano::election::prioritize_election (nano::vote_generator_session & generat
generator_session_a.add (root, status.winner->hash ());
}
void nano::election::try_generate_votes (nano::block_hash const & hash_a)
{
nano::unique_lock<std::mutex> lock (node.active.mutex);
if (status.winner->hash () == hash_a)
{
lock.unlock ();
generate_votes (hash_a);
}
}
void nano::election::generate_votes (nano::block_hash const & hash_a)
void nano::election::generate_votes ()
{
debug_assert (!node.active.mutex.try_lock ());
if (node.config.enable_voting && node.wallets.reps ().voting > 0)
{
node.active.generator.add (root, hash_a);
node.active.generator.add (root, status.winner->hash ());
}
}

View file

@ -39,7 +39,6 @@ class election final : public std::enable_shared_from_this<nano::election>
private: // State management
enum class state_t
{
idle,
passive, // only listening for incoming votes
active, // actively request confirmations
broadcasting, // request confirmations and broadcast the winner
@ -52,7 +51,7 @@ private: // State management
static int constexpr active_request_count_min = 2;
static int constexpr active_broadcasting_duration_factor = 30;
static int constexpr confirmed_duration_factor = 5;
std::atomic<nano::election::state_t> state_m = { state_t::idle };
std::atomic<nano::election::state_t> state_m = { state_t::passive };
// These time points must be protected by this mutex
std::mutex timepoints_mutex;
@ -66,7 +65,7 @@ private: // State management
void send_confirm_req (nano::confirmation_solicitor &);
void activate_dependencies ();
// Calculate votes for local representatives
void generate_votes (nano::block_hash const &);
void generate_votes ();
void remove_votes (nano::block_hash const &);
std::atomic<bool> prioritized_m = { false };
@ -87,22 +86,17 @@ public:
size_t insert_inactive_votes_cache (nano::block_hash const &);
bool prioritized () const;
void prioritize_election (nano::vote_generator_session &);
// Calculate votes if the current winner matches \p hash_a
void try_generate_votes (nano::block_hash const & hash_a);
// Erase all blocks from active and, if not confirmed, clear digests from network filters
void cleanup ();
public: // State transitions
bool transition_time (nano::confirmation_solicitor &);
void transition_passive ();
void transition_active ();
private:
void transition_passive_impl ();
void transition_active_impl ();
public:
bool idle () const;
bool confirmed () const;
nano::node & node;
std::unordered_map<nano::account, nano::vote_info> last_votes;