Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vote spacing #2763

Merged
merged 18 commits into from
Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
637ecd9
Currently there is no rate limiter on vote generation for a particula…
clemahieu Nov 18, 2020
41a6932
Merge branch 'develop' into vote_spacing_new
clemahieu Nov 19, 2020
38ac88d
Removing check on hash since we won't produce a new vote if there's a…
clemahieu Nov 19, 2020
fae170c
Moving delay in to voting_constants and referencing voting_constants …
clemahieu Nov 20, 2020
72684e0
Erasing duplicate roots.
clemahieu Dec 3, 2020
a278c99
Adding test for correct broadcast deduplication.
clemahieu Dec 3, 2020
9890305
Retaining historical blocks until election completion. history::votes…
clemahieu Dec 15, 2020
e83ba43
Moving vote spacing to its own data structure.
clemahieu Dec 15, 2020
709dde2
Testing vote_spacing class and adding trimming.
clemahieu Dec 15, 2020
ef5d5a7
Revert "Retaining historical blocks until election completion. histor…
Dec 16, 2020
b5e65a1
Merge branch 'develop' into vote_spacing_new
clemahieu Dec 16, 2020
53eb86f
Removing duplicate roots from the same vote and adjusting tests to ac…
clemahieu Dec 17, 2020
1a88e19
Merge branch 'remove_duplicate_root' into vote_spacing_new
clemahieu Dec 17, 2020
9b0704b
Only block voting if the hash is different.
clemahieu Dec 17, 2020
46c3330
Merge branch 'develop' into vote_spacing_new
clemahieu Dec 17, 2020
d727ec3
Cleaning up unneeded test friend classes.
clemahieu Jan 11, 2021
f4d7df7
Ensure vote has gone out before rolling back, otherwise the vote migh…
clemahieu Jan 11, 2021
c8c497f
Merge branch 'develop' into vote_spacing_new
clemahieu Jan 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions nano/core_test/voting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,119 @@ TEST (vote_generator, session)
thread.join ();
ASSERT_TIMELY (2s, 1 == node->stats.count (nano::stat::type::vote, nano::stat::detail::vote_indeterminate));
}

TEST (vote_spacing, basic)
{
nano::vote_spacing spacing{ std::chrono::milliseconds{ 100 } };
nano::root root1{ 1 };
nano::root root2{ 2 };
nano::block_hash hash3{ 3 };
nano::block_hash hash4{ 4 };
nano::block_hash hash5{ 5 };
ASSERT_EQ (0, spacing.size ());
ASSERT_TRUE (spacing.votable (root1, hash3));
spacing.flag (root1, hash3);
ASSERT_EQ (1, spacing.size ());
ASSERT_TRUE (spacing.votable (root1, hash3));
ASSERT_FALSE (spacing.votable (root1, hash4));
spacing.flag (root2, hash5);
ASSERT_EQ (2, spacing.size ());
}

TEST (vote_spacing, prune)
{
auto length = std::chrono::milliseconds{ 100 };
nano::vote_spacing spacing{ length };
nano::root root1{ 1 };
nano::root root2{ 2 };
nano::block_hash hash3{ 3 };
nano::block_hash hash4{ 4 };
spacing.flag (root1, hash3);
ASSERT_EQ (1, spacing.size ());
std::this_thread::sleep_for (length);
spacing.flag (root2, hash4);
ASSERT_EQ (1, spacing.size ());
}

namespace nano
{
TEST (vote_spacing, vote_generator)
{
nano::node_config config;
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
nano::system system;
auto & node = *system.add_node (config);
auto & wallet = *system.wallet (0);
wallet.insert_adhoc (nano::dev_genesis_key.prv);
nano::state_block_builder builder;
auto send1 = builder.make_block ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (nano::genesis_amount - nano::Gxrb_ratio)
.link (nano::dev_genesis_key.pub)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.work (*system.work.generate (nano::genesis_hash))
.build_shared ();
auto send2 = builder.make_block ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (nano::genesis_amount - nano::Gxrb_ratio - 1)
.link (nano::dev_genesis_key.pub)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.work (*system.work.generate (nano::genesis_hash))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, node.ledger.process (node.store.tx_begin_write (), *send1).code);
ASSERT_EQ (0, node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts));
node.active.generator.add (nano::genesis_hash, send1->hash ());
ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts) == 1);
ASSERT_FALSE (node.ledger.rollback (node.store.tx_begin_write (), send1->hash ()));
ASSERT_EQ (nano::process_result::progress, node.ledger.process (node.store.tx_begin_write (), *send2).code);
node.active.generator.add (nano::genesis_hash, send2->hash ());
ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_spacing) == 1);
ASSERT_EQ (1, node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts));
std::this_thread::sleep_for (config.network_params.voting.delay);
node.active.generator.add (nano::genesis_hash, send2->hash ());
ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts) == 2);
}

