Return per hash vote result (#4510)
* Election use `nano::vote_code` * Return map of results from `active_transactions::vote (...)` * Fix test * Use `to_stat_detail (nano::vote_code)` helper * Ignore duplicate hashes when processing votes * Compilation fix * Fix compilation
This commit is contained in:
parent
e8beaa8f89
commit
d2e6f97283
13 changed files with 121 additions and 109 deletions
|
@ -583,19 +583,19 @@ TEST (active_transactions, vote_replays)
|
|||
|
||||
// First vote is not a replay and confirms the election, second vote should be a replay since the election has confirmed but not yet removed
|
||||
auto vote_send1 = nano::test::make_final_vote (nano::dev::genesis_key, { send1 });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
|
||||
// Wait until the election is removed, at which point the vote is still a replay since it's been recently confirmed
|
||||
ASSERT_TIMELY_EQ (5s, node.active.size (), 1);
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
|
||||
// Open new account
|
||||
auto vote_open1 = nano::test::make_final_vote (nano::dev::genesis_key, { open1 });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_TIMELY (5s, node.active.empty ());
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_EQ (nano::Gxrb_ratio, node.ledger.weight (key.pub));
|
||||
|
||||
// send 1 raw to key to key
|
||||
|
@ -616,27 +616,27 @@ TEST (active_transactions, vote_replays)
|
|||
// vote2_send2 is a non final vote with little weight, vote1_send2 is the vote that confirms the election
|
||||
auto vote1_send2 = nano::test::make_final_vote (nano::dev::genesis_key, { send2 });
|
||||
auto vote2_send2 = nano::test::make_vote (key, { send2 }, 0, 0);
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote2_send2)); // this vote cannot confirm the election
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote2_send2).at (send2->hash ())); // this vote cannot confirm the election
|
||||
ASSERT_EQ (1, node.active.size ());
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2)); // this vote cannot confirm the election
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2).at (send2->hash ())); // this vote cannot confirm the election
|
||||
ASSERT_EQ (1, node.active.size ());
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2)); // this vote confirms the election
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote1_send2).at (send2->hash ())); // this vote confirms the election
|
||||
|
||||
// this should still return replay, either because the election is still in the AEC or because it is recently confirmed
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2).at (send2->hash ()));
|
||||
ASSERT_TIMELY (5s, node.active.empty ());
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote1_send2).at (send2->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::replay, node.active.vote (vote2_send2).at (send2->hash ()));
|
||||
|
||||
// Removing blocks as recently confirmed makes every vote indeterminate
|
||||
{
|
||||
nano::lock_guard<nano::mutex> guard (node.active.mutex);
|
||||
node.active.recently_confirmed.clear ();
|
||||
}
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_send1).at (send1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote_open1).at (open1->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote1_send2).at (send2->hash ()));
|
||||
ASSERT_EQ (nano::vote_code::indeterminate, node.active.vote (vote2_send2).at (send2->hash ()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ TEST (election, quorum_minimum_flip_success)
|
|||
ASSERT_TIMELY_EQ (5s, election->blocks ().size (), 2);
|
||||
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send2->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send2->hash ()));
|
||||
|
||||
ASSERT_TIMELY (5s, election->confirmed ());
|
||||
auto const winner = election->winner ();
|
||||
|
@ -121,7 +121,7 @@ TEST (election, quorum_minimum_flip_fail)
|
|||
|
||||
// genesis generates a final vote for send2 but it should not be enough to reach quorum due to the online_weight_minimum being so high
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send2->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send2->hash ()));
|
||||
|
||||
// give the election some time before asserting it is not confirmed so that in case
|
||||
// it would be wrongfully confirmed, have that immediately fail instead of race
|
||||
|
@ -157,7 +157,7 @@ TEST (election, quorum_minimum_confirm_success)
|
|||
ASSERT_NE (nullptr, election);
|
||||
ASSERT_EQ (1, election->blocks ().size ());
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send1->hash ()));
|
||||
ASSERT_NE (nullptr, node1.block (send1->hash ()));
|
||||
ASSERT_TIMELY (5s, election->confirmed ());
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ TEST (election, quorum_minimum_confirm_fail)
|
|||
ASSERT_EQ (1, election->blocks ().size ());
|
||||
|
||||
auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote).at (send1->hash ()));
|
||||
|
||||
// give the election a chance to confirm
|
||||
WAIT (1s);
|
||||
|
@ -251,7 +251,7 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks)
|
|||
ASSERT_EQ (1, election->blocks ().size ());
|
||||
|
||||
auto vote1 = nano::test::make_final_vote (nano::dev::genesis_key, { send1->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1).at (send1->hash ()));
|
||||
|
||||
auto channel = node1.network.find_node_id (node2.get_node_id ());
|
||||
ASSERT_NE (channel, nullptr);
|
||||
|
@ -265,7 +265,7 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks)
|
|||
// Modify online_m for online_reps to more than is available, this checks that voting below updates it to current online reps.
|
||||
node1.online_reps.online_m = node_config.online_weight_minimum.number () + 20;
|
||||
}
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2).at (send1->hash ()));
|
||||
ASSERT_TIMELY (5s, election->confirmed ());
|
||||
ASSERT_NE (nullptr, node1.block (send1->hash ()));
|
||||
}
|
||||
|
|
|
@ -932,9 +932,9 @@ TEST (votes, add_one)
|
|||
auto election1 = node1.active.election (send1->qualified_root ());
|
||||
ASSERT_EQ (1, election1->votes ().size ());
|
||||
auto vote1 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, nano::vote::timestamp_min * 1, 0);
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1).at (send1->hash ()));
|
||||
auto vote2 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, nano::vote::timestamp_min * 2, 0);
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
|
||||
ASSERT_EQ (nano::vote_code::ignored, node1.active.vote (vote2).at (send1->hash ())); // Ignored due to vote cooldown
|
||||
ASSERT_EQ (2, election1->votes ().size ());
|
||||
auto votes1 (election1->votes ());
|
||||
auto existing1 (votes1.find (nano::dev::genesis_key.pub));
|
||||
|
@ -973,7 +973,7 @@ TEST (votes, add_existing)
|
|||
ASSERT_TIMELY (5s, node1.active.election (send1->qualified_root ()));
|
||||
auto election1 = node1.active.election (send1->qualified_root ());
|
||||
auto vote1 = nano::test::make_vote (nano::dev::genesis_key, { send1 }, nano::vote::timestamp_min * 1, 0);
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1).at (send1->hash ()));
|
||||
// Block is already processed from vote
|
||||
ASSERT_TRUE (node1.active.publish (send1));
|
||||
ASSERT_EQ (nano::vote::timestamp_min * 1, election1->last_votes[nano::dev::genesis_key.pub].timestamp);
|
||||
|
@ -995,13 +995,13 @@ TEST (votes, add_existing)
|
|||
auto vote_info1 = election1->get_last_vote (nano::dev::genesis_key.pub);
|
||||
vote_info1.time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
|
||||
election1->set_last_vote (nano::dev::genesis_key.pub, vote_info1);
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2));
|
||||
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote2).at (send2->hash ()));
|
||||
ASSERT_EQ (nano::vote::timestamp_min * 2, election1->last_votes[nano::dev::genesis_key.pub].timestamp);
|
||||
// Also resend the old vote, and see if we respect the timestamp
|
||||
auto vote_info2 = election1->get_last_vote (nano::dev::genesis_key.pub);
|
||||
vote_info2.time = std::chrono::steady_clock::now () - std::chrono::seconds (20);
|
||||
election1->set_last_vote (nano::dev::genesis_key.pub, vote_info2);
|
||||
ASSERT_EQ (nano::vote_code::replay, node1.active.vote (vote1));
|
||||
ASSERT_EQ (nano::vote_code::replay, node1.active.vote (vote1).at (send1->hash ()));
|
||||
ASSERT_EQ (nano::vote::timestamp_min * 2, election1->votes ()[nano::dev::genesis_key.pub].timestamp);
|
||||
auto votes (election1->votes ());
|
||||
ASSERT_EQ (2, votes.size ());
|
||||
|
|
|
@ -208,7 +208,7 @@ TEST (vote_processor, no_broadcast_local)
|
|||
ASSERT_FALSE (node.wallets.reps ().have_half_rep ()); // Genesis balance remaining after `send' is less than the half_rep threshold
|
||||
// Process a vote with a key that is in the local wallet.
|
||||
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector<nano::block_hash>{ send->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ()));
|
||||
// Make sure the vote was processed.
|
||||
auto election (node.active.election (send->qualified_root ()));
|
||||
ASSERT_NE (nullptr, election);
|
||||
|
@ -256,7 +256,7 @@ TEST (vote_processor, local_broadcast_without_a_representative)
|
|||
node.start_election (send);
|
||||
// Process a vote without a representative
|
||||
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector<nano::block_hash>{ send->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ()));
|
||||
// Make sure the vote was processed.
|
||||
std::shared_ptr<nano::election> election;
|
||||
ASSERT_TIMELY (5s, election = node.active.election (send->qualified_root ()));
|
||||
|
@ -309,7 +309,7 @@ TEST (vote_processor, no_broadcast_local_with_a_principal_representative)
|
|||
ASSERT_TRUE (node.wallets.reps ().have_half_rep ()); // Genesis balance after `send' is over both half_rep and PR threshold.
|
||||
// Process a vote with a key that is in the local wallet.
|
||||
auto vote = std::make_shared<nano::vote> (nano::dev::genesis_key.pub, nano::dev::genesis_key.prv, nano::milliseconds_since_epoch (), nano::vote::duration_max, std::vector<nano::block_hash>{ send->hash () });
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote));
|
||||
ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote).at (send->hash ()));
|
||||
// Make sure the vote was processed.
|
||||
auto election (node.active.election (send->qualified_root ()));
|
||||
ASSERT_NE (nullptr, election);
|
||||
|
|
|
@ -84,6 +84,7 @@ enum class detail : uint8_t
|
|||
none,
|
||||
success,
|
||||
unknown,
|
||||
cache,
|
||||
|
||||
// processing queue
|
||||
queue,
|
||||
|
@ -167,12 +168,15 @@ enum class detail : uint8_t
|
|||
frontier_confirmation_failed,
|
||||
error_socket_close,
|
||||
|
||||
// vote specific
|
||||
vote_valid,
|
||||
vote_replay,
|
||||
vote_indeterminate,
|
||||
vote_invalid,
|
||||
// vote result
|
||||
vote,
|
||||
valid,
|
||||
replay,
|
||||
indeterminate,
|
||||
|
||||
// vote processor
|
||||
vote_overflow,
|
||||
vote_ignored,
|
||||
|
||||
// election specific
|
||||
vote_new,
|
||||
|
|
|
@ -438,31 +438,33 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p
|
|||
}
|
||||
|
||||
// Validate a vote and apply it to the current election if one exists
|
||||
nano::vote_code nano::active_transactions::vote (std::shared_ptr<nano::vote> const & vote_a)
|
||||
std::unordered_map<nano::block_hash, nano::vote_code> nano::active_transactions::vote (std::shared_ptr<nano::vote> const & vote)
|
||||
{
|
||||
nano::vote_code result{ nano::vote_code::indeterminate };
|
||||
// If all hashes were recently confirmed then it is a replay
|
||||
unsigned recently_confirmed_counter (0);
|
||||
|
||||
std::vector<std::pair<std::shared_ptr<nano::election>, nano::block_hash>> process;
|
||||
std::unordered_map<nano::block_hash, nano::vote_code> results;
|
||||
std::unordered_map<nano::block_hash, std::shared_ptr<nano::election>> process;
|
||||
std::vector<nano::block_hash> inactive; // Hashes that should be added to inactive vote cache
|
||||
|
||||
{
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
for (auto const & hash : vote_a->hashes)
|
||||
for (auto const & hash : vote->hashes)
|
||||
{
|
||||
auto existing (blocks.find (hash));
|
||||
if (existing != blocks.end ())
|
||||
// Ignore duplicate hashes (should not happen with a well-behaved voting node)
|
||||
if (results.find (hash) != results.end ())
|
||||
{
|
||||
process.emplace_back (existing->second, hash);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto existing = blocks.find (hash); existing != blocks.end ())
|
||||
{
|
||||
process[hash] = existing->second;
|
||||
}
|
||||
else if (!recently_confirmed.exists (hash))
|
||||
{
|
||||
inactive.emplace_back (hash);
|
||||
results[hash] = nano::vote_code::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
++recently_confirmed_counter;
|
||||
results[hash] = nano::vote_code::replay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -470,37 +472,38 @@ nano::vote_code nano::active_transactions::vote (std::shared_ptr<nano::vote> con
|
|||
// Process inactive votes outside of the critical section
|
||||
for (auto & hash : inactive)
|
||||
{
|
||||
add_vote_cache (hash, vote_a);
|
||||
add_vote_cache (hash, vote);
|
||||
}
|
||||
|
||||
if (!process.empty ())
|
||||
{
|
||||
bool replay = false;
|
||||
bool processed = false;
|
||||
|
||||
for (auto const & [election, block_hash] : process)
|
||||
for (auto const & [block_hash, election] : process)
|
||||
{
|
||||
auto const vote_result = election->vote (vote_a->account, vote_a->timestamp (), block_hash);
|
||||
processed |= (vote_result == nano::election::vote_result::processed);
|
||||
replay |= (vote_result == nano::election::vote_result::replay);
|
||||
auto const vote_result = election->vote (vote->account, vote->timestamp (), block_hash);
|
||||
results[block_hash] = vote_result;
|
||||
|
||||
processed |= (vote_result == nano::vote_code::vote);
|
||||
}
|
||||
|
||||
// Republish vote if it is new and the node does not host a principal representative (or close to)
|
||||
if (processed)
|
||||
{
|
||||
auto const reps (node.wallets.reps ());
|
||||
if (!reps.have_half_rep () && !reps.exists (vote_a->account))
|
||||
if (!reps.have_half_rep () && !reps.exists (vote->account))
|
||||
{
|
||||
node.network.flood_vote (vote_a, 0.5f);
|
||||
node.network.flood_vote (vote, 0.5f);
|
||||
}
|
||||
}
|
||||
result = replay ? nano::vote_code::replay : nano::vote_code::vote;
|
||||
}
|
||||
else if (recently_confirmed_counter == vote_a->hashes.size ())
|
||||
{
|
||||
result = nano::vote_code::replay;
|
||||
}
|
||||
return result;
|
||||
|
||||
// All hashes should have their result set
|
||||
debug_assert (std::all_of (vote->hashes.begin (), vote->hashes.end (), [&results] (auto const & hash) {
|
||||
return results.find (hash) != results.end ();
|
||||
}));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
bool nano::active_transactions::active (nano::qualified_root const & root_a) const
|
||||
|
|
|
@ -152,7 +152,7 @@ public:
|
|||
*/
|
||||
nano::election_insertion_result insert (std::shared_ptr<nano::block> const &, nano::election_behavior = nano::election_behavior::normal);
|
||||
// Distinguishes replay votes, cannot be determined if the block is not in any election
|
||||
nano::vote_code vote (std::shared_ptr<nano::vote> const &);
|
||||
std::unordered_map<nano::block_hash, nano::vote_code> vote (std::shared_ptr<nano::vote> const &);
|
||||
// Is the root of this block in the roots container
|
||||
bool active (nano::block const &) const;
|
||||
bool active (nano::qualified_root const &) const;
|
||||
|
|
|
@ -434,12 +434,12 @@ std::shared_ptr<nano::block> nano::election::find (nano::block_hash const & hash
|
|||
return result;
|
||||
}
|
||||
|
||||
auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, vote_source vote_source_a) -> vote_result
|
||||
nano::vote_code nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano::block_hash const & block_hash_a, nano::vote_source vote_source_a)
|
||||
{
|
||||
auto weight = node.ledger.weight (rep);
|
||||
if (!node.network_params.network.is_dev_network () && weight <= node.minimum_principal_weight ())
|
||||
{
|
||||
return vote_result::ignored;
|
||||
return vote_code::indeterminate;
|
||||
}
|
||||
|
||||
nano::unique_lock<nano::mutex> lock{ mutex };
|
||||
|
@ -450,11 +450,11 @@ auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano
|
|||
auto last_vote_l (last_vote_it->second);
|
||||
if (last_vote_l.timestamp > timestamp_a)
|
||||
{
|
||||
return vote_result::replay;
|
||||
return vote_code::replay;
|
||||
}
|
||||
if (last_vote_l.timestamp == timestamp_a && !(last_vote_l.hash < block_hash_a))
|
||||
{
|
||||
return vote_result::replay;
|
||||
return vote_code::replay;
|
||||
}
|
||||
|
||||
auto max_vote = timestamp_a == std::numeric_limits<uint64_t>::max () && last_vote_l.timestamp < timestamp_a;
|
||||
|
@ -468,7 +468,7 @@ auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano
|
|||
|
||||
if (!max_vote && !past_cooldown)
|
||||
{
|
||||
return vote_result::ignored;
|
||||
return vote_code::ignored;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -494,7 +494,7 @@ auto nano::election::vote (nano::account const & rep, uint64_t timestamp_a, nano
|
|||
confirm_if_quorum (lock);
|
||||
}
|
||||
|
||||
return vote_result::processed;
|
||||
return vote_code::vote;
|
||||
}
|
||||
|
||||
bool nano::election::publish (std::shared_ptr<nano::block> const & block_a)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <nano/node/election_behavior.hpp>
|
||||
#include <nano/node/election_status.hpp>
|
||||
#include <nano/node/vote_with_weight_info.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
@ -42,24 +43,10 @@ struct election_extended_status final
|
|||
void operator() (nano::object_stream &) const;
|
||||
};
|
||||
|
||||
class election final : public std::enable_shared_from_this<nano::election>
|
||||
class election final : public std::enable_shared_from_this<election>
|
||||
{
|
||||
nano::id_t const id{ nano::next_id () }; // Track individual objects when tracing
|
||||
|
||||
public:
|
||||
enum class vote_source
|
||||
{
|
||||
live,
|
||||
cache,
|
||||
};
|
||||
|
||||
enum class vote_result
|
||||
{
|
||||
ignored,
|
||||
processed,
|
||||
replay,
|
||||
};
|
||||
|
||||
private:
|
||||
// Minimum time between broadcasts of the current winner of an election, as a backup to requesting confirmations
|
||||
std::chrono::milliseconds base_latency () const;
|
||||
|
@ -117,7 +104,7 @@ public: // Interface
|
|||
* Process vote. Internally uses cooldown to throttle non-final votes
|
||||
* If the election reaches consensus, it will be confirmed
|
||||
*/
|
||||
vote_result vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, vote_source = vote_source::live);
|
||||
nano::vote_code vote (nano::account const & representative, uint64_t timestamp, nano::block_hash const & block_hash, nano::vote_source = nano::vote_source::live);
|
||||
bool publish (std::shared_ptr<nano::block> const & block_a);
|
||||
// Confirm this block if quorum is met
|
||||
void confirm_if_quorum (nano::unique_lock<nano::mutex> &);
|
||||
|
|
|
@ -69,8 +69,8 @@ std::size_t nano::vote_cache::entry::fill (std::shared_ptr<nano::election> const
|
|||
std::size_t inserted = 0;
|
||||
for (const auto & entry : voters_m)
|
||||
{
|
||||
auto result = election->vote (entry.representative, entry.timestamp, hash_m, nano::election::vote_source::cache);
|
||||
if (result == nano::election::vote_result::processed)
|
||||
auto result = election->vote (entry.representative, entry.timestamp, hash_m, nano::vote_source::cache);
|
||||
if (result == nano::vote_code::vote)
|
||||
{
|
||||
inserted++;
|
||||
}
|
||||
|
|
|
@ -163,32 +163,25 @@ void nano::vote_processor::verify_votes (decltype (votes) const & votes_a)
|
|||
|
||||
nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr<nano::vote> const & vote_a, std::shared_ptr<nano::transport::channel> const & channel_a, bool validated)
|
||||
{
|
||||
auto result (nano::vote_code::invalid);
|
||||
auto result = nano::vote_code::invalid;
|
||||
if (validated || !vote_a->validate ())
|
||||
{
|
||||
result = active.vote (vote_a);
|
||||
auto vote_results = active.vote (vote_a);
|
||||
|
||||
// Aggregate results for individual hashes
|
||||
bool replay = false;
|
||||
bool processed = false;
|
||||
for (auto const & [hash, hash_result] : vote_results)
|
||||
{
|
||||
replay |= (hash_result == nano::vote_code::replay);
|
||||
processed |= (hash_result == nano::vote_code::vote);
|
||||
}
|
||||
result = replay ? nano::vote_code::replay : (processed ? nano::vote_code::vote : nano::vote_code::indeterminate);
|
||||
|
||||
observers.vote.notify (vote_a, channel_a, result);
|
||||
}
|
||||
std::string status;
|
||||
switch (result)
|
||||
{
|
||||
case nano::vote_code::invalid:
|
||||
status = "Invalid";
|
||||
stats.inc (nano::stat::type::vote, nano::stat::detail::vote_invalid);
|
||||
break;
|
||||
case nano::vote_code::replay:
|
||||
status = "Replay";
|
||||
stats.inc (nano::stat::type::vote, nano::stat::detail::vote_replay);
|
||||
break;
|
||||
case nano::vote_code::vote:
|
||||
status = "Vote";
|
||||
stats.inc (nano::stat::type::vote, nano::stat::detail::vote_valid);
|
||||
break;
|
||||
case nano::vote_code::indeterminate:
|
||||
status = "Indeterminate";
|
||||
stats.inc (nano::stat::type::vote, nano::stat::detail::vote_indeterminate);
|
||||
break;
|
||||
}
|
||||
|
||||
stats.inc (nano::stat::type::vote, to_stat_detail (result));
|
||||
|
||||
logger.trace (nano::log::type::vote_processor, nano::log::detail::vote_processed,
|
||||
nano::log::arg{ "vote", vote_a },
|
||||
|
|
|
@ -341,6 +341,20 @@ nano::block_hash const & nano::unchecked_key::key () const
|
|||
return previous;
|
||||
}
|
||||
|
||||
nano::stat::detail nano::to_stat_detail (nano::vote_code code)
|
||||
{
|
||||
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (code));
|
||||
debug_assert (value);
|
||||
return value.value_or (nano::stat::detail{});
|
||||
}
|
||||
|
||||
nano::stat::detail nano::to_stat_detail (nano::vote_source source)
|
||||
{
|
||||
auto value = magic_enum::enum_cast<nano::stat::detail> (magic_enum::enum_name (source));
|
||||
debug_assert (value);
|
||||
return value.value_or (nano::stat::detail{});
|
||||
}
|
||||
|
||||
std::string_view nano::to_string (nano::block_status code)
|
||||
{
|
||||
return magic_enum::enum_name (code);
|
||||
|
|
|
@ -191,9 +191,20 @@ enum class vote_code
|
|||
invalid, // Vote is not signed correctly
|
||||
replay, // Vote does not have the highest timestamp, it's a replay
|
||||
vote, // Vote has the highest timestamp
|
||||
indeterminate // Unknown if replay or vote
|
||||
indeterminate, // Unknown if replay or vote
|
||||
ignored, // Vote is valid, but got ingored (e.g. due to cooldown)
|
||||
};
|
||||
|
||||
nano::stat::detail to_stat_detail (vote_code);
|
||||
|
||||
enum class vote_source
|
||||
{
|
||||
live,
|
||||
cache,
|
||||
};
|
||||
|
||||
nano::stat::detail to_stat_detail (vote_source);
|
||||
|
||||
enum class block_status
|
||||
{
|
||||
progress, // Hasn't been seen before, signed correctly
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue