Skip to content

Commit

Permalink
Comments
Browse files Browse the repository at this point in the history
  • Loading branch information
pwojcikdev committed Aug 24, 2022
1 parent c8cb794 commit 28eccf7
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 4 deletions.
53 changes: 49 additions & 4 deletions nano/core_test/vote_cache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ nano::keypair create_rep (nano::uint128_t weight)

std::shared_ptr<nano::vote> create_vote (nano::keypair & key, uint64_t timestamp, uint8_t duration, std::vector<nano::block_hash> hashes)
{
return std::make_shared<nano::vote> (key.pub, key.prv, timestamp, duration, hashes);
return nano::test::make_vote (key, hashes, timestamp, duration);
}

nano::block_hash random_hash ()
Expand All @@ -47,7 +47,7 @@ nano::block_hash random_hash ()

nano::vote_cache::config make_config (std::size_t max_size = 1024)
{
nano::vote_cache::config cfg;
nano::vote_cache::config cfg{};
cfg.max_size = max_size;
return cfg;
}
Expand All @@ -62,6 +62,9 @@ TEST (vote_cache, construction)
ASSERT_FALSE (vote_cache.find (hash1));
}

/*
* Inserts single hash to cache, ensures it can be retrieved and dequeued
*/
TEST (vote_cache, insert_one_hash)
{
nano::vote_cache vote_cache{ make_config () };
Expand All @@ -81,6 +84,10 @@ TEST (vote_cache, insert_one_hash)
ASSERT_EQ (peek1->tally, 7);
}

/*
* Inserts multiple votes for single hash
* Ensures all of them can be retrieved and that tally is properly accumulated
*/
TEST (vote_cache, insert_one_hash_many_votes)
{
nano::vote_cache vote_cache{ make_config () };
Expand All @@ -95,57 +102,73 @@ TEST (vote_cache, insert_one_hash_many_votes)
vote_cache.vote (vote1->hashes.front (), vote1);
vote_cache.vote (vote2->hashes.front (), vote2);
vote_cache.vote (vote3->hashes.front (), vote3);
// We have 3 votes but for a single hash, so just one entry in vote cache
ASSERT_EQ (1, vote_cache.cache_size ());
auto peek1 = vote_cache.peek ();
ASSERT_TRUE (peek1);
ASSERT_EQ (peek1->voters.size (), 3);
// Tally must be the sum of rep weights
ASSERT_EQ (peek1->tally, 7 + 9 + 11);
}

/*
* Inserts multiple votes for multiple hashes
* Ensures all of them can be retrieved and that queue returns the highest tally entries first
*/
TEST (vote_cache, insert_many_hashes_many_votes)
{
nano::vote_cache vote_cache{ make_config () };
vote_cache.rep_weight_query = rep_weight_query ();
// There will be 3 random hashes to vote for
auto hash1 = random_hash ();
auto hash2 = random_hash ();
auto hash3 = random_hash ();
// There will be 4 reps with different weights
auto rep1 = create_rep (7);
auto rep2 = create_rep (9);
auto rep3 = create_rep (11);
auto rep4 = create_rep (13);
// Votes: rep1 > hash1, rep2 > hash2, rep3 > hash3, rep4 > hash1 (the same as rep1)
auto vote1 = create_vote (rep1, 1024 * 1024, 0, { hash1 });
auto vote2 = create_vote (rep2, 1024 * 1024, 0, { hash2 });
auto vote3 = create_vote (rep3, 1024 * 1024, 0, { hash3 });
auto vote4 = create_vote (rep4, 1024 * 1024, 0, { hash1 });
// Insert first 3 votes in cache
vote_cache.vote (vote1->hashes.front (), vote1);
vote_cache.vote (vote2->hashes.front (), vote2);
vote_cache.vote (vote3->hashes.front (), vote3);
// Ensure all of those are properly inserted
ASSERT_EQ (3, vote_cache.cache_size ());
ASSERT_TRUE (vote_cache.find (hash1));
ASSERT_TRUE (vote_cache.find (hash2));
ASSERT_TRUE (vote_cache.find (hash3));

// Ensure that first entry in queue is the one for hash3 (rep3 has the highest weight of the first 3 reps)
auto peek1 = vote_cache.peek ();
ASSERT_TRUE (peek1);
ASSERT_EQ (peek1->voters.size (), 1);
ASSERT_EQ (peek1->tally, 11);
ASSERT_EQ (peek1->hash, hash3);

// Now add a vote from rep4 with the highest voting weight
vote_cache.vote (vote4->hashes.front (), vote4);

// Ensure that the first entry in queue is now the one for hash1 (rep1 + rep4 tally weight)
auto pop1 = vote_cache.pop ();
ASSERT_TRUE (pop1);
ASSERT_EQ ((*pop1).voters.size (), 2);
ASSERT_EQ ((*pop1).tally, 7 + 13);
ASSERT_EQ ((*pop1).hash, hash1);
ASSERT_TRUE (vote_cache.find (hash1)); // Only pop from queue, votes should still be stored in cache

// After popping the previous entry, the next entry in queue should be hash3 (rep3 tally weight)
auto pop2 = vote_cache.pop ();
ASSERT_EQ ((*pop2).voters.size (), 1);
ASSERT_EQ ((*pop2).tally, 11);
ASSERT_EQ ((*pop2).hash, hash3);
ASSERT_TRUE (vote_cache.find (hash3));

// And last one should be hash2 with rep2 tally weight
auto pop3 = vote_cache.pop ();
ASSERT_EQ ((*pop3).voters.size (), 1);
ASSERT_EQ ((*pop3).tally, 9);
Expand All @@ -155,6 +178,9 @@ TEST (vote_cache, insert_many_hashes_many_votes)
ASSERT_TRUE (vote_cache.queue_empty ());
}

/*
* Ensure that duplicate votes are ignored
*/
TEST (vote_cache, insert_duplicate)
{
nano::vote_cache vote_cache{ make_config () };
Expand All @@ -168,6 +194,9 @@ TEST (vote_cache, insert_duplicate)
ASSERT_EQ (1, vote_cache.cache_size ());
}

/*
* Ensure that when processing vote from a representative that is already cached, we always update to the vote with the highest timestamp
*/
TEST (vote_cache, insert_newer)
{
nano::vote_cache vote_cache{ make_config () };
Expand All @@ -184,10 +213,14 @@ TEST (vote_cache, insert_newer)
ASSERT_TRUE (peek2);
ASSERT_EQ (1, vote_cache.cache_size ());
ASSERT_EQ (1, peek2->voters.size ());
ASSERT_GT (peek2->voters.front ().second, peek1->voters.front ().second); // timestamp2 > timestamp1
// Second entry should have timestamp greater than the first one
ASSERT_GT (peek2->voters.front ().second, peek1->voters.front ().second);
ASSERT_EQ (peek2->voters.front ().second, std::numeric_limits<uint64_t>::max ()); // final timestamp
}

/*
* Ensure that when processing vote from a representative that is already cached, votes with older timestamp are ignored
*/
TEST (vote_cache, insert_older)
{
nano::vote_cache vote_cache{ make_config () };
Expand All @@ -207,6 +240,9 @@ TEST (vote_cache, insert_older)
ASSERT_EQ (peek2->voters.front ().second, peek1->voters.front ().second); // timestamp2 == timestamp1
}

/*
* Ensure that erase functionality works
*/
TEST (vote_cache, erase)
{
nano::vote_cache vote_cache{ make_config () };
Expand Down Expand Up @@ -241,13 +277,18 @@ TEST (vote_cache, erase)
ASSERT_TRUE (vote_cache.cache_empty ());
}

/*
* Ensure that when cache is overfilled, we remove the oldest entries first
*/
TEST (vote_cache, overfill)
{
// Create a vote cache with max size set to 1024
nano::vote_cache vote_cache{ make_config (1024) };
vote_cache.rep_weight_query = rep_weight_query ();
const int count = 16 * 1024;
for (int n = 0; n < count; ++n)
{
// The more recent the vote, the less voting weight it has
auto rep1 = create_rep (count - n);
auto hash1 = random_hash ();
auto vote1 = create_vote (rep1, 1024 * 1024, 0, { hash1 });
Expand All @@ -256,9 +297,13 @@ TEST (vote_cache, overfill)
ASSERT_LT (vote_cache.cache_size (), count);
auto peek1 = vote_cache.peek ();
ASSERT_TRUE (peek1);
ASSERT_EQ (peek1->tally, 1024); // Check that oldest votes are dropped first
// Check that oldest votes are dropped first
ASSERT_EQ (peek1->tally, 1024);
}

/*
* Check that when a single vote cache entry is overfilled, it ignores any new votes
*/
TEST (vote_cache, overfill_entry)
{
nano::vote_cache vote_cache{ make_config () };
Expand Down
4 changes: 4 additions & 0 deletions nano/node/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,10 @@ void nano::active_transactions::check_inactive_vote_cache (nano::block_hash cons
}
}

/*
* This is called when a new block is received from live network
* We check if maybe we already have enough inactive votes stored for it to start an election
*/
void nano::active_transactions::trigger_inactive_votes_cache_election (std::shared_ptr<nano::block> const & block_a)
{
check_inactive_vote_cache (block_a->hash ());
Expand Down

0 comments on commit 28eccf7

Please sign in to comment.