TEST (vote_spacing, rapid)
{
nano::node_config config;
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
nano::system system;
auto & node = *system.add_node (config);
auto & wallet = *system.wallet (0);
wallet.insert_adhoc (nano::dev_genesis_key.prv);
nano::state_block_builder builder;
auto send1 = builder.make_block ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (nano::genesis_amount - nano::Gxrb_ratio)
.link (nano::dev_genesis_key.pub)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.work (*system.work.generate (nano::genesis_hash))
.build_shared ();
auto send2 = builder.make_block ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (nano::genesis_amount - nano::Gxrb_ratio - 1)
.link (nano::dev_genesis_key.pub)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.work (*system.work.generate (nano::genesis_hash))
.build_shared ();
ASSERT_EQ (nano::process_result::progress, node.ledger.process (node.store.tx_begin_write (), *send1).code);
node.active.generator.add (nano::genesis_hash, send1->hash ());
ASSERT_FALSE (node.ledger.rollback (node.store.tx_begin_write (), send1->hash ()));
ASSERT_EQ (nano::process_result::progress, node.ledger.process (node.store.tx_begin_write (), *send2).code);
node.active.generator.add (nano::genesis_hash, send2->hash ());
ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_spacing) == 1);
ASSERT_TIMELY (3s, 1 == node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts));
std::this_thread::sleep_for (config.network_params.voting.delay);
node.active.generator.add (nano::genesis_hash, send2->hash ());
ASSERT_TIMELY (3s, node.stats.count (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts) == 2);
}
}
3 changes: 3 additions & 0 deletions nano/lib/stats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,9 @@ std::string nano::stat::detail_to_string (uint32_t key)
case nano::stat::detail::generator_replies_discarded:
res = "generator_replies_discarded";
break;
case nano::stat::detail::generator_spacing:
res = "generator_spacing";
break;
}
return res;
}
Expand Down
3 changes: 2 additions & 1 deletion nano/lib/stats.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,8 @@ class stat final
// vote generator
generator_broadcasts,
generator_replies,
generator_replies_discarded
generator_replies_discarded,
generator_spacing
};

/** Direction of the stat. If the direction is irrelevant, use in */
Expand Down
68 changes: 62 additions & 6 deletions nano/node/voting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,44 @@

#include <chrono>

void nano::vote_spacing::trim ()
{
recent.get<tag_time> ().erase (recent.get<tag_time> ().begin (), recent.get<tag_time> ().upper_bound (std::chrono::steady_clock::now () - delay));
}

bool nano::vote_spacing::votable (nano::root const & root_a, nano::block_hash const & hash_a) const
{
bool result = true;
for (auto range = recent.get<tag_root> ().equal_range (root_a); result && range.first != range.second; ++range.first)
{
auto & item = *range.first;
result = hash_a == item.hash || item.time < std::chrono::steady_clock::now () - delay;
}
return result;
}

void nano::vote_spacing::flag (nano::root const & root_a, nano::block_hash const & hash_a)
{
trim ();
auto now = std::chrono::steady_clock::now ();
auto existing = recent.get<tag_root> ().find (root_a);
if (existing != recent.end ())
{
recent.get<tag_root> ().modify (existing, [now] (entry & entry) {
entry.time = now;
});
}
else
{
recent.insert ({ root_a, now, hash_a });
}
}

size_t nano::vote_spacing::size () const
{
return recent.size ();
}

bool nano::local_vote_history::consistency_check (nano::root const & root_a) const
{
auto & history_by_root (history.get<tag_root> ());
Expand Down Expand Up @@ -117,6 +155,7 @@ ledger (ledger_a),
wallets (wallets_a),
vote_processor (vote_processor_a),
history (history_a),
spacing{ config_a.network_params.voting.delay },
network (network_a),
stats (stats_a),
thread ([this]() { run (); })
Expand Down Expand Up @@ -218,18 +257,27 @@ void nano::vote_generator::broadcast (nano::unique_lock<std::mutex> & lock_a)
}
if (cached_votes.empty () && std::find (roots.begin (), roots.end (), root) == roots.end ())
{
roots.push_back (root);
hashes.push_back (hash);
if (spacing.votable (root, hash))
{
roots.push_back (root);
hashes.push_back (hash);
}
else
{
stats.inc (nano::stat::type::vote_generator, nano::stat::detail::generator_spacing);
}
}
candidates.pop_front ();
}
if (!hashes.empty ())
{
lock_a.unlock ();
vote (hashes, roots, [this](auto const & vote_a) { this->broadcast_action (vote_a); });
vote (hashes, roots, [this](auto const & vote_a) {
this->broadcast_action (vote_a);
this->stats.inc (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts);
});
lock_a.lock ();
}
stats.inc (nano::stat::type::vote_generator, nano::stat::detail::generator_broadcasts);
}

void nano::vote_generator::reply (nano::unique_lock<std::mutex> & lock_a, request_t && request_a)
Expand Down Expand Up @@ -260,8 +308,15 @@ void nano::vote_generator::reply (nano::unique_lock<std::mutex> & lock_a, reques
}
if (cached_votes.empty () && std::find (roots.begin (), roots.end (), root) == roots.end ())
{
roots.push_back (i->first);
hashes.push_back (i->second);
if (spacing.votable (root, hash))
{
roots.push_back (root);
hashes.push_back (hash);
}
else
{
stats.inc (nano::stat::type::vote_generator, nano::stat::detail::generator_spacing);
}
}
}
if (!hashes.empty ())
Expand Down Expand Up @@ -289,6 +344,7 @@ void nano::vote_generator::vote (std::vector<nano::block_hash> const & hashes_a,
for (size_t i (0), n (hashes_a.size ()); i != n; ++i)
{
history.add (roots_a[i], hashes_a[i], vote_l);
spacing.flag (roots_a[i], hashes_a[i]);
}
action_a (vote_l);
}
Expand Down
33 changes: 32 additions & 1 deletion nano/node/voting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <nano/secure/common.hpp>

#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/sequenced_index.hpp>
#include <boost/multi_index_container.hpp>
Expand All @@ -31,6 +32,34 @@ namespace transport
class channel;
}

class vote_spacing final
{
class entry
{
public:
nano::root root;
std::chrono::steady_clock::time_point time;
nano::block_hash hash;
};

boost::multi_index_container<entry,
mi::indexed_by<
mi::hashed_non_unique<mi::tag<class tag_root>,
mi::member<entry, nano::root, &entry::root>>,
mi::ordered_non_unique<mi::tag<class tag_time>,
mi::member<entry, std::chrono::steady_clock::time_point, &entry::time>>
>>
recent;
std::chrono::milliseconds const delay;
void trim ();
public:
vote_spacing (std::chrono::milliseconds const & delay) :
delay{ delay } {}
bool votable (nano::root const & root_a, nano::block_hash const & hash_a) const;
void flag (nano::root const & root_a, nano::block_hash const & hash_a);
size_t size () const;
};

class local_vote_history final
{
class local_vote final
Expand Down Expand Up @@ -77,7 +106,6 @@ class local_vote_history final
mutable std::mutex mutex;

friend std::unique_ptr<container_info_component> collect_container_info (local_vote_history & history, const std::string & name);

friend class local_vote_history_basic_Test;
};

Expand Down Expand Up @@ -110,6 +138,7 @@ class vote_generator final
nano::wallets & wallets;
nano::vote_processor & vote_processor;
nano::local_vote_history & history;
nano::vote_spacing spacing;
nano::network & network;
nano::stat & stats;
mutable std::mutex mutex;
Expand All @@ -122,6 +151,8 @@ class vote_generator final
bool started{ false };
std::thread thread;

friend class vote_generator_vote_spacing_Test;
friend class vote_generator_vote_rapid_Test;
clemahieu marked this conversation as resolved.
Show resolved Hide resolved
friend std::unique_ptr<container_info_component> collect_container_info (vote_generator & vote_generator, const std::string & name);
};

Expand Down