Confirm delta (#2884)

* Rewrite tests in terms of state blocks.

* online_weight_minimum is already considered in the calculation of node::delta. Using online_weight_minimum directly can cause quorum requirements to be unnecessarily high as the online_weight_minimum is approached.

Replacing references to online_weight_minimum with node:delta and adding tests to ensure rollbacks and confirmation is done precisely on the correct conditions.

* online_weight_minimum is already considered in the calculation of node::delta. Using online_weight_minimum directly can cause quorum requirements to be unnecessarily high as actual online vote weight approaches online_weight_minimum.

Replacing references to online_weight_minimum with node:delta and adding tests to ensure rollbacks and confirmation is done precisely on the correct conditions.

* Replace additional direct references to online_weight_minimum with references to delta (), which already considers the online_weight_minimum.

* Changing vote_processor to reference nano::node instead of all of its components directly.

* Moving online_stake calculation on to node.

* Tracking trended weight separately from currently online weight.
Moving delta on to online_reps class so all network weight calculations are done in one place.

* Cleaning up tests and removing unrelated vote_processor changes.

* Avoiding extra memory allocations on vector.
Generating timestamp within mutex to avoid issues of delays.
Removing reference to unused header.
This commit is contained in:
clemahieu 2020-11-06 18:21:03 +01:00 committed by GitHub
commit 932ee97318
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 343 additions and 156 deletions

View file

@ -463,7 +463,7 @@ TEST (bootstrap_processor, frontiers_unconfirmed_threshold)
nano::keypair key1, key2;
// Generating invalid chain
auto threshold (node1->gap_cache.bootstrap_threshold () + 1);
ASSERT_LT (threshold, node1->config.online_weight_minimum.number ());
ASSERT_LT (threshold, node1->online_reps.delta ());
auto send1 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, genesis.hash (), nano::dev_genesis_key.pub, nano::genesis_amount - threshold, key1.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (genesis.hash ())));
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - threshold - nano::Gxrb_ratio, key2.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1->hash ())));

View file

@ -93,9 +93,9 @@ TEST (confirmation_height, multiple_accounts)
nano::block_hash latest1 (system.nodes[0]->latest (nano::dev_genesis_key.pub));
// Send to all accounts
nano::send_block send1 (latest1, key1.pub, system.nodes.front ()->config.online_weight_minimum.number () + 300, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (latest1));
nano::send_block send2 (send1.hash (), key2.pub, system.nodes.front ()->config.online_weight_minimum.number () + 200, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1.hash ()));
nano::send_block send3 (send2.hash (), key3.pub, system.nodes.front ()->config.online_weight_minimum.number () + 100, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send2.hash ()));
nano::send_block send1 (latest1, key1.pub, node->online_reps.delta () + 300, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (latest1));
nano::send_block send2 (send1.hash (), key2.pub, node->online_reps.delta () + 200, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1.hash ()));
nano::send_block send3 (send2.hash (), key3.pub, node->online_reps.delta () + 100, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send2.hash ()));
// Open all accounts
nano::open_block open1 (send1.hash (), nano::genesis_account, key1.pub, key1.prv, key1.pub, *system.work.generate (key1.pub));
@ -393,7 +393,7 @@ TEST (confirmation_height, send_receive_between_2_accounts)
nano::keypair key1;
nano::block_hash latest (node->latest (nano::dev_genesis_key.pub));
nano::send_block send1 (latest, key1.pub, node->config.online_weight_minimum.number () + 2, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (latest));
nano::send_block send1 (latest, key1.pub, node->online_reps.delta () + 2, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (latest));
nano::open_block open1 (send1.hash (), nano::genesis_account, key1.pub, key1.prv, key1.pub, *system.work.generate (key1.pub));
nano::send_block send2 (open1.hash (), nano::genesis_account, 1000, key1.prv, key1.pub, *system.work.generate (open1.hash ()));
@ -404,11 +404,11 @@ TEST (confirmation_height, send_receive_between_2_accounts)
nano::receive_block receive2 (receive1.hash (), send3.hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (receive1.hash ()));
nano::receive_block receive3 (receive2.hash (), send4.hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (receive2.hash ()));
nano::send_block send5 (receive3.hash (), key1.pub, node->config.online_weight_minimum.number () + 1, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (receive3.hash ()));
nano::send_block send5 (receive3.hash (), key1.pub, node->online_reps.delta () + 1, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (receive3.hash ()));
auto receive4 = std::make_shared<nano::receive_block> (send4.hash (), send5.hash (), key1.prv, key1.pub, *system.work.generate (send4.hash ()));
// Unpocketed send
nano::keypair key2;
nano::send_block send6 (send5.hash (), key2.pub, node->config.online_weight_minimum.number (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send5.hash ()));
nano::send_block send6 (send5.hash (), key2.pub, node->online_reps.delta (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send5.hash ()));
{
auto transaction = node->store.tx_begin_write ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code);
@ -487,7 +487,7 @@ TEST (confirmation_height, send_receive_self)
// Send to another account to prevent automatic receiving on the genesis account
nano::keypair key1;
nano::send_block send4 (receive3->hash (), key1.pub, node->config.online_weight_minimum.number (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (receive3->hash ()));
nano::send_block send4 (receive3->hash (), key1.pub, node->online_reps.delta (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (receive3->hash ()));
{
auto transaction = node->store.tx_begin_write ();
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send1).code);

View file

@ -13,3 +13,155 @@ TEST (election, construction)
auto election = node.active.insert (genesis.open).election;
election->transition_active ();
}
TEST (election, quorum_minimum_flip_success)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.online_weight_minimum = nano::genesis_amount;
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node1 = *system.add_node (node_config);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (node1.online_reps.delta ())
.link (key1.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build_shared ();
node1.work_generate_blocking (*send1);
nano::keypair key2;
auto send2 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (node1.online_reps.delta ())
.link (key2.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build_shared ();
node1.work_generate_blocking (*send2);
node1.process_active (send1);
node1.process_active (send2);
node1.block_processor.flush ();
auto election{ node1.active.insert (send1) };
ASSERT_FALSE (election.inserted);
ASSERT_NE (nullptr, election.election);
ASSERT_EQ (2, election.election->blocks ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send2));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
node1.block_processor.flush ();
ASSERT_NE (nullptr, node1.block (send2->hash ()));
ASSERT_TRUE (election.election->confirmed ());
}
TEST (election, quorum_minimum_flip_fail)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.online_weight_minimum = nano::genesis_amount;
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node1 = *system.add_node (node_config);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (node1.online_reps.delta () - 1)
.link (key1.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build_shared ();
node1.work_generate_blocking (*send1);
nano::keypair key2;
auto send2 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (node1.online_reps.delta () - 1)
.link (key2.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build_shared ();
node1.work_generate_blocking (*send2);
node1.process_active (send1);
node1.process_active (send2);
node1.block_processor.flush ();
auto election{ node1.active.insert (send1) };
ASSERT_FALSE (election.inserted);
ASSERT_NE (nullptr, election.election);
ASSERT_EQ (2, election.election->blocks ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send2));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
node1.block_processor.flush ();
ASSERT_NE (nullptr, node1.block (send1->hash ()));
ASSERT_FALSE (election.election->confirmed ());
}
TEST (election, quorum_minimum_confirm_success)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.online_weight_minimum = nano::genesis_amount;
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node1 = *system.add_node (node_config);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (node1.online_reps.delta ())
.link (key1.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build_shared ();
node1.work_generate_blocking (*send1);
node1.process_active (send1);
node1.block_processor.flush ();
auto election{ node1.active.insert (send1) };
ASSERT_FALSE (election.inserted);
ASSERT_NE (nullptr, election.election);
ASSERT_EQ (1, election.election->blocks ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
node1.block_processor.flush ();
ASSERT_NE (nullptr, node1.block (send1->hash ()));
ASSERT_TRUE (election.election->confirmed ());
}
TEST (election, quorum_minimum_confirm_fail)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.online_weight_minimum = nano::genesis_amount;
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node1 = *system.add_node (node_config);
nano::keypair key1;
nano::block_builder builder;
auto send1 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub)
.balance (node1.online_reps.delta () - 1)
.link (key1.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build_shared ();
node1.work_generate_blocking (*send1);
node1.process_active (send1);
node1.block_processor.flush ();
auto election{ node1.active.insert (send1) };
ASSERT_FALSE (election.inserted);
ASSERT_NE (nullptr, election.election);
ASSERT_EQ (1, election.election->blocks ().size ());
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
node1.block_processor.flush ();
ASSERT_NE (nullptr, node1.block (send1->hash ()));
ASSERT_FALSE (election.election->confirmed ());
}

View file

@ -837,17 +837,22 @@ TEST (votes, add_existing)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.online_weight_minimum = std::numeric_limits<nano::uint128_t>::max ();
node_config.online_weight_minimum = nano::genesis_amount;
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
auto & node1 = *system.add_node (node_config);
nano::genesis genesis;
nano::keypair key1;
auto send1 (std::make_shared<nano::send_block> (genesis.hash (), key1.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0));
nano::block_builder builder;
std::shared_ptr<nano::block> send1 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub) // No representative, blocks can't confirm
.balance (nano::genesis_amount / 2 - nano::Gxrb_ratio)
.link (key1.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build ();
node1.work_generate_blocking (*send1);
{
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code);
}
ASSERT_EQ (nano::process_result::progress, node1.ledger.process (node1.store.tx_begin_write (), *send1).code);
auto election1 = node1.active.insert (send1);
auto vote1 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 1, send1));
ASSERT_EQ (nano::vote_code::vote, node1.active.vote (vote1));
@ -855,7 +860,15 @@ TEST (votes, add_existing)
ASSERT_TRUE (node1.active.publish (send1));
ASSERT_EQ (1, election1.election->last_votes[nano::dev_genesis_key.pub].timestamp);
nano::keypair key2;
auto send2 (std::make_shared<nano::send_block> (genesis.hash (), key2.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0));
std::shared_ptr<nano::block> send2 = builder.state ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
.representative (nano::dev_genesis_key.pub) // No representative, blocks can't confirm
.balance (nano::genesis_amount / 2 - nano::Gxrb_ratio)
.link (key2.pub)
.work (0)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.build ();
node1.work_generate_blocking (*send2);
auto vote2 (std::make_shared<nano::vote> (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send2));
// Pretend we've waited the timeout

View file

@ -229,14 +229,14 @@ TEST (node, quick_confirm)
auto send = nano::send_block_builder ()
.previous (previous)
.destination (key.pub)
.balance (node1.config.online_weight_minimum.number () + 1)
.balance (node1.online_reps.delta () + 1)
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
.work (*system.work.generate (previous))
.build_shared ();
node1.process_active (send);
ASSERT_TIMELY (10s, !node1.balance (key.pub).is_zero ());
ASSERT_EQ (node1.balance (nano::dev_genesis_key.pub), node1.config.online_weight_minimum.number () + 1);
ASSERT_EQ (node1.balance (key.pub), genesis_start_balance - (node1.config.online_weight_minimum.number () + 1));
ASSERT_EQ (node1.balance (nano::dev_genesis_key.pub), node1.online_reps.delta () + 1);
ASSERT_EQ (node1.balance (key.pub), genesis_start_balance - (node1.online_reps.delta () + 1));
}
TEST (node, node_receive_quorum)
@ -2415,15 +2415,19 @@ TEST (node, online_reps)
nano::system system (1);
auto & node1 (*system.nodes[0]);
// 1 sample of minimum weight
ASSERT_EQ (node1.config.online_weight_minimum, node1.online_reps.online_stake ());
ASSERT_EQ (node1.config.online_weight_minimum, node1.online_reps.trended ());
auto vote (std::make_shared<nano::vote> ());
ASSERT_EQ (0, node1.online_reps.online ());
node1.online_reps.observe (nano::dev_genesis_key.pub);
ASSERT_EQ (nano::genesis_amount, node1.online_reps.online ());
// 1 minimum, 1 maximum
ASSERT_EQ (node1.config.online_weight_minimum, node1.online_reps.trended ());
node1.online_reps.sample ();
ASSERT_EQ (nano::genesis_amount, node1.online_reps.online_stake ());
ASSERT_EQ (nano::genesis_amount, node1.online_reps.trended ());
node1.online_reps.clear ();
// 2 minimum, 1 maximum
node1.online_reps.sample ();
ASSERT_EQ (node1.config.online_weight_minimum, node1.online_reps.online_stake ());
ASSERT_EQ (node1.config.online_weight_minimum, node1.online_reps.trended ());
}
TEST (node, block_confirm)
@ -2537,8 +2541,8 @@ TEST (node, confirm_quorum)
auto & node1 (*system.nodes[0]);
nano::genesis genesis;
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
// Put greater than online_weight_minimum in pending so quorum can't be reached
nano::amount new_balance (node1.config.online_weight_minimum.number () - nano::Gxrb_ratio);
// Put greater than node.delta () in pending so quorum can't be reached
nano::amount new_balance (node1.online_reps.delta () - nano::Gxrb_ratio);
auto send1 = nano::state_block_builder ()
.account (nano::dev_genesis_key.pub)
.previous (genesis.hash ())
@ -3855,7 +3859,7 @@ TEST (node, rollback_vote_self)
auto & node = *system.add_node (flags);
nano::state_block_builder builder;
nano::keypair key;
auto weight = node.config.online_weight_minimum.number ();
auto weight = node.online_reps.delta ();
auto send1 = builder.make_block ()
.account (nano::dev_genesis_key.pub)
.previous (nano::genesis_hash)
@ -4490,33 +4494,6 @@ TEST (rep_crawler, local)
}
}
TEST (online_reps, backup)
{
nano::logger_mt logger;
auto store = nano::make_store (logger, nano::unique_path ());
ASSERT_TRUE (!store->init_error ());
nano::stat stats;
nano::ledger ledger (*store, stats);
{
nano::genesis genesis;
auto transaction (store->tx_begin_write ());
store->initialize (transaction, genesis, ledger.cache);
}
nano::network_params params;
nano::online_reps online_reps (ledger, params, 0);
ASSERT_EQ (0, online_reps.list ().size ());
online_reps.observe (nano::dev_genesis_key.pub);
// The reported list of online reps is the union of the current list and the backup list, which changes when sampling
ASSERT_EQ (1, online_reps.list ().size ());
ASSERT_TRUE (online_reps.online_stake ().is_zero ());
online_reps.sample ();
ASSERT_EQ (1, online_reps.list ().size ());
// The trend is also correctly updated
ASSERT_EQ (nano::genesis_amount, online_reps.online_stake ());
online_reps.sample ();
ASSERT_EQ (0, online_reps.list ().size ());
}
namespace
{
void add_required_children_node_config_tree (nano::jsonconfig & tree)

View file

@ -156,7 +156,7 @@ TEST (websocket, confirmation)
nano::keypair key;
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
auto balance = nano::genesis_amount;
auto send_amount = node1->config.online_weight_minimum.number () + 1;
auto send_amount = node1->online_reps.delta () + 1;
// Quick-confirm a block, legacy blocks should work without filtering
{
nano::block_hash previous (node1->latest (nano::dev_genesis_key.pub));
@ -248,7 +248,7 @@ TEST (websocket, confirmation_options)
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
nano::keypair key;
auto balance = nano::genesis_amount;
auto send_amount = node1->config.online_weight_minimum.number () + 1;
auto send_amount = node1->online_reps.delta () + 1;
nano::block_hash previous (node1->latest (nano::dev_genesis_key.pub));
{
balance -= send_amount;
@ -416,7 +416,7 @@ TEST (websocket, vote)
nano::keypair key;
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
nano::block_hash previous (node1->latest (nano::dev_genesis_key.pub));
auto send (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, previous, nano::dev_genesis_key.pub, nano::genesis_amount - (node1->config.online_weight_minimum.number () + 1), key.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (previous)));
auto send (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, previous, nano::dev_genesis_key.pub, nano::genesis_amount - (node1->online_reps.delta () + 1), key.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (previous)));
node1->process_active (send);
ASSERT_TIMELY (5s, future.wait_for (0s) == std::future_status::ready);
@ -505,7 +505,7 @@ TEST (websocket, vote_options_representatives)
nano::keypair key;
auto balance = nano::genesis_amount;
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
auto send_amount = node1->config.online_weight_minimum.number () + 1;
auto send_amount = node1->online_reps.delta () + 1;
auto confirm_block = [&]() {
nano::block_hash previous (node1->latest (nano::dev_genesis_key.pub));
balance -= send_amount;

View file

@ -77,7 +77,7 @@ int main (int argc, char * const * argv)
("debug_block_count", "Display the number of blocks")
("debug_bootstrap_generate", "Generate bootstrap sequence of blocks")
("debug_dump_frontier_unchecked_dependents", "Dump frontiers which have matching unchecked keys")
("debug_dump_online_weight", "Dump online_weights table")
("debug_dump_trended_weight", "Dump trended weights table")
("debug_dump_representatives", "List representatives and weights")
("debug_account_count", "Display the number of accounts")
("debug_profile_generate", "Profile work generation")
@ -382,12 +382,12 @@ int main (int argc, char * const * argv)
result = -1;
}
}
else if (vm.count ("debug_dump_online_weight"))
else if (vm.count ("debug_dump_trended_weight"))
{
auto inactive_node = nano::default_inactive_node (data_path, vm);
auto node = inactive_node->node;
auto current (node->online_reps.online_stake ());
std::cout << boost::str (boost::format ("Online Weight %1%\n") % current);
auto current (node->online_reps.trended ());
std::cout << boost::str (boost::format ("Trended Weight %1%\n") % current);
auto transaction (node->store.tx_begin_read ());
for (auto i (node->store.online_weight_begin (transaction)), n (node->store.online_weight_end ()); i != n; ++i)
{

View file

@ -1462,7 +1462,7 @@ nano::inactive_cache_status nano::active_transactions::inactive_votes_bootstrap_
tally += node.ledger.weight (voter);
}
if (!previously_a.confirmed && tally >= node.config.online_weight_minimum.number ())
if (!previously_a.confirmed && tally >= node.online_reps.delta ())
{
status.bootstrap_started = true;
status.confirmed = true;
@ -1471,7 +1471,7 @@ nano::inactive_cache_status nano::active_transactions::inactive_votes_bootstrap_
{
status.bootstrap_started = true;
}
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)
if (!previously_a.election_started && voters_a.size () >= election_start_voters_min && tally >= (node.online_reps.trended () / 100) * node.config.election_hint_weight_percent)
{
status.election_started = true;
}

View file

@ -229,17 +229,13 @@ bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a
return result;
}
bool nano::election::have_quorum (nano::tally_t const & tally_a, nano::uint128_t tally_sum) const
bool nano::election::have_quorum (nano::tally_t const & tally_a) const
{
bool result = false;
if (tally_sum >= node.config.online_weight_minimum.number ())
{
auto i (tally_a.begin ());
++i;
auto second (i != tally_a.end () ? i->first : 0);
auto delta_l (node.delta ());
result = tally_a.begin ()->first > (second + delta_l);
}
auto i (tally_a.begin ());
++i;
auto second (i != tally_a.end () ? i->first : 0);
auto delta_l (node.online_reps.delta ());
bool result{ tally_a.begin ()->first >= (second + delta_l) };
return result;
}
@ -284,13 +280,13 @@ void nano::election::confirm_if_quorum (nano::unique_lock<std::mutex> & lock_a)
{
sum += i.first;
}
if (sum >= node.config.online_weight_minimum.number () && winner_hash_l != status_winner_hash_l)
if (sum >= node.online_reps.delta () && winner_hash_l != status_winner_hash_l)
{
status.winner = block_l;
remove_votes (status_winner_hash_l);
node.block_processor.force (block_l);
}
if (have_quorum (tally_l, sum))
if (have_quorum (tally_l))
{
if (node.config.logging.vote_logging () || (node.config.logging.election_fork_tally_logging () && last_blocks.size () > 1))
{
@ -334,7 +330,7 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint
{
// see republish_vote documentation for an explanation of these rules
auto replay (false);
auto online_stake (node.online_reps.online_stake ());
auto online_stake (node.online_reps.trended ());
auto weight (node.ledger.weight (rep));
auto should_process (false);
if (node.network_params.network.is_dev_network () || weight > node.minimum_principal_weight (online_stake))
@ -396,7 +392,7 @@ bool nano::election::publish (std::shared_ptr<nano::block> const & block_a)
auto result (confirmed ());
if (!result && last_blocks.size () >= 10)
{
if (last_tally[block_a->hash ()] < node.online_reps.online_stake () / 10)
if (last_tally[block_a->hash ()] < node.online_reps.trended () / 10)
{
result = true;
node.network.publish_filter.clear (block_a);

View file

@ -96,7 +96,7 @@ public: // Status
void log_votes (nano::tally_t const &, std::string const & = "") const;
nano::tally_t tally () const;
bool have_quorum (nano::tally_t const &, nano::uint128_t) const;
bool have_quorum (nano::tally_t const &) const;
// Guarded by mutex
nano::election_status status;

View file

@ -77,7 +77,7 @@ bool nano::gap_cache::bootstrap_check (std::vector<nano::account> const & voters
bool start_bootstrap (false);
if (!node.flags.disable_lazy_bootstrap)
{
if (tally >= node.config.online_weight_minimum.number ())
if (tally >= node.online_reps.delta ())
{
start_bootstrap = true;
}
@ -117,7 +117,7 @@ void nano::gap_cache::bootstrap_start (nano::block_hash const & hash_a)
nano::uint128_t nano::gap_cache::bootstrap_threshold ()
{
auto result ((node.online_reps.online_stake () / 256) * node.config.bootstrap_fraction_numerator);
auto result ((node.online_reps.trended () / 256) * node.config.bootstrap_fraction_numerator);
return result;
}

View file

@ -1994,12 +1994,12 @@ void nano::json_handler::confirmation_info ()
void nano::json_handler::confirmation_quorum ()
{
response_l.put ("quorum_delta", node.delta ().convert_to<std::string> ());
response_l.put ("quorum_delta", node.online_reps.delta ().convert_to<std::string> ());
response_l.put ("online_weight_quorum_percent", std::to_string (node.config.online_weight_quorum));
response_l.put ("online_weight_minimum", node.config.online_weight_minimum.to_string_dec ());
response_l.put ("online_stake_total", node.online_reps.online_stake ().convert_to<std::string> ());
response_l.put ("online_stake_total", node.online_reps.online ().convert_to<std::string> ());
response_l.put ("trended_stake_total", node.online_reps.trended ().convert_to<std::string> ());
response_l.put ("peers_stake_total", node.rep_crawler.total_weight ().convert_to<std::string> ());
response_l.put ("peers_stake_required", std::max (node.config.online_weight_minimum.number (), node.delta ()).convert_to<std::string> ());
if (request.get<bool> ("peer_details", false))
{
boost::property_tree::ptree peers;

View file

@ -248,7 +248,7 @@ void nano::network::send_confirm_req (std::shared_ptr<nano::transport::channel>
void nano::network::broadcast_confirm_req (std::shared_ptr<nano::block> block_a)
{
auto list (std::make_shared<std::vector<std::shared_ptr<nano::transport::channel>>> (node.rep_crawler.representative_endpoints (std::numeric_limits<size_t>::max ())));
if (list->empty () || node.rep_crawler.total_weight () < node.config.online_weight_minimum.number ())
if (list->empty () || node.rep_crawler.total_weight () < node.online_reps.delta ())
{
// broadcast request to all peers (with max limit 2 * sqrt (peers count))
auto peers (node.network.list (std::min<size_t> (100, node.network.fanout (2.0))));

View file

@ -118,7 +118,7 @@ block_processor_thread ([this]() {
this->block_processor.process_blocks ();
}),
// clang-format on
online_reps (ledger, network_params, config.online_weight_minimum.number ()),
online_reps (ledger, config),
vote_uniquer (block_uniquer),
confirmation_height_processor (ledger, write_database_queue, config.conf_height_processor_batch_min_time, config.logging, logger, node_initialized_latch, flags.confirmation_height_processor_mode),
active (*this, confirmation_height_processor),
@ -779,7 +779,7 @@ nano::block_hash nano::node::rep_block (nano::account const & account_a)
nano::uint128_t nano::node::minimum_principal_weight ()
{
return minimum_principal_weight (online_reps.online_stake ());
return minimum_principal_weight (online_reps.trended ());
}
nano::uint128_t nano::node::minimum_principal_weight (nano::uint128_t const & online_stake)
@ -1130,12 +1130,6 @@ bool nano::node::block_confirmed_or_being_confirmed (nano::transaction const & t
return confirmation_height_processor.is_processing_block (hash_a) || ledger.block_confirmed (transaction_a, hash_a);
}
nano::uint128_t nano::node::delta () const
{
auto result ((online_reps.online_stake () / 100) * config.online_weight_quorum);
return result;
}
void nano::node::ongoing_online_weight_calculation_queue ()
{
std::weak_ptr<nano::node> node_w (shared_from_this ());
@ -1151,7 +1145,7 @@ void nano::node::ongoing_online_weight_calculation_queue ()
bool nano::node::online () const
{
return rep_crawler.total_weight () > (std::max (config.online_weight_minimum.number (), delta ()));
return rep_crawler.total_weight () > online_reps.delta ();
}
void nano::node::ongoing_online_weight_calculation ()

View file

@ -149,7 +149,6 @@ public:
bool block_confirmed_or_being_confirmed (nano::transaction const &, nano::block_hash const &);
void process_fork (nano::transaction const &, std::shared_ptr<nano::block> const &, uint64_t);
void do_rpc_callback (boost::asio::ip::tcp::resolver::iterator i_a, std::string const &, uint16_t, std::shared_ptr<std::string>, std::shared_ptr<std::string>, std::shared_ptr<boost::asio::ip::tcp::resolver>);
nano::uint128_t delta () const;
void ongoing_online_weight_calculation ();
void ongoing_online_weight_calculation_queue ();
bool online () const;

View file

@ -1,17 +1,16 @@
#include <nano/node/nodeconfig.hpp>
#include <nano/node/online_reps.hpp>
#include <nano/secure/blockstore.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/ledger.hpp>
nano::online_reps::online_reps (nano::ledger & ledger_a, nano::network_params & network_params_a, nano::uint128_t minimum_a) :
ledger (ledger_a),
network_params (network_params_a),
minimum (minimum_a)
nano::online_reps::online_reps (nano::ledger & ledger_a, nano::node_config const & config_a) :
ledger{ ledger_a },
config{ config_a }
{
if (!ledger.store.init_error ())
{
auto transaction (ledger.store.tx_begin_read ());
online = trend (transaction);
trended_m = calculate_trend (transaction);
}
}
@ -20,82 +19,112 @@ void nano::online_reps::observe (nano::account const & rep_a)
if (ledger.weight (rep_a) > 0)
{
nano::lock_guard<std::mutex> lock (mutex);
reps.insert (rep_a);
auto now = std::chrono::steady_clock::now ();
auto new_insert = reps.get<tag_account> ().erase (rep_a) == 0;
reps.insert ({ now, rep_a });
auto cutoff = reps.get<tag_time> ().lower_bound (now - std::chrono::seconds (config.network_params.node.weight_period));
auto trimmed = reps.get<tag_time> ().begin () != cutoff;
reps.get<tag_time> ().erase (reps.get<tag_time> ().begin (), cutoff);
if (new_insert || trimmed)
{
online_m = calculate_online ();
}
}
}
void nano::online_reps::sample ()
{
auto transaction (ledger.store.tx_begin_write ({ tables::online_weight }));
// Discard oldest entries
while (ledger.store.online_weight_count (transaction) >= network_params.node.max_weight_samples)
nano::unique_lock<std::mutex> lock (mutex);
nano::uint128_t online_l = online_m;
lock.unlock ();
nano::uint128_t trend_l;
{
auto oldest (ledger.store.online_weight_begin (transaction));
debug_assert (oldest != ledger.store.online_weight_end ());
ledger.store.online_weight_del (transaction, oldest->first);
auto transaction (ledger.store.tx_begin_write ({ tables::online_weight }));
// Discard oldest entries
while (ledger.store.online_weight_count (transaction) >= config.network_params.node.max_weight_samples)
{
auto oldest (ledger.store.online_weight_begin (transaction));
debug_assert (oldest != ledger.store.online_weight_end ());
ledger.store.online_weight_del (transaction, oldest->first);
}
ledger.store.online_weight_put (transaction, std::chrono::system_clock::now ().time_since_epoch ().count (), online_l);
trend_l = calculate_trend (transaction);
}
// Calculate current active rep weight
nano::uint128_t current;
std::unordered_set<nano::account> reps_copy;
{
nano::lock_guard<std::mutex> lock (mutex);
last_reps = reps;
reps_copy.swap (reps);
}
for (auto & i : reps_copy)
{
current += ledger.weight (i);
}
ledger.store.online_weight_put (transaction, std::chrono::system_clock::now ().time_since_epoch ().count (), current);
auto trend_l (trend (transaction));
nano::lock_guard<std::mutex> lock (mutex);
online = trend_l;
lock.lock ();
trended_m = trend_l;
}
nano::uint128_t nano::online_reps::trend (nano::transaction & transaction_a)
nano::uint128_t nano::online_reps::calculate_online () const
{
nano::uint128_t current;
for (auto & i : reps)
{
current += ledger.weight (i.account);
}
return current;
}
nano::uint128_t nano::online_reps::calculate_trend (nano::transaction & transaction_a) const
{
std::vector<nano::uint128_t> items;
items.reserve (network_params.node.max_weight_samples + 1);
items.push_back (minimum);
items.reserve (config.network_params.node.max_weight_samples + 1);
items.push_back (config.online_weight_minimum.number ());
for (auto i (ledger.store.online_weight_begin (transaction_a)), n (ledger.store.online_weight_end ()); i != n; ++i)
{
items.push_back (i->second.number ());
}
nano::uint128_t result;
// Pick median value for our target vote weight
auto median_idx = items.size () / 2;
nth_element (items.begin (), items.begin () + median_idx, items.end ());
return nano::uint128_t{ items[median_idx] };
result = items[median_idx];
return result;
}
nano::uint128_t nano::online_reps::online_stake () const
nano::uint128_t nano::online_reps::trended () const
{
nano::lock_guard<std::mutex> lock (mutex);
return std::max (online, minimum);
return trended_m;
}
nano::uint128_t nano::online_reps::online () const
{
nano::lock_guard<std::mutex> lock (mutex);
return online_m;
}
nano::uint128_t nano::online_reps::delta () const
{
nano::lock_guard<std::mutex> lock (mutex);
auto weight = std::max ({ online_m, trended_m, config.online_weight_minimum.number () });
auto result ((weight / 100) * config.online_weight_quorum);
return result;
}
std::vector<nano::account> nano::online_reps::list ()
{
std::vector<nano::account> result;
decltype (reps) all_reps;
{
nano::lock_guard<std::mutex> lock (mutex);
all_reps.insert (last_reps.begin (), last_reps.end ());
all_reps.insert (reps.begin (), reps.end ());
}
result.insert (result.end (), all_reps.begin (), all_reps.end ());
nano::lock_guard<std::mutex> lock (mutex);
std::for_each (reps.begin (), reps.end (), [&result](rep_info const & info_a) { result.push_back (info_a.account); });
return result;
}
void nano::online_reps::clear ()
{
nano::lock_guard<std::mutex> lock (mutex);
reps.clear ();
online_m = 0;
}
std::unique_ptr<nano::container_info_component> nano::collect_container_info (online_reps & online_reps, const std::string & name)
{
size_t count;
{
nano::lock_guard<std::mutex> guard (online_reps.mutex);
count = online_reps.last_reps.size ();
count = online_reps.reps.size ();
}
auto sizeof_element = sizeof (decltype (online_reps.last_reps)::value_type);
auto sizeof_element = sizeof (decltype (online_reps.reps)::value_type);
auto composite = std::make_unique<container_info_composite> (name);
composite->add_component (std::make_unique<container_info_leaf> (container_info{ "reps", count, sizeof_element }));
return composite;

View file

@ -2,6 +2,12 @@
#include <nano/lib/numbers.hpp>
#include <nano/lib/utility.hpp>
#include <nano/secure/common.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index_container.hpp>
#include <memory>
#include <unordered_set>
@ -10,31 +16,55 @@
namespace nano
{
class ledger;
class network_params;
class node_config;
class transaction;
/** Track online representatives and trend online weight */
class online_reps final
{
public:
online_reps (nano::ledger & ledger_a, nano::network_params & network_params_a, nano::uint128_t minimum_a);
online_reps (nano::ledger & ledger_a, nano::node_config const & config_a);
/** Add voting account \p rep_account to the set of online representatives */
void observe (nano::account const & rep_account);
/** Called periodically to sample online weight */
void sample ();
/** Returns the trended online stake, but never less than configured minimum */
nano::uint128_t online_stake () const;
/** Returns the trended online stake */
nano::uint128_t trended () const;
/** Returns the current online stake */
nano::uint128_t online () const;
/** Returns the quorum required for confirmation*/
nano::uint128_t delta () const;
/** List of online representatives, both the currently sampling ones and the ones observed in the previous sampling period */
std::vector<nano::account> list ();
void clear ();
private:
nano::uint128_t trend (nano::transaction &);
class rep_info
{
public:
std::chrono::steady_clock::time_point time;
nano::account account;
};
class tag_time
{
};
class tag_account
{
};
nano::uint128_t calculate_trend (nano::transaction &) const;
nano::uint128_t calculate_online () const;
mutable std::mutex mutex;
nano::ledger & ledger;
nano::network_params & network_params;
std::unordered_set<nano::account> reps;
std::unordered_set<nano::account> last_reps;
nano::uint128_t online;
nano::node_config const & config;
boost::multi_index_container<rep_info,
boost::multi_index::indexed_by<
boost::multi_index::ordered_non_unique<boost::multi_index::tag<tag_time>,
boost::multi_index::member<rep_info, std::chrono::steady_clock::time_point, &rep_info::time>>,
boost::multi_index::hashed_unique<boost::multi_index::tag<tag_account>,
boost::multi_index::member<rep_info, nano::account, &rep_info::account>>>>
reps;
nano::uint128_t trended_m;
nano::uint128_t online_m;
nano::uint128_t minimum;
friend std::unique_ptr<container_info_component> collect_container_info (online_reps & online_reps, const std::string & name);

View file

@ -84,7 +84,7 @@ void nano::rep_crawler::ongoing_crawl ()
update_weights ();
validate ();
query (get_crawl_targets (total_weight_l));
auto sufficient_weight (total_weight_l > node.config.online_weight_minimum.number ());
auto sufficient_weight (total_weight_l > node.online_reps.delta ());
// If online weight drops below minimum, reach out to preconfigured peers
if (!sufficient_weight)
{
@ -107,7 +107,7 @@ std::vector<std::shared_ptr<nano::transport::channel>> nano::rep_crawler::get_cr
constexpr size_t aggressive_count = 40;
// Crawl more aggressively if we lack sufficient total peer weight.
bool sufficient_weight (total_weight_a > node.config.online_weight_minimum.number ());
bool sufficient_weight (total_weight_a > node.online_reps.delta ());
uint16_t required_peer_count = sufficient_weight ? conservative_count : aggressive_count;
// Add random peers. We do this even if we have enough weight, in order to pick up reps

View file

@ -3,7 +3,6 @@
#include <nano/lib/threading.hpp>
#include <nano/lib/timer.hpp>
#include <nano/node/active_transactions.hpp>
#include <nano/node/node.hpp>
#include <nano/node/node_observers.hpp>
#include <nano/node/nodeconfig.hpp>
#include <nano/node/online_reps.hpp>
@ -257,7 +256,7 @@ void nano::vote_processor::calculate_weights ()
representatives_1.clear ();
representatives_2.clear ();
representatives_3.clear ();
auto supply (online_reps.online_stake ());
auto supply (online_reps.trended ());
auto rep_amounts = ledger.cache.rep_weights.get_rep_amounts ();
for (auto const & rep_amount : rep_amounts)
{

View file

@ -60,9 +60,7 @@ private:
nano::online_reps & online_reps;
nano::ledger & ledger;
nano::network_params & network_params;
size_t max_votes;
std::deque<std::pair<std::shared_ptr<nano::vote>, std::shared_ptr<nano::transport::channel>>> votes;
/** Representatives levels for random early detection */
std::unordered_set<nano::account> representatives_1;

View file

@ -5983,7 +5983,7 @@ TEST (rpc, online_reps)
auto node2 = add_ipc_enabled_node (system);
nano::keypair key;
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
ASSERT_TRUE (node2->online_reps.online_stake () == node2->config.online_weight_minimum.number ());
ASSERT_TRUE (node2->online_reps.online () == node2->config.online_weight_minimum.number ());
auto send_block (system.wallet (0)->send_action (nano::dev_genesis_key.pub, key.pub, nano::Gxrb_ratio));
ASSERT_NE (nullptr, send_block);
scoped_io_thread_name_change scoped_thread_name_io;

View file

@ -522,7 +522,7 @@ TEST (confirmation_height, many_accounts_single_confirmation)
nano::keypair key;
system.wallet (0)->insert_adhoc (key.prv);
nano::send_block send (last_open_hash, key.pub, node->config.online_weight_minimum.number (), last_keypair.prv, last_keypair.pub, *system.work.generate (last_open_hash));
nano::send_block send (last_open_hash, key.pub, node->online_reps.delta (), last_keypair.prv, last_keypair.pub, *system.work.generate (last_open_hash));
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
nano::open_block open (send.hash (), last_keypair.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub));
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, open).code);
@ -591,7 +591,7 @@ TEST (confirmation_height, many_accounts_many_confirmations)
nano::keypair key;
system.wallet (0)->insert_adhoc (key.prv);
nano::send_block send (ladev_genesis, key.pub, node->config.online_weight_minimum.number (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (ladev_genesis));
nano::send_block send (ladev_genesis, key.pub, node->online_reps.delta (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (ladev_genesis));
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, send).code);
auto open = std::make_shared<nano::open_block> (send.hash (), nano::dev_genesis_key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub));
ASSERT_EQ (nano::process_result::progress, node->ledger.process (transaction, *open).code);