diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 23e7bc6aeb..219603e458 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -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::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::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 ()))); diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index aabbd4afc7..e191bc8839 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -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 (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); diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp index d64ba3b041..f2af540de6 100644 --- a/nano/core_test/election.cpp +++ b/nano/core_test/election.cpp @@ -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::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::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::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::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 ()); +} diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 611c887abb..483a7b020b 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -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::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 (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 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::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 (genesis.hash (), key2.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); + std::shared_ptr 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::dev_genesis_key.pub, nano::dev_genesis_key.prv, 2, send2)); // Pretend we've waited the timeout diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 6941762edc..50ec952d63 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -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 ()); + 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) diff --git a/nano/core_test/websocket.cpp b/nano/core_test/websocket.cpp index 8d1fbc5980..955af433d0 100644 --- a/nano/core_test/websocket.cpp +++ b/nano/core_test/websocket.cpp @@ -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::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::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; diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 5b889a56b6..604985c1e6 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -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) { diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index ae53172a8e..4266b30593 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -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; } diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 3b01441dac..ed854e50af 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -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 & 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 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); diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 9d9a1cf01d..22ce77ab25 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -96,7 +96,7 @@ class election final : public std::enable_shared_from_this 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; diff --git a/nano/node/gap_cache.cpp b/nano/node/gap_cache.cpp index b5d9900aa7..2688f64348 100644 --- a/nano/node/gap_cache.cpp +++ b/nano/node/gap_cache.cpp @@ -77,7 +77,7 @@ bool nano::gap_cache::bootstrap_check (std::vector 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; } diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index bf1f55eb43..c07fef0a47 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -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 ()); + response_l.put ("quorum_delta", node.online_reps.delta ().convert_to ()); 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 ()); + response_l.put ("online_stake_total", node.online_reps.online ().convert_to ()); + response_l.put ("trended_stake_total", node.online_reps.trended ().convert_to ()); response_l.put ("peers_stake_total", node.rep_crawler.total_weight ().convert_to ()); - response_l.put ("peers_stake_required", std::max (node.config.online_weight_minimum.number (), node.delta ()).convert_to ()); if (request.get ("peer_details", false)) { boost::property_tree::ptree peers; diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 3138f8a385..68e3995467 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -248,7 +248,7 @@ void nano::network::send_confirm_req (std::shared_ptr void nano::network::broadcast_confirm_req (std::shared_ptr block_a) { auto list (std::make_shared>> (node.rep_crawler.representative_endpoints (std::numeric_limits::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 (100, node.network.fanout (2.0)))); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 09f470f4bb..08ac038371 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -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 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 () diff --git a/nano/node/node.hpp b/nano/node/node.hpp index ce6ae0a7b8..97b04b6a71 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -149,7 +149,6 @@ class node final : public std::enable_shared_from_this bool block_confirmed_or_being_confirmed (nano::transaction const &, nano::block_hash const &); void process_fork (nano::transaction const &, std::shared_ptr const &, uint64_t); void do_rpc_callback (boost::asio::ip::tcp::resolver::iterator i_a, std::string const &, uint16_t, std::shared_ptr, std::shared_ptr, std::shared_ptr); - nano::uint128_t delta () const; void ongoing_online_weight_calculation (); void ongoing_online_weight_calculation_queue (); bool online () const; diff --git a/nano/node/online_reps.cpp b/nano/node/online_reps.cpp index 3f825ad3c4..8b55ee18a6 100644 --- a/nano/node/online_reps.cpp +++ b/nano/node/online_reps.cpp @@ -1,17 +1,16 @@ +#include #include #include -#include #include -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 lock (mutex); - reps.insert (rep_a); + auto now = std::chrono::steady_clock::now (); + auto new_insert = reps.get ().erase (rep_a) == 0; + reps.insert ({ now, rep_a }); + auto cutoff = reps.get ().lower_bound (now - std::chrono::seconds (config.network_params.node.weight_period)); + auto trimmed = reps.get ().begin () != cutoff; + reps.get ().erase (reps.get ().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 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 + lock.lock (); + trended_m = trend_l; +} + +nano::uint128_t nano::online_reps::calculate_online () const +{ nano::uint128_t current; - std::unordered_set reps_copy; + for (auto & i : reps) { - nano::lock_guard lock (mutex); - last_reps = reps; - reps_copy.swap (reps); + current += ledger.weight (i.account); } - 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 lock (mutex); - online = trend_l; + return current; } -nano::uint128_t nano::online_reps::trend (nano::transaction & transaction_a) +nano::uint128_t nano::online_reps::calculate_trend (nano::transaction & transaction_a) const { std::vector 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::trended () const +{ + nano::lock_guard lock (mutex); + return trended_m; } -nano::uint128_t nano::online_reps::online_stake () const +nano::uint128_t nano::online_reps::online () const { nano::lock_guard lock (mutex); - return std::max (online, minimum); + return online_m; +} + +nano::uint128_t nano::online_reps::delta () const +{ + nano::lock_guard 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::online_reps::list () { std::vector result; - decltype (reps) all_reps; - { - nano::lock_guard 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 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 lock (mutex); + reps.clear (); + online_m = 0; +} + std::unique_ptr nano::collect_container_info (online_reps & online_reps, const std::string & name) { size_t count; { nano::lock_guard 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 (name); composite->add_component (std::make_unique (container_info{ "reps", count, sizeof_element })); return composite; diff --git a/nano/node/online_reps.hpp b/nano/node/online_reps.hpp index 8d7bb104d7..79c4cf5d26 100644 --- a/nano/node/online_reps.hpp +++ b/nano/node/online_reps.hpp @@ -2,6 +2,12 @@ #include #include +#include + +#include +#include +#include +#include #include #include @@ -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 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 reps; - std::unordered_set last_reps; - nano::uint128_t online; + nano::node_config const & config; + boost::multi_index_container, + boost::multi_index::member>, + boost::multi_index::hashed_unique, + boost::multi_index::member>>> + reps; + nano::uint128_t trended_m; + nano::uint128_t online_m; nano::uint128_t minimum; friend std::unique_ptr collect_container_info (online_reps & online_reps, const std::string & name); diff --git a/nano/node/repcrawler.cpp b/nano/node/repcrawler.cpp index 8cf4c1659a..7e762e6b98 100644 --- a/nano/node/repcrawler.cpp +++ b/nano/node/repcrawler.cpp @@ -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> 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 diff --git a/nano/node/vote_processor.cpp b/nano/node/vote_processor.cpp index c902e66047..8e7dec94d9 100644 --- a/nano/node/vote_processor.cpp +++ b/nano/node/vote_processor.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -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) { diff --git a/nano/node/vote_processor.hpp b/nano/node/vote_processor.hpp index 0bb27918cc..e0df26810a 100644 --- a/nano/node/vote_processor.hpp +++ b/nano/node/vote_processor.hpp @@ -60,9 +60,7 @@ class vote_processor final nano::online_reps & online_reps; nano::ledger & ledger; nano::network_params & network_params; - size_t max_votes; - std::deque, std::shared_ptr>> votes; /** Representatives levels for random early detection */ std::unordered_set representatives_1; diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index a69438cc56..53875ab035 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -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; diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index c79688057f..cad16788c4 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -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 (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);