Inactive votes election hint (#2886)
* Add config field election_hint_weight_percent * Add class inactive_cache_status to hold confirmed/bootstrap_started and new election_started(new), simplifying some code * Start election on a threshold - missing check to not repeat * Split out bootstrap start function * Extra check for changes in inactive_cache_status * FIx gap cache bootstrap start * Inactive elections election start test with 5 representatives & genesis * Additional check to ensure block_get is done twice at most; function doc; rename status_a to previously_a * Formatting * Add path to start elections from existing inactive votes on block processing * Assert that the iterator is not invalidated, which would happen should we decide to erase from the inactive votes cache after inserting an election * A more obvious condition * Start impromptu elections passively * Remove unecessary node in test (Wes review) * Update test with checks on the assumptions * Rename check -> trigger (Colin review) * Revert "Split out bootstrap start function" This reverts commit 9113784a1031f9f8f50342710402053e33ac0805 and bf6eb0b48e5e3abe9a95f48accf97d6e4132804f * Remove election::transition_passive () calls as elections now start as passive implicitly Co-authored-by: Sergey Kroshnin <sergiysw@gmail.com>
This commit is contained in:
parent
0007250678
commit
6783671f85
8 changed files with 320 additions and 68 deletions
|
@ -646,6 +646,167 @@ TEST (active_transactions, inactive_votes_cache_multiple_votes)
|
|||
ASSERT_EQ (2, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_cached));
|
||||
}
|
||||
|
||||
TEST (active_transactions, inactive_votes_cache_election_start)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (nano::get_available_port (), system.logging);
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto & node = *system.add_node (node_config);
|
||||
nano::block_hash latest (node.latest (nano::dev_genesis_key.pub));
|
||||
nano::keypair key1, key2, key3, key4, key5;
|
||||
nano::send_block_builder send_block_builder;
|
||||
nano::state_block_builder state_block_builder;
|
||||
auto send1 = send_block_builder.make_block ()
|
||||
.previous (latest)
|
||||
.destination (key1.pub)
|
||||
.balance (nano::genesis_amount - 2000 * nano::Gxrb_ratio)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (latest))
|
||||
.build_shared ();
|
||||
auto send2 = send_block_builder.make_block ()
|
||||
.previous (send1->hash ())
|
||||
.destination (key2.pub)
|
||||
.balance (nano::genesis_amount - 4000 * nano::Gxrb_ratio)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build_shared ();
|
||||
auto send3 = send_block_builder.make_block ()
|
||||
.previous (send2->hash ())
|
||||
.destination (key3.pub)
|
||||
.balance (nano::genesis_amount - 6000 * nano::Gxrb_ratio)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send2->hash ()))
|
||||
.build_shared ();
|
||||
auto send4 = send_block_builder.make_block ()
|
||||
.previous (send3->hash ())
|
||||
.destination (key4.pub)
|
||||
.balance (nano::genesis_amount - 8000 * nano::Gxrb_ratio)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send3->hash ()))
|
||||
.build_shared ();
|
||||
auto send5 = send_block_builder.make_block ()
|
||||
.previous (send4->hash ())
|
||||
.destination (key5.pub)
|
||||
.balance (nano::genesis_amount - 10000 * nano::Gxrb_ratio)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send4->hash ()))
|
||||
.build_shared ();
|
||||
auto open1 = state_block_builder.make_block ()
|
||||
.account (key1.pub)
|
||||
.previous (0)
|
||||
.representative (key1.pub)
|
||||
.balance (2000 * nano::Gxrb_ratio)
|
||||
.link (send1->hash ())
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (key1.pub))
|
||||
.build_shared ();
|
||||
auto open2 = state_block_builder.make_block ()
|
||||
.account (key2.pub)
|
||||
.previous (0)
|
||||
.representative (key2.pub)
|
||||
.balance (2000 * nano::Gxrb_ratio)
|
||||
.link (send2->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (key2.pub))
|
||||
.build_shared ();
|
||||
auto open3 = state_block_builder.make_block ()
|
||||
.account (key3.pub)
|
||||
.previous (0)
|
||||
.representative (key3.pub)
|
||||
.balance (2000 * nano::Gxrb_ratio)
|
||||
.link (send3->hash ())
|
||||
.sign (key3.prv, key3.pub)
|
||||
.work (*system.work.generate (key3.pub))
|
||||
.build_shared ();
|
||||
auto open4 = state_block_builder.make_block ()
|
||||
.account (key4.pub)
|
||||
.previous (0)
|
||||
.representative (key4.pub)
|
||||
.balance (2000 * nano::Gxrb_ratio)
|
||||
.link (send4->hash ())
|
||||
.sign (key4.prv, key4.pub)
|
||||
.work (*system.work.generate (key4.pub))
|
||||
.build_shared ();
|
||||
auto open5 = state_block_builder.make_block ()
|
||||
.account (key5.pub)
|
||||
.previous (0)
|
||||
.representative (key5.pub)
|
||||
.balance (2000 * nano::Gxrb_ratio)
|
||||
.link (send5->hash ())
|
||||
.sign (key5.prv, key5.pub)
|
||||
.work (*system.work.generate (key5.pub))
|
||||
.build_shared ();
|
||||
node.block_processor.add (send1);
|
||||
node.block_processor.add (send2);
|
||||
node.block_processor.add (send3);
|
||||
node.block_processor.add (send4);
|
||||
node.block_processor.add (send5);
|
||||
node.block_processor.add (open1);
|
||||
node.block_processor.add (open2);
|
||||
node.block_processor.add (open3);
|
||||
node.block_processor.add (open4);
|
||||
node.block_processor.add (open5);
|
||||
node.block_processor.flush ();
|
||||
ASSERT_TIMELY (5s, 11 == node.ledger.cache.block_count);
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
ASSERT_EQ (1, node.ledger.cache.cemented_count);
|
||||
// These blocks will be processed later
|
||||
auto send6 = send_block_builder.make_block ()
|
||||
.previous (send5->hash ())
|
||||
.destination (nano::keypair ().pub)
|
||||
.balance (send5->balance ().number () - 1)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send5->hash ()))
|
||||
.build_shared ();
|
||||
auto send7 = send_block_builder.make_block ()
|
||||
.previous (send6->hash ())
|
||||
.destination (nano::keypair ().pub)
|
||||
.balance (send6->balance ().number () - 1)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send6->hash ()))
|
||||
.build_shared ();
|
||||
// Inactive votes
|
||||
std::vector<nano::block_hash> hashes{ open1->hash (), open2->hash (), open3->hash (), open4->hash (), open5->hash (), send7->hash () };
|
||||
auto vote1 (std::make_shared<nano::vote> (key1.pub, key1.prv, 0, hashes));
|
||||
node.vote_processor.vote (vote1, std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
|
||||
auto vote2 (std::make_shared<nano::vote> (key2.pub, key2.prv, 0, hashes));
|
||||
node.vote_processor.vote (vote2, std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
|
||||
auto vote3 (std::make_shared<nano::vote> (key3.pub, key3.prv, 0, hashes));
|
||||
node.vote_processor.vote (vote3, std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
|
||||
auto vote4 (std::make_shared<nano::vote> (key4.pub, key4.prv, 0, hashes));
|
||||
node.vote_processor.vote (vote4, std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
|
||||
ASSERT_TIMELY (5s, node.active.inactive_votes_cache_size () == 6);
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
ASSERT_EQ (1, node.ledger.cache.cemented_count);
|
||||
// 5 votes are required to start election
|
||||
auto vote5 (std::make_shared<nano::vote> (key5.pub, key5.prv, 0, hashes));
|
||||
node.vote_processor.vote (vote5, std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
|
||||
ASSERT_TIMELY (5s, 5 == node.active.size ());
|
||||
// Confirm elections with weight quorum
|
||||
auto vote0 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 0, hashes));
|
||||
node.vote_processor.vote (vote0, std::make_shared<nano::transport::channel_udp> (node.network.udp_channels, node.network.endpoint (), node.network_params.protocol.protocol_version));
|
||||
ASSERT_TIMELY (5s, node.active.empty ());
|
||||
ASSERT_TIMELY (5s, 11 == node.ledger.cache.cemented_count);
|
||||
// A late block arrival also checks the inactive votes cache
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
auto send7_cache (node.active.find_inactive_votes_cache (send7->hash ()));
|
||||
ASSERT_EQ (6, send7_cache.voters.size ());
|
||||
ASSERT_TRUE (send7_cache.status.bootstrap_started);
|
||||
ASSERT_TRUE (send7_cache.status.confirmed);
|
||||
ASSERT_TRUE (send7_cache.status.election_started); // already marked even though the block does not exist
|
||||
node.process_active (send6);
|
||||
node.block_processor.flush ();
|
||||
// An election is started for send6 but does not confirm
|
||||
ASSERT_TIMELY (5s, 1 == node.active.size ());
|
||||
node.vote_processor.flush ();
|
||||
ASSERT_FALSE (node.block_confirmed_or_being_confirmed (node.store.tx_begin_read (), send6->hash ()));
|
||||
// send7 cannot be voted on but an election should be started from inactive votes
|
||||
ASSERT_FALSE (node.ledger.can_vote (node.store.tx_begin_read (), *send7));
|
||||
node.process_active (send7);
|
||||
node.block_processor.flush ();
|
||||
ASSERT_TIMELY (5s, 13 == node.ledger.cache.cemented_count);
|
||||
}
|
||||
|
||||
TEST (active_transactions, update_difficulty)
|
||||
{
|
||||
nano::system system (2);
|
||||
|
|
|
@ -169,6 +169,7 @@ TEST (toml, daemon_config_deserialize_defaults)
|
|||
ASSERT_EQ (conf.node.work_watcher_period, defaults.node.work_watcher_period);
|
||||
ASSERT_EQ (conf.node.online_weight_minimum, defaults.node.online_weight_minimum);
|
||||
ASSERT_EQ (conf.node.online_weight_quorum, defaults.node.online_weight_quorum);
|
||||
ASSERT_EQ (conf.node.election_hint_weight_percent, defaults.node.election_hint_weight_percent);
|
||||
ASSERT_EQ (conf.node.password_fanout, defaults.node.password_fanout);
|
||||
ASSERT_EQ (conf.node.peering_port, defaults.node.peering_port);
|
||||
ASSERT_EQ (conf.node.pow_sleep_interval, defaults.node.pow_sleep_interval);
|
||||
|
@ -404,6 +405,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
network_threads = 999
|
||||
online_weight_minimum = "999"
|
||||
online_weight_quorum = 99
|
||||
election_hint_weight_percent = 19
|
||||
password_fanout = 999
|
||||
peering_port = 999
|
||||
pow_sleep_interval= 999
|
||||
|
@ -568,6 +570,7 @@ TEST (toml, daemon_config_deserialize_no_defaults)
|
|||
ASSERT_NE (conf.node.work_watcher_period, defaults.node.work_watcher_period);
|
||||
ASSERT_NE (conf.node.online_weight_minimum, defaults.node.online_weight_minimum);
|
||||
ASSERT_NE (conf.node.online_weight_quorum, defaults.node.online_weight_quorum);
|
||||
ASSERT_NE (conf.node.election_hint_weight_percent, defaults.node.election_hint_weight_percent);
|
||||
ASSERT_NE (conf.node.password_fanout, defaults.node.password_fanout);
|
||||
ASSERT_NE (conf.node.peering_port, defaults.node.peering_port);
|
||||
ASSERT_NE (conf.node.pow_sleep_interval, defaults.node.pow_sleep_interval);
|
||||
|
@ -757,32 +760,66 @@ TEST (toml, rpc_config_no_required)
|
|||
/** Deserialize a node config with incorrect values */
|
||||
TEST (toml, daemon_config_deserialize_errors)
|
||||
{
|
||||
std::stringstream ss_max_work_generate_multiplier;
|
||||
ss_max_work_generate_multiplier << R"toml(
|
||||
[node]
|
||||
max_work_generate_multiplier = 0.9
|
||||
)toml";
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
[node]
|
||||
max_work_generate_multiplier = 0.9
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig toml;
|
||||
toml.read (ss_max_work_generate_multiplier);
|
||||
nano::daemon_config conf;
|
||||
conf.deserialize_toml (toml);
|
||||
nano::tomlconfig toml;
|
||||
toml.read (ss);
|
||||
nano::daemon_config conf;
|
||||
conf.deserialize_toml (toml);
|
||||
|
||||
ASSERT_EQ (toml.get_error ().get_message (), "max_work_generate_multiplier must be greater than or equal to 1");
|
||||
ASSERT_EQ (toml.get_error ().get_message (), "max_work_generate_multiplier must be greater than or equal to 1");
|
||||
}
|
||||
|
||||
std::stringstream ss_frontiers_confirmation;
|
||||
ss_frontiers_confirmation << R"toml(
|
||||
[node]
|
||||
frontiers_confirmation = "randomstring"
|
||||
)toml";
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
[node]
|
||||
frontiers_confirmation = "randomstring"
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig toml2;
|
||||
toml2.read (ss_frontiers_confirmation);
|
||||
nano::daemon_config conf2;
|
||||
conf2.deserialize_toml (toml2);
|
||||
nano::tomlconfig toml;
|
||||
toml.read (ss);
|
||||
nano::daemon_config conf;
|
||||
conf.deserialize_toml (toml);
|
||||
|
||||
ASSERT_EQ (toml2.get_error ().get_message (), "frontiers_confirmation value is invalid (available: always, auto, disabled)");
|
||||
ASSERT_EQ (conf2.node.frontiers_confirmation, nano::frontiers_confirmation_mode::invalid);
|
||||
ASSERT_EQ (toml.get_error ().get_message (), "frontiers_confirmation value is invalid (available: always, auto, disabled)");
|
||||
ASSERT_EQ (conf.node.frontiers_confirmation, nano::frontiers_confirmation_mode::invalid);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
[node]
|
||||
election_hint_weight_percent = 4
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig toml;
|
||||
toml.read (ss);
|
||||
nano::daemon_config conf;
|
||||
conf.deserialize_toml (toml);
|
||||
|
||||
ASSERT_EQ (toml.get_error ().get_message (), "election_hint_weight_percent must be a number between 5 and 50");
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
[node]
|
||||
election_hint_weight_percent = 51
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig toml;
|
||||
toml.read (ss);
|
||||
nano::daemon_config conf;
|
||||
conf.deserialize_toml (toml);
|
||||
|
||||
ASSERT_EQ (toml.get_error ().get_message (), "election_hint_weight_percent must be a number between 5 and 50");
|
||||
}
|
||||
}
|
||||
|
||||
TEST (toml, daemon_read_config)
|
||||
|
|
|
@ -1234,7 +1234,7 @@ void nano::active_transactions::add_inactive_votes_cache (nano::block_hash const
|
|||
auto existing (inactive_by_hash.find (hash_a));
|
||||
if (existing != inactive_by_hash.end ())
|
||||
{
|
||||
if (!existing->confirmed || !existing->bootstrap_started)
|
||||
if (existing->needs_eval ())
|
||||
{
|
||||
auto is_new (false);
|
||||
inactive_by_hash.modify (existing, [representative_a, &is_new](nano::inactive_cache_information & info) {
|
||||
|
@ -1249,17 +1249,13 @@ void nano::active_transactions::add_inactive_votes_cache (nano::block_hash const
|
|||
|
||||
if (is_new)
|
||||
{
|
||||
bool confirmed (false);
|
||||
if (inactive_votes_bootstrap_check (existing->voters, hash_a, confirmed) && !existing->bootstrap_started)
|
||||
auto const status = inactive_votes_bootstrap_check (existing->voters, hash_a, existing->status);
|
||||
if (status != existing->status)
|
||||
{
|
||||
inactive_by_hash.modify (existing, [](nano::inactive_cache_information & info) {
|
||||
info.bootstrap_started = true;
|
||||
});
|
||||
}
|
||||
if (confirmed && !existing->confirmed)
|
||||
{
|
||||
inactive_by_hash.modify (existing, [](nano::inactive_cache_information & info) {
|
||||
info.confirmed = true;
|
||||
// The iterator is only valid if the container was unchanged, e.g., by erasing this item after inserting an election
|
||||
debug_assert (inactive_by_hash.count (hash_a));
|
||||
inactive_by_hash.modify (existing, [status](nano::inactive_cache_information & info) {
|
||||
info.status = status;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1267,11 +1263,10 @@ void nano::active_transactions::add_inactive_votes_cache (nano::block_hash const
|
|||
}
|
||||
else
|
||||
{
|
||||
std::vector<nano::account> representative_vector (1, representative_a);
|
||||
bool confirmed (false);
|
||||
bool start_bootstrap (inactive_votes_bootstrap_check (representative_vector, hash_a, confirmed));
|
||||
std::vector<nano::account> representative_vector{ representative_a };
|
||||
auto const status (inactive_votes_bootstrap_check (representative_vector, hash_a, {}));
|
||||
auto & inactive_by_arrival (inactive_votes_cache.get<tag_arrival> ());
|
||||
inactive_by_arrival.emplace (nano::inactive_cache_information{ std::chrono::steady_clock::now (), hash_a, representative_vector, start_bootstrap, confirmed });
|
||||
inactive_by_arrival.emplace (nano::inactive_cache_information{ std::chrono::steady_clock::now (), hash_a, representative_vector, status });
|
||||
if (inactive_votes_cache.size () > node.flags.inactive_votes_cache_size)
|
||||
{
|
||||
inactive_by_arrival.erase (inactive_by_arrival.begin ());
|
||||
|
@ -1280,6 +1275,16 @@ void nano::active_transactions::add_inactive_votes_cache (nano::block_hash const
|
|||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::trigger_inactive_votes_cache_election (std::shared_ptr<nano::block> const & block_a)
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (mutex);
|
||||
auto const status = find_inactive_votes_cache (block_a->hash ()).status;
|
||||
if (status.election_started)
|
||||
{
|
||||
insert_impl (block_a);
|
||||
}
|
||||
}
|
||||
|
||||
nano::inactive_cache_information nano::active_transactions::find_inactive_votes_cache (nano::block_hash const & hash_a)
|
||||
{
|
||||
auto & inactive_by_hash (inactive_votes_cache.get<tag_hash> ());
|
||||
|
@ -1299,46 +1304,66 @@ void nano::active_transactions::erase_inactive_votes_cache (nano::block_hash con
|
|||
inactive_votes_cache.get<tag_hash> ().erase (hash_a);
|
||||
}
|
||||
|
||||
bool nano::active_transactions::inactive_votes_bootstrap_check (std::vector<nano::account> const & voters_a, nano::block_hash const & hash_a, bool & confirmed_a)
|
||||
nano::inactive_cache_status nano::active_transactions::inactive_votes_bootstrap_check (std::vector<nano::account> const & voters_a, nano::block_hash const & hash_a, nano::inactive_cache_status const & previously_a)
|
||||
{
|
||||
/** Perform checks on accumulated tally from inactive votes
|
||||
* These votes are generally either for unconfirmed blocks or old confirmed blocks
|
||||
* That check is made after hitting a tally threshold, and always as late and as few times as possible
|
||||
*/
|
||||
nano::inactive_cache_status status (previously_a);
|
||||
constexpr unsigned election_start_voters_min{ 5 };
|
||||
uint128_t tally;
|
||||
for (auto const & voter : voters_a)
|
||||
{
|
||||
tally += node.ledger.weight (voter);
|
||||
}
|
||||
bool start_bootstrap (false);
|
||||
if (tally >= node.config.online_weight_minimum.number ())
|
||||
|
||||
if (!previously_a.confirmed && tally >= node.config.online_weight_minimum.number ())
|
||||
{
|
||||
start_bootstrap = true;
|
||||
confirmed_a = true;
|
||||
status.bootstrap_started = true;
|
||||
status.confirmed = true;
|
||||
}
|
||||
else if (!node.flags.disable_legacy_bootstrap && tally > node.gap_cache.bootstrap_threshold ())
|
||||
else if (!previously_a.bootstrap_started && !node.flags.disable_legacy_bootstrap && node.flags.disable_lazy_bootstrap && tally > node.gap_cache.bootstrap_threshold ())
|
||||
{
|
||||
start_bootstrap = true;
|
||||
status.bootstrap_started = true;
|
||||
}
|
||||
if (start_bootstrap && !node.ledger.block_exists (hash_a))
|
||||
if (!previously_a.election_started && voters_a.size () >= election_start_voters_min && tally >= (node.online_reps.online_stake () / 100) * node.config.election_hint_weight_percent)
|
||||
{
|
||||
auto node_l (node.shared ());
|
||||
node.alarm.add (std::chrono::steady_clock::now () + node.network_params.bootstrap.gap_cache_bootstrap_start_interval, [node_l, hash_a]() {
|
||||
auto transaction (node_l->store.tx_begin_read ());
|
||||
if (!node_l->store.block_exists (transaction, hash_a))
|
||||
{
|
||||
if (!node_l->bootstrap_initiator.in_progress ())
|
||||
{
|
||||
node_l->logger.try_log (boost::str (boost::format ("Missing block %1% which has enough votes to warrant lazy bootstrapping it") % hash_a.to_string ()));
|
||||
}
|
||||
if (!node_l->flags.disable_lazy_bootstrap)
|
||||
{
|
||||
node_l->bootstrap_initiator.bootstrap_lazy (hash_a);
|
||||
}
|
||||
else if (!node_l->flags.disable_legacy_bootstrap)
|
||||
{
|
||||
node_l->bootstrap_initiator.bootstrap ();
|
||||
}
|
||||
}
|
||||
});
|
||||
status.election_started = true;
|
||||
}
|
||||
return start_bootstrap;
|
||||
|
||||
if ((status.election_started && !previously_a.election_started) || (status.bootstrap_started && !previously_a.bootstrap_started))
|
||||
{
|
||||
auto transaction (node.store.tx_begin_read ());
|
||||
auto block = node.store.block_get (transaction, hash_a);
|
||||
if (block && status.election_started && !previously_a.election_started && !node.block_confirmed_or_being_confirmed (transaction, hash_a))
|
||||
{
|
||||
insert_impl (block);
|
||||
}
|
||||
else if (!block && status.bootstrap_started && !previously_a.bootstrap_started)
|
||||
{
|
||||
auto node_l (node.shared ());
|
||||
node.alarm.add (std::chrono::steady_clock::now () + node.network_params.bootstrap.gap_cache_bootstrap_start_interval, [node_l, hash_a]() {
|
||||
auto transaction (node_l->store.tx_begin_read ());
|
||||
if (!node_l->store.block_exists (transaction, hash_a))
|
||||
{
|
||||
if (!node_l->bootstrap_initiator.in_progress ())
|
||||
{
|
||||
node_l->logger.try_log (boost::str (boost::format ("Missing block %1% which has enough votes to warrant lazy bootstrapping it") % hash_a.to_string ()));
|
||||
}
|
||||
if (!node_l->flags.disable_lazy_bootstrap)
|
||||
{
|
||||
node_l->bootstrap_initiator.bootstrap_lazy (hash_a);
|
||||
}
|
||||
else if (!node_l->flags.disable_legacy_bootstrap)
|
||||
{
|
||||
node_l->bootstrap_initiator.bootstrap ();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t nano::active_transactions::election_winner_details_size ()
|
||||
|
|
|
@ -48,14 +48,30 @@ public:
|
|||
nano::qualified_root root;
|
||||
};
|
||||
|
||||
class inactive_cache_status final
|
||||
{
|
||||
public:
|
||||
bool bootstrap_started{ false };
|
||||
bool election_started{ false }; // Did item reach config threshold to start an impromptu election?
|
||||
bool confirmed{ false }; // Did item reach votes quorum? (minimum config value)
|
||||
|
||||
bool operator!= (inactive_cache_status const other) const
|
||||
{
|
||||
return bootstrap_started != other.bootstrap_started || election_started != other.election_started || confirmed != other.confirmed;
|
||||
}
|
||||
};
|
||||
|
||||
class inactive_cache_information final
|
||||
{
|
||||
public:
|
||||
std::chrono::steady_clock::time_point arrival;
|
||||
nano::block_hash hash;
|
||||
std::vector<nano::account> voters;
|
||||
bool bootstrap_started{ false };
|
||||
bool confirmed{ false }; // Did item reach votes quorum? (minimum config value)
|
||||
nano::inactive_cache_status status;
|
||||
bool needs_eval () const
|
||||
{
|
||||
return !status.bootstrap_started || !status.election_started || !status.confirmed;
|
||||
}
|
||||
};
|
||||
|
||||
class dropped_elections final
|
||||
|
@ -178,6 +194,8 @@ public:
|
|||
void add_recently_cemented (nano::election_status const &);
|
||||
void add_recently_confirmed (nano::qualified_root const &, nano::block_hash const &);
|
||||
void add_inactive_votes_cache (nano::block_hash const &, nano::account const &);
|
||||
// Inserts an election if conditions are met
|
||||
void trigger_inactive_votes_cache_election (std::shared_ptr<nano::block> const &);
|
||||
nano::inactive_cache_information find_inactive_votes_cache (nano::block_hash const &);
|
||||
void erase_inactive_votes_cache (nano::block_hash const &);
|
||||
nano::confirmation_height_processor & confirmation_height_processor;
|
||||
|
@ -265,7 +283,7 @@ private:
|
|||
mi::member<nano::inactive_cache_information, nano::block_hash, &nano::inactive_cache_information::hash>>>>;
|
||||
ordered_cache inactive_votes_cache;
|
||||
// clang-format on
|
||||
bool inactive_votes_bootstrap_check (std::vector<nano::account> const &, nano::block_hash const &, bool &);
|
||||
nano::inactive_cache_status inactive_votes_bootstrap_check (std::vector<nano::account> const &, nano::block_hash const &, nano::inactive_cache_status const &);
|
||||
boost::thread thread;
|
||||
|
||||
friend class election;
|
||||
|
|
|
@ -299,6 +299,10 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std::
|
|||
{
|
||||
node.active.insert (block_a, process_return_a.previous_balance.number ());
|
||||
}
|
||||
else
|
||||
{
|
||||
node.active.trigger_inactive_votes_cache_election (block_a);
|
||||
}
|
||||
|
||||
// Announce block contents to the network
|
||||
if (origin_a == nano::block_origin::local)
|
||||
|
|
|
@ -450,7 +450,7 @@ bool nano::bootstrap_attempt_legacy::confirm_frontiers (nano::unique_lock<std::m
|
|||
{
|
||||
tally += node->ledger.weight (voter);
|
||||
}
|
||||
if (existing.confirmed || (tally > reps_weight / 8 && existing.voters.size () >= representatives.size () * 0.6)) // 12.5% of weight, 60% of reps
|
||||
if (existing.status.confirmed || (tally > reps_weight / 8 && existing.voters.size () >= representatives.size () * 0.6)) // 12.5% of weight, 60% of reps
|
||||
{
|
||||
ii = frontiers.erase (ii);
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const
|
|||
toml.put ("receive_minimum", receive_minimum.to_string_dec (), "Minimum receive amount. Only affects node wallets. A large amount is recommended to avoid automatic work generation for tiny transactions.\ntype:string,amount,raw");
|
||||
toml.put ("online_weight_minimum", online_weight_minimum.to_string_dec (), "Online weight minimum required to confirm a block.\ntype:string,amount,raw");
|
||||
toml.put ("online_weight_quorum", online_weight_quorum, "Percentage of votes required to confirm blocks. A value below 50 is not recommended.\ntype:uint64");
|
||||
toml.put ("election_hint_weight_percent", election_hint_weight_percent, "Percentage of online weight to hint at starting an election. Defaults to 10.\ntype:uint32,[5,50]");
|
||||
toml.put ("password_fanout", password_fanout, "Password fanout factor.\ntype:uint64");
|
||||
toml.put ("io_threads", io_threads, "Number of threads dedicated to I/O operations. Defaults to the number of CPU threads, and at least 4.\ntype:uint64");
|
||||
toml.put ("network_threads", network_threads, "Number of threads dedicated to processing network messages. Defaults to the number of CPU threads, and at least 4.\ntype:uint64");
|
||||
|
@ -308,6 +309,7 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml)
|
|||
toml.get<uint16_t> ("peering_port", peering_port);
|
||||
toml.get<unsigned> ("bootstrap_fraction_numerator", bootstrap_fraction_numerator);
|
||||
toml.get<unsigned> ("online_weight_quorum", online_weight_quorum);
|
||||
toml.get<unsigned> ("election_hint_weight_percent", election_hint_weight_percent);
|
||||
toml.get<unsigned> ("password_fanout", password_fanout);
|
||||
toml.get<unsigned> ("io_threads", io_threads);
|
||||
toml.get<unsigned> ("work_threads", work_threads);
|
||||
|
@ -395,6 +397,10 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml)
|
|||
{
|
||||
toml.get_error ().set ("online_weight_quorum must be less than 100");
|
||||
}
|
||||
if (election_hint_weight_percent < 5 || election_hint_weight_percent > 50)
|
||||
{
|
||||
toml.get_error ().set ("election_hint_weight_percent must be a number between 5 and 50");
|
||||
}
|
||||
if (password_fanout < 16 || password_fanout > 1024 * 1024)
|
||||
{
|
||||
toml.get_error ().set ("password_fanout must be a number between 16 and 1048576");
|
||||
|
|
|
@ -56,6 +56,7 @@ public:
|
|||
unsigned vote_generator_threshold{ 3 };
|
||||
nano::amount online_weight_minimum{ 60000 * nano::Gxrb_ratio };
|
||||
unsigned online_weight_quorum{ 50 };
|
||||
unsigned election_hint_weight_percent{ 10 };
|
||||
unsigned password_fanout{ 1024 };
|
||||
unsigned io_threads{ std::max<unsigned> (4, std::thread::hardware_concurrency ()) };
|
||||
unsigned network_threads{ std::max<unsigned> (4, std::thread::hardware_concurrency ()) };
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue