Remove election fallback behaviors and adjusted multiplier calculations (#2891)
* Remove election fallback behaviors and adjusted multiplier calculations Removes backtracking and dependency activation. The move to sequential elections also allows removing the adjusted multiplier from chains. Frontier successor insertion (https://github.com/nanocurrency/nano-node/pull/2885) is the new fallback behavior which does not escalate. * Fix node.block_confirm which relied on activating dependencies
This commit is contained in:
parent
6783671f85
commit
ee85e1cc8e
11 changed files with 16 additions and 978 deletions
|
@ -99,166 +99,6 @@ TEST (active_transactions, confirm_frontier)
|
|||
}
|
||||
}
|
||||
|
||||
TEST (active_transactions, adjusted_multiplier_priority)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (nano::get_available_port (), system.logging);
|
||||
node_config.enable_voting = false;
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto & node1 = *system.add_node (node_config);
|
||||
nano::keypair key1, key2, key3;
|
||||
|
||||
nano::state_block_builder builder;
|
||||
auto send1 = builder.make_block ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (nano::genesis_hash)
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (nano::genesis_amount - 10 * nano::xrb_ratio)
|
||||
.link (key1.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (nano::genesis_hash))
|
||||
.build_shared ();
|
||||
auto send2 = builder.make_block ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (send1->hash ())
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (nano::genesis_amount - 20 * nano::xrb_ratio)
|
||||
.link (key2.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build_shared ();
|
||||
auto open1 = builder.make_block ()
|
||||
.account (key1.pub)
|
||||
.previous (0)
|
||||
.representative (key1.pub)
|
||||
.balance (10 * nano::xrb_ratio)
|
||||
.link (send1->hash ())
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (key1.pub))
|
||||
.build_shared ();
|
||||
auto open2 = builder.make_block ()
|
||||
.account (key2.pub)
|
||||
.previous (0)
|
||||
.representative (key2.pub)
|
||||
.balance (10 * nano::xrb_ratio)
|
||||
.link (send2->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (key2.pub))
|
||||
.build_shared ();
|
||||
node1.process_active (send1); // genesis
|
||||
node1.process_active (send2); // genesis
|
||||
node1.process_active (open1); // key1
|
||||
node1.process_active (open2); // key2
|
||||
nano::blocks_confirm (node1, { send1, send2, open1, open2 });
|
||||
ASSERT_TIMELY (10s, node1.active.size () == 4);
|
||||
|
||||
// Check adjusted difficulty
|
||||
{
|
||||
nano::lock_guard<std::mutex> active_guard (node1.active.mutex);
|
||||
node1.active.update_adjusted_multiplier ();
|
||||
ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ());
|
||||
ASSERT_LT (node1.active.roots.find (send2->qualified_root ())->adjusted_multiplier, node1.active.roots.find (send1->qualified_root ())->adjusted_multiplier);
|
||||
ASSERT_LT (node1.active.roots.find (open1->qualified_root ())->adjusted_multiplier, node1.active.roots.find (send1->qualified_root ())->adjusted_multiplier);
|
||||
ASSERT_LT (node1.active.roots.find (open2->qualified_root ())->adjusted_multiplier, node1.active.roots.find (send2->qualified_root ())->adjusted_multiplier);
|
||||
}
|
||||
|
||||
// Confirm elections
|
||||
system.deadline_set (10s);
|
||||
while (!node1.active.empty ())
|
||||
{
|
||||
nano::lock_guard<std::mutex> active_guard (node1.active.mutex);
|
||||
if (!node1.active.roots.empty ())
|
||||
{
|
||||
node1.active.roots.begin ()->election->confirm_once ();
|
||||
}
|
||||
}
|
||||
ASSERT_TIMELY (10s, node1.ledger.cache.cemented_count == 5 && node1.active.empty ());
|
||||
|
||||
//genesis and key1,key2 are opened
|
||||
//start chain of 2 on each
|
||||
auto send3 = builder.make_block ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (send2->hash ())
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (9 * nano::xrb_ratio)
|
||||
.link (key3.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send2->hash (), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))
|
||||
.build_shared ();
|
||||
auto send4 = builder.make_block ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (send3->hash ())
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (8 * nano::xrb_ratio)
|
||||
.link (key3.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send3->hash (), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))
|
||||
.build_shared ();
|
||||
auto send5 = builder.make_block ()
|
||||
.account (key1.pub)
|
||||
.previous (open1->hash ())
|
||||
.representative (key1.pub)
|
||||
.balance (9 * nano::xrb_ratio)
|
||||
.link (key3.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (system.work_generate_limited (open1->hash (), nano::difficulty::from_multiplier (10, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base)))
|
||||
.build_shared ();
|
||||
auto send6 = builder.make_block ()
|
||||
.account (key1.pub)
|
||||
.previous (send5->hash ())
|
||||
.representative (key1.pub)
|
||||
.balance (8 * nano::xrb_ratio)
|
||||
.link (key3.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (system.work_generate_limited (send5->hash (), nano::difficulty::from_multiplier (10, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base)))
|
||||
.build_shared ();
|
||||
auto send7 = builder.make_block ()
|
||||
.account (key2.pub)
|
||||
.previous (open2->hash ())
|
||||
.representative (key2.pub)
|
||||
.balance (9 * nano::xrb_ratio)
|
||||
.link (key3.pub)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (system.work_generate_limited (open2->hash (), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))
|
||||
.build_shared ();
|
||||
auto send8 = builder.make_block ()
|
||||
.account (key2.pub)
|
||||
.previous (send7->hash ())
|
||||
.representative (key2.pub)
|
||||
.balance (8 * nano::xrb_ratio)
|
||||
.link (key3.pub)
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (system.work_generate_limited (send7->hash (), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))
|
||||
.build_shared ();
|
||||
|
||||
node1.process_active (send3); // genesis
|
||||
node1.process_active (send5); // key1
|
||||
node1.process_active (send7); // key2
|
||||
node1.process_active (send4); // genesis
|
||||
node1.process_active (send6); // key1
|
||||
node1.process_active (send8); // key2
|
||||
nano::blocks_confirm (node1, { send3, send4, send5, send6, send7, send8 });
|
||||
|
||||
ASSERT_TIMELY (10s, node1.active.size () == 6);
|
||||
|
||||
// Check adjusted difficulty
|
||||
nano::lock_guard<std::mutex> lock (node1.active.mutex);
|
||||
node1.active.update_adjusted_multiplier ();
|
||||
double last_adjusted (0.0);
|
||||
for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i)
|
||||
{
|
||||
//first root has nothing to compare
|
||||
if (last_adjusted != 0.0)
|
||||
{
|
||||
ASSERT_LE (i->adjusted_multiplier, last_adjusted);
|
||||
}
|
||||
last_adjusted = i->adjusted_multiplier;
|
||||
}
|
||||
ASSERT_LT (node1.active.roots.find (send4->qualified_root ())->adjusted_multiplier, node1.active.roots.find (send3->qualified_root ())->adjusted_multiplier);
|
||||
ASSERT_LT (node1.active.roots.find (send6->qualified_root ())->adjusted_multiplier, node1.active.roots.find (send5->qualified_root ())->adjusted_multiplier);
|
||||
ASSERT_LT (node1.active.roots.find (send8->qualified_root ())->adjusted_multiplier, node1.active.roots.find (send7->qualified_root ())->adjusted_multiplier);
|
||||
}
|
||||
|
||||
TEST (active_transactions, keep_local)
|
||||
{
|
||||
nano::system system;
|
||||
|
@ -328,129 +168,6 @@ TEST (active_transactions, keep_local)
|
|||
ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::election_drop));
|
||||
}
|
||||
|
||||
TEST (active_transactions, prioritize_chains)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (nano::get_available_port (), system.logging);
|
||||
node_config.enable_voting = false;
|
||||
node_config.active_elections_size = 4; //bound to 4, wont drop wallet created transactions, but good to test dropping remote
|
||||
// Disable frontier confirmation to allow the test to finish before
|
||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto & node1 = *system.add_node (node_config);
|
||||
nano::genesis genesis;
|
||||
nano::keypair key1, key2, key3;
|
||||
|
||||
nano::state_block_builder builder;
|
||||
auto send1 = builder.make_block ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (genesis.hash ())
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (nano::genesis_amount - 10 * nano::xrb_ratio)
|
||||
.link (key1.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (genesis.hash ()))
|
||||
.build_shared ();
|
||||
auto open1 = builder.make_block ()
|
||||
.account (key1.pub)
|
||||
.previous (0)
|
||||
.representative (key1.pub)
|
||||
.balance (10 * nano::xrb_ratio)
|
||||
.link (send1->hash ())
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (key1.pub))
|
||||
.build_shared ();
|
||||
auto send2 = builder.make_block ()
|
||||
.account (key1.pub)
|
||||
.previous (open1->hash ())
|
||||
.representative (key1.pub)
|
||||
.balance (9 * nano::xrb_ratio)
|
||||
.link (key2.pub)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (open1->hash ()))
|
||||
.build_shared ();
|
||||
auto send3 = builder.make_block ()
|
||||
.from (*send2)
|
||||
.previous (send2->hash ())
|
||||
.balance (8 * nano::xrb_ratio)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (send2->hash ()))
|
||||
.build_shared ();
|
||||
auto send4 = builder.make_block ()
|
||||
.from (*send3)
|
||||
.previous (send3->hash ())
|
||||
.balance (7 * nano::xrb_ratio)
|
||||
.sign (key1.prv, key1.pub)
|
||||
.work (*system.work.generate (send3->hash ()))
|
||||
.build_shared ();
|
||||
auto send5 = builder.make_block ()
|
||||
.from (*send1)
|
||||
.previous (send1->hash ())
|
||||
.balance (nano::genesis_amount - 20 * nano::xrb_ratio)
|
||||
.link (key2.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send1->hash ()))
|
||||
.build_shared ();
|
||||
auto send6 = builder.make_block ()
|
||||
.from (*send5)
|
||||
.previous (send5->hash ())
|
||||
.balance (nano::genesis_amount - 30 * nano::xrb_ratio)
|
||||
.link (key3.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (send5->hash ()))
|
||||
.build_shared ();
|
||||
auto open2 = builder.make_block ()
|
||||
.account (key2.pub)
|
||||
.previous (0)
|
||||
.representative (key2.pub)
|
||||
.balance (10 * nano::xrb_ratio)
|
||||
.link (send5->hash ())
|
||||
.sign (key2.prv, key2.pub)
|
||||
.work (*system.work.generate (key2.pub))
|
||||
.build_shared ();
|
||||
|
||||
auto multiplier1 (nano::normalized_multiplier (nano::difficulty::to_multiplier (open2->difficulty (), nano::work_threshold (open2->work_version (), nano::block_details (nano::epoch::epoch_0, false, true, false))), node1.network_params.network.publish_thresholds.epoch_1));
|
||||
auto multiplier2 (nano::normalized_multiplier (nano::difficulty::to_multiplier (send6->difficulty (), nano::work_threshold (open2->work_version (), nano::block_details (nano::epoch::epoch_0, true, false, false))), node1.network_params.network.publish_thresholds.epoch_1));
|
||||
|
||||
node1.process_active (send1);
|
||||
node1.process_active (open1);
|
||||
node1.process_active (send5);
|
||||
nano::blocks_confirm (node1, { send1, open1, send5 });
|
||||
ASSERT_TIMELY (10s, node1.active.size () == 3);
|
||||
while (!node1.active.empty ())
|
||||
{
|
||||
nano::lock_guard<std::mutex> active_guard (node1.active.mutex);
|
||||
if (!node1.active.roots.empty ())
|
||||
{
|
||||
node1.active.roots.begin ()->election->confirm_once ();
|
||||
}
|
||||
}
|
||||
node1.process_active (send2);
|
||||
node1.process_active (send3);
|
||||
node1.process_active (send4);
|
||||
node1.process_active (send6);
|
||||
nano::blocks_confirm (node1, { send2, send3, send4, send6 });
|
||||
ASSERT_TIMELY (10s, node1.active.size () == 4);
|
||||
std::this_thread::sleep_for (1s);
|
||||
node1.process_active (open2);
|
||||
ASSERT_TIMELY (10s, node1.active.size () == 4);
|
||||
size_t seen (0);
|
||||
{
|
||||
nano::lock_guard<std::mutex> active_guard (node1.active.mutex);
|
||||
node1.active.update_adjusted_multiplier ();
|
||||
auto it (node1.active.roots.get<1> ().begin ());
|
||||
while (!node1.active.roots.empty () && it != node1.active.roots.get<1> ().end ())
|
||||
{
|
||||
if (it->multiplier == multiplier1 || it->multiplier == multiplier2)
|
||||
{
|
||||
seen++;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
ASSERT_LT (seen, 2);
|
||||
ASSERT_EQ (node1.active.size (), 4);
|
||||
}
|
||||
|
||||
TEST (active_transactions, inactive_votes_cache)
|
||||
{
|
||||
nano::system system (1);
|
||||
|
@ -977,89 +694,8 @@ TEST (active_transactions, vote_replays)
|
|||
}
|
||||
}
|
||||
|
||||
TEST (active_transactions, activate_dependencies)
|
||||
{
|
||||
// Ensure that we attempt to backtrack if an election isn't getting confirmed and there are more uncemented blocks to start elections for
|
||||
nano::system system;
|
||||
nano::node_config config (nano::get_available_port (), system.logging);
|
||||
config.enable_voting = true;
|
||||
nano::node_flags flags;
|
||||
flags.disable_bootstrap_listener = true;
|
||||
config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||
auto node1 (system.add_node (config, flags));
|
||||
config.peering_port = nano::get_available_port ();
|
||||
auto node2 (system.add_node (config, flags));
|
||||
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
|
||||
nano::genesis genesis;
|
||||
nano::block_builder builder;
|
||||
auto block0 = builder.state ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (genesis.hash ())
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (nano::genesis_amount - nano::Gxrb_ratio)
|
||||
.link (0)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (node1->work_generate_blocking (genesis.hash ()).value ())
|
||||
.build_shared ();
|
||||
// Establish a representative
|
||||
node2->process_active (block0);
|
||||
node2->block_processor.flush ();
|
||||
ASSERT_TIMELY (10s, node1->block (block0->hash ()));
|
||||
auto block1 = builder.state ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (block0->hash ())
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (nano::genesis_amount - nano::Gxrb_ratio)
|
||||
.link (0)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (node1->work_generate_blocking (block0->hash ()).value ())
|
||||
.build ();
|
||||
// Wait for confirmation of the previous block, which tries to activate the successor
|
||||
// We want to test that behavior through activating dependencies instead
|
||||
ASSERT_TIMELY (3s, node2->block_confirmed (block0->hash ()));
|
||||
{
|
||||
auto transaction = node2->store.tx_begin_write ();
|
||||
ASSERT_EQ (nano::process_result::progress, node2->ledger.process (transaction, *block1).code);
|
||||
}
|
||||
std::shared_ptr<nano::block> block2 = builder.state ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (block1->hash ())
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (nano::genesis_amount - 2 * nano::Gxrb_ratio)
|
||||
.link (0)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (node1->work_generate_blocking (block1->hash ()).value ())
|
||||
.build ();
|
||||
node2->process_active (block2);
|
||||
node2->block_processor.flush ();
|
||||
node2->block_confirm (block2);
|
||||
ASSERT_TIMELY (10s, node1->block (block2->hash ()));
|
||||
ASSERT_NE (nullptr, node1->block (block2->hash ()));
|
||||
ASSERT_TIMELY (10s, node1->active.empty () && node2->active.empty ());
|
||||
ASSERT_TRUE (node1->block_confirmed_or_being_confirmed (node1->store.tx_begin_read (), block2->hash ()));
|
||||
ASSERT_TRUE (node2->block_confirmed_or_being_confirmed (node2->store.tx_begin_read (), block2->hash ()));
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
TEST (active_transactions, activate_dependencies_invalid)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_flags flags;
|
||||
flags.disable_request_loop = true;
|
||||
auto & node (*system.add_node (flags));
|
||||
node.active.pending_dependencies.emplace_back (nano::genesis ().open->hash (), 10);
|
||||
node.active.pending_dependencies.emplace_back (1, 1);
|
||||
node.active.pending_dependencies.emplace_back (0, -1);
|
||||
node.active.pending_dependencies.emplace_back (-1, 0);
|
||||
{
|
||||
nano::unique_lock<std::mutex> lock (node.active.mutex);
|
||||
node.active.activate_dependencies (lock);
|
||||
}
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
ASSERT_EQ (0, node.active.pending_dependencies.size ());
|
||||
}
|
||||
|
||||
// Tests that blocks are correctly cleared from the duplicate filter for unconfirmed elections
|
||||
TEST (active_transactions, dropped_cleanup)
|
||||
{
|
||||
|
|
|
@ -189,106 +189,3 @@ TEST (conflicts, reprioritize)
|
|||
ASSERT_EQ (multiplier2, existing2->multiplier);
|
||||
}
|
||||
}
|
||||
|
||||
TEST (conflicts, dependency)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config node_config (nano::get_available_port (), system.logging);
|
||||
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::xrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0));
|
||||
node1->work_generate_blocking (*send1);
|
||||
auto state_open1 (std::make_shared<nano::state_block> (key1.pub, 0, nano::dev_genesis_key.pub, nano::xrb_ratio, send1->hash (), key1.prv, key1.pub, 0));
|
||||
node1->work_generate_blocking (*state_open1);
|
||||
ASSERT_EQ (nano::process_result::progress, node1->process (*send1).code);
|
||||
ASSERT_EQ (nano::process_result::progress, node1->process (*state_open1).code);
|
||||
ASSERT_EQ (0, node1->active.size ());
|
||||
auto election1 = node1->active.insert (send1);
|
||||
node1->active.insert (state_open1);
|
||||
ASSERT_EQ (2, node1->active.size ());
|
||||
// Check dependency for send block
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (node1->active.mutex);
|
||||
ASSERT_NE (nullptr, election1.election);
|
||||
ASSERT_EQ (1, election1.election->dependent_blocks.size ());
|
||||
ASSERT_NE (election1.election->dependent_blocks.end (), election1.election->dependent_blocks.find (state_open1->hash ()));
|
||||
}
|
||||
}
|
||||
|
||||
TEST (conflicts, adjusted_multiplier)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_flags flags;
|
||||
flags.disable_request_loop = true;
|
||||
auto & node1 (*system.add_node (flags));
|
||||
nano::genesis genesis;
|
||||
nano::keypair key1;
|
||||
nano::keypair key2;
|
||||
nano::keypair key3;
|
||||
ASSERT_EQ (0, node1.active.size ());
|
||||
auto send1 (std::make_shared<nano::send_block> (genesis.hash (), key1.pub, nano::genesis_amount - 2 * nano::xrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (genesis.hash ())));
|
||||
node1.process_active (send1);
|
||||
auto send2 (std::make_shared<nano::send_block> (send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - 3 * nano::xrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1->hash ())));
|
||||
node1.process_active (send2);
|
||||
auto receive1 (std::make_shared<nano::receive_block> (send2->hash (), send2->hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send2->hash ())));
|
||||
node1.process_active (receive1);
|
||||
auto open1 (std::make_shared<nano::open_block> (send1->hash (), key1.pub, key1.pub, key1.prv, key1.pub, *system.work.generate (key1.pub)));
|
||||
node1.process_active (open1);
|
||||
auto send3 (std::make_shared<nano::state_block> (key1.pub, open1->hash (), key1.pub, nano::xrb_ratio, key2.pub, key1.prv, key1.pub, *system.work.generate (open1->hash ())));
|
||||
node1.process_active (send3);
|
||||
auto send4 (std::make_shared<nano::state_block> (key1.pub, send3->hash (), key1.pub, 0, key3.pub, key1.prv, key1.pub, *system.work.generate (send3->hash ())));
|
||||
node1.process_active (send4);
|
||||
ASSERT_EQ (node1.ledger.epoch_signer (node1.ledger.epoch_link (nano::epoch::epoch_1)), nano::dev_genesis_key.pub);
|
||||
auto open_epoch1 (std::make_shared<nano::state_block> (key2.pub, 0, 0, 0, node1.ledger.epoch_link (nano::epoch::epoch_1), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (key2.pub)));
|
||||
node1.process_active (open_epoch1);
|
||||
auto receive2 (std::make_shared<nano::state_block> (key2.pub, open_epoch1->hash (), 0, nano::xrb_ratio, send3->hash (), key2.prv, key2.pub, *system.work.generate (open_epoch1->hash ())));
|
||||
node1.process_active (receive2);
|
||||
auto open2 (std::make_shared<nano::state_block> (key3.pub, 0, key3.pub, nano::xrb_ratio, send4->hash (), key3.prv, key3.pub, *system.work.generate (key3.pub)));
|
||||
node1.process_active (open2);
|
||||
auto change1 (std::make_shared<nano::state_block> (key3.pub, open2->hash (), nano::dev_genesis_key.pub, nano::xrb_ratio, 0, key3.prv, key3.pub, *system.work.generate (open2->hash ())));
|
||||
node1.process_active (change1);
|
||||
nano::keypair key4;
|
||||
auto send5 (std::make_shared<nano::state_block> (key3.pub, change1->hash (), nano::dev_genesis_key.pub, 0, key4.pub, key3.prv, key3.pub, *system.work.generate (change1->hash ()))); // Pending for open epoch block
|
||||
node1.process_active (send5);
|
||||
nano::blocks_confirm (node1, { send1, send2, receive1, open1, send3, send4, open_epoch1, receive2, open2, change1, send5 });
|
||||
ASSERT_TIMELY (3s, node1.active.size () == 11);
|
||||
std::unordered_map<nano::block_hash, double> adjusted_multipliers;
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (node1.active.mutex);
|
||||
node1.active.update_adjusted_multiplier ();
|
||||
ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), send1->hash ());
|
||||
for (auto i (node1.active.roots.get<1> ().begin ()), n (node1.active.roots.get<1> ().end ()); i != n; ++i)
|
||||
{
|
||||
adjusted_multipliers.insert (std::make_pair (i->election->status.winner->hash (), i->adjusted_multiplier));
|
||||
}
|
||||
}
|
||||
// genesis
|
||||
ASSERT_GT (adjusted_multipliers.find (send1->hash ())->second, adjusted_multipliers.find (send2->hash ())->second);
|
||||
ASSERT_GT (adjusted_multipliers.find (send2->hash ())->second, adjusted_multipliers.find (receive1->hash ())->second);
|
||||
// key1
|
||||
ASSERT_GT (adjusted_multipliers.find (send1->hash ())->second, adjusted_multipliers.find (open1->hash ())->second);
|
||||
ASSERT_GT (adjusted_multipliers.find (open1->hash ())->second, adjusted_multipliers.find (send3->hash ())->second);
|
||||
ASSERT_GT (adjusted_multipliers.find (send3->hash ())->second, adjusted_multipliers.find (send4->hash ())->second);
|
||||
//key2
|
||||
ASSERT_GT (adjusted_multipliers.find (send3->hash ())->second, adjusted_multipliers.find (receive2->hash ())->second);
|
||||
ASSERT_GT (adjusted_multipliers.find (open_epoch1->hash ())->second, adjusted_multipliers.find (receive2->hash ())->second);
|
||||
// key3
|
||||
ASSERT_GT (adjusted_multipliers.find (send4->hash ())->second, adjusted_multipliers.find (open2->hash ())->second);
|
||||
ASSERT_GT (adjusted_multipliers.find (open2->hash ())->second, adjusted_multipliers.find (change1->hash ())->second);
|
||||
ASSERT_GT (adjusted_multipliers.find (change1->hash ())->second, adjusted_multipliers.find (send5->hash ())->second);
|
||||
// Independent elections can have higher difficulty than adjusted tree
|
||||
auto open_epoch2 (std::make_shared<nano::state_block> (key4.pub, 0, 0, 0, node1.ledger.epoch_link (nano::epoch::epoch_1), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (key4.pub, nano::difficulty::from_multiplier ((adjusted_multipliers.find (send1->hash ())->second), node1.network_params.network.publish_thresholds.base))));
|
||||
ASSERT_GT (open_epoch2->difficulty (), nano::difficulty::from_multiplier ((adjusted_multipliers.find (send1->hash ())->second), node1.network_params.network.publish_thresholds.base));
|
||||
node1.process_active (open_epoch2);
|
||||
node1.block_processor.flush ();
|
||||
node1.block_confirm (open_epoch2);
|
||||
ASSERT_TIMELY (3s, node1.active.size () == 12);
|
||||
{
|
||||
nano::lock_guard<std::mutex> guard (node1.active.mutex);
|
||||
node1.active.update_adjusted_multiplier ();
|
||||
ASSERT_EQ (node1.active.roots.size (), 12);
|
||||
ASSERT_EQ (node1.active.roots.get<1> ().begin ()->election->status.winner->hash (), open_epoch2->hash ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,161 +13,3 @@ TEST (election, construction)
|
|||
auto election = node.active.insert (genesis.open).election;
|
||||
election->transition_active ();
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
TEST (election, bisect_dependencies)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_flags flags;
|
||||
flags.disable_request_loop = true;
|
||||
auto & node = *system.add_node (flags);
|
||||
nano::genesis genesis;
|
||||
nano::confirmation_height_info conf_info;
|
||||
ASSERT_FALSE (node.store.confirmation_height_get (node.store.tx_begin_read (), nano::dev_genesis_key.pub, conf_info));
|
||||
ASSERT_EQ (1, conf_info.height);
|
||||
std::vector<std::shared_ptr<nano::block>> blocks;
|
||||
blocks.push_back (nullptr); // idx == height
|
||||
blocks.push_back (genesis.open);
|
||||
nano::block_builder builder;
|
||||
auto amount = nano::genesis_amount;
|
||||
for (int i = 0; i < 299; ++i)
|
||||
{
|
||||
auto latest = blocks.back ();
|
||||
blocks.push_back (builder.state ()
|
||||
.previous (latest->hash ())
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (--amount)
|
||||
.link (nano::dev_genesis_key.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (latest->hash ()))
|
||||
.build ());
|
||||
ASSERT_EQ (nano::process_result::progress, node.process (*blocks.back ()).code);
|
||||
}
|
||||
ASSERT_EQ (301, blocks.size ());
|
||||
ASSERT_TRUE (node.active.empty ());
|
||||
{
|
||||
auto election = node.active.insert (blocks.back ()).election;
|
||||
ASSERT_NE (nullptr, election);
|
||||
ASSERT_EQ (300, election->blocks.begin ()->second->sideband ().height);
|
||||
nano::unique_lock<std::mutex> lock (node.active.mutex);
|
||||
election->activate_dependencies ();
|
||||
node.active.activate_dependencies (lock);
|
||||
}
|
||||
// The first dependency activation also starts an election for the first unconfirmed block
|
||||
ASSERT_EQ (3, node.active.size ());
|
||||
{
|
||||
auto election = node.active.election (blocks[2]->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
ASSERT_EQ (2, election->blocks.begin ()->second->sideband ().height);
|
||||
}
|
||||
|
||||
auto check_height_and_activate_next = [&node, &blocks](uint64_t height_a) {
|
||||
auto election = node.active.election (blocks[height_a]->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
ASSERT_EQ (height_a, election->blocks.begin ()->second->sideband ().height);
|
||||
nano::unique_lock<std::mutex> lock (node.active.mutex);
|
||||
election->activate_dependencies ();
|
||||
node.active.activate_dependencies (lock);
|
||||
};
|
||||
check_height_and_activate_next (300 - 128); // ensure limited by 128 jumps
|
||||
ASSERT_EQ (4, node.active.size ());
|
||||
check_height_and_activate_next (87);
|
||||
ASSERT_EQ (5, node.active.size ());
|
||||
check_height_and_activate_next (44);
|
||||
ASSERT_EQ (6, node.active.size ());
|
||||
check_height_and_activate_next (23);
|
||||
ASSERT_EQ (7, node.active.size ());
|
||||
check_height_and_activate_next (12);
|
||||
ASSERT_EQ (8, node.active.size ());
|
||||
check_height_and_activate_next (7);
|
||||
ASSERT_EQ (9, node.active.size ());
|
||||
check_height_and_activate_next (4);
|
||||
ASSERT_EQ (10, node.active.size ());
|
||||
check_height_and_activate_next (3);
|
||||
ASSERT_EQ (10, node.active.size ()); // height 2 already inserted initially, no more blocks to activate
|
||||
check_height_and_activate_next (2);
|
||||
ASSERT_EQ (10, node.active.size ()); // conf height is 1, no more blocks to activate
|
||||
ASSERT_EQ (node.active.blocks.size (), node.active.roots.size ());
|
||||
}
|
||||
|
||||
// Tests successful dependency activation of the open block of an account, and its corresponding source
|
||||
TEST (election, dependencies_open_link)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_flags flags;
|
||||
flags.disable_request_loop = true;
|
||||
auto & node = *system.add_node (flags);
|
||||
|
||||
nano::state_block_builder builder;
|
||||
nano::keypair key;
|
||||
|
||||
// Send to key
|
||||
auto gen_send = builder.make_block ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.previous (nano::genesis_hash)
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.link (key.pub)
|
||||
.balance (nano::genesis_amount - 1)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*system.work.generate (nano::genesis_hash))
|
||||
.build ();
|
||||
// Receive from genesis
|
||||
auto key_open = builder.make_block ()
|
||||
.account (key.pub)
|
||||
.previous (0)
|
||||
.representative (key.pub)
|
||||
.link (gen_send->hash ())
|
||||
.balance (1)
|
||||
.sign (key.prv, key.pub)
|
||||
.work (*system.work.generate (key.pub))
|
||||
.build ();
|
||||
|
||||
// Send to self
|
||||
std::shared_ptr<nano::block> key_send = builder.make_block ()
|
||||
.account (key.pub)
|
||||
.previous (key_open->hash ())
|
||||
.representative (key.pub)
|
||||
.link (key.pub)
|
||||
.balance (0)
|
||||
.sign (key.prv, key.pub)
|
||||
.work (*system.work.generate (key_open->hash ()))
|
||||
.build ();
|
||||
|
||||
node.process (*gen_send);
|
||||
node.process (*key_open);
|
||||
node.process (*key_send);
|
||||
|
||||
// Insert frontier
|
||||
node.block_confirm (key_send);
|
||||
ASSERT_EQ (1, node.active.size ());
|
||||
{
|
||||
auto election = node.active.election (key_send->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
nano::unique_lock<std::mutex> lock (node.active.mutex);
|
||||
election->activate_dependencies ();
|
||||
node.active.activate_dependencies (lock);
|
||||
}
|
||||
// Must have activated the open block
|
||||
ASSERT_EQ (2, node.active.size ());
|
||||
{
|
||||
auto election = node.active.election (key_open->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
nano::unique_lock<std::mutex> lock (node.active.mutex);
|
||||
election->activate_dependencies ();
|
||||
node.active.activate_dependencies (lock);
|
||||
}
|
||||
// Must have activated the open's source block
|
||||
ASSERT_EQ (3, node.active.size ());
|
||||
{
|
||||
auto election = node.active.election (gen_send->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
nano::unique_lock<std::mutex> lock (node.active.mutex);
|
||||
election->activate_dependencies ();
|
||||
node.active.activate_dependencies (lock);
|
||||
}
|
||||
// Nothing else to activate
|
||||
ASSERT_EQ (3, node.active.size ());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3244,54 +3244,3 @@ TEST (ledger, can_vote)
|
|||
ledger.store.confirmation_height_put (transaction, nano::genesis_account, height);
|
||||
ASSERT_TRUE (ledger.can_vote (transaction, *receive2));
|
||||
}
|
||||
|
||||
TEST (ledger, backtrack)
|
||||
{
|
||||
nano::genesis genesis;
|
||||
nano::stat stats;
|
||||
nano::logger_mt logger;
|
||||
auto store = nano::make_store (logger, nano::unique_path ());
|
||||
ASSERT_TRUE (!store->init_error ());
|
||||
bool cb_hit = false;
|
||||
nano::ledger ledger (*store, stats, nano::generate_cache (), [&cb_hit]() {
|
||||
cb_hit = true;
|
||||
});
|
||||
{
|
||||
auto transaction (store->tx_begin_write ());
|
||||
store->initialize (transaction, genesis, ledger.cache);
|
||||
}
|
||||
nano::work_pool pool (std::numeric_limits<unsigned>::max ());
|
||||
std::vector<std::shared_ptr<nano::block>> blocks;
|
||||
blocks.push_back (nullptr); // idx == height
|
||||
blocks.push_back (genesis.open);
|
||||
auto amount = nano::genesis_amount;
|
||||
for (auto i = 0; i < 300; ++i)
|
||||
{
|
||||
nano::block_builder builder;
|
||||
std::error_code ec;
|
||||
auto latest = blocks.back ();
|
||||
blocks.push_back (builder.state ()
|
||||
.previous (latest->hash ())
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
.representative (nano::dev_genesis_key.pub)
|
||||
.balance (--amount)
|
||||
.link (nano::dev_genesis_key.pub)
|
||||
.sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub)
|
||||
.work (*pool.generate (latest->hash ()))
|
||||
.build (ec));
|
||||
ASSERT_FALSE (ec);
|
||||
ASSERT_EQ (nano::process_result::progress, ledger.process (store->tx_begin_write (), *blocks.back ()).code);
|
||||
}
|
||||
ASSERT_EQ (302, blocks.size ());
|
||||
ASSERT_EQ (301, blocks[301]->sideband ().height);
|
||||
auto transaction (store->tx_begin_read ());
|
||||
auto block_100 = ledger.backtrack (transaction, blocks[300], 200);
|
||||
ASSERT_NE (nullptr, block_100);
|
||||
ASSERT_EQ (*block_100, *blocks[100]);
|
||||
ASSERT_NE (nullptr, ledger.backtrack (transaction, blocks[10], 10));
|
||||
ASSERT_NE (ledger.backtrack (transaction, blocks[10], 1), ledger.backtrack (transaction, blocks[11], 2));
|
||||
ASSERT_EQ (ledger.backtrack (transaction, blocks[1], 0), ledger.backtrack (transaction, blocks[1], 1));
|
||||
ASSERT_NE (ledger.backtrack (transaction, blocks[2], 0), ledger.backtrack (transaction, blocks[2], 1));
|
||||
ASSERT_EQ (nullptr, ledger.backtrack (transaction, nullptr, 0));
|
||||
ASSERT_EQ (nullptr, ledger.backtrack (transaction, nullptr, 10));
|
||||
}
|
||||
|
|
|
@ -2513,7 +2513,6 @@ TEST (node, block_confirm)
|
|||
auto & node2 (*system.nodes[1]);
|
||||
nano::genesis genesis;
|
||||
nano::keypair key;
|
||||
system.wallet (1)->insert_adhoc (nano::dev_genesis_key.prv);
|
||||
nano::state_block_builder builder;
|
||||
auto send1 = builder.make_block ()
|
||||
.account (nano::dev_genesis_key.pub)
|
||||
|
@ -2533,6 +2532,16 @@ TEST (node, block_confirm)
|
|||
ASSERT_TIMELY (5s, node1.ledger.block_exists (send1->hash ()) && node2.ledger.block_exists (send1_copy->hash ()));
|
||||
ASSERT_TRUE (node1.ledger.block_exists (send1->hash ()));
|
||||
ASSERT_TRUE (node2.ledger.block_exists (send1_copy->hash ()));
|
||||
// Confirm send1 on node2 so it can vote for send2
|
||||
node2.block_confirm (send1_copy);
|
||||
{
|
||||
auto election = node2.active.election (send1_copy->qualified_root ());
|
||||
ASSERT_NE (nullptr, election);
|
||||
nano::lock_guard<std::mutex> guard (node2.active.mutex);
|
||||
election->confirm_once ();
|
||||
}
|
||||
ASSERT_TIMELY (3s, node2.block_confirmed (send1_copy->hash ()) && node2.active.empty ());
|
||||
system.wallet (1)->insert_adhoc (nano::dev_genesis_key.prv);
|
||||
auto send2 (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, send1->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio * 2, key.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (send1->hash ())));
|
||||
{
|
||||
auto transaction (node1.store.tx_begin_write ());
|
||||
|
@ -2542,9 +2551,9 @@ TEST (node, block_confirm)
|
|||
auto transaction (node2.store.tx_begin_write ());
|
||||
ASSERT_EQ (nano::process_result::progress, node2.ledger.process (transaction, *send2).code);
|
||||
}
|
||||
node1.block_confirm (send2);
|
||||
ASSERT_TRUE (node1.active.list_recently_cemented ().empty ());
|
||||
ASSERT_TIMELY (10s, !node1.active.list_recently_cemented ().empty ());
|
||||
node1.block_confirm (send2);
|
||||
ASSERT_TIMELY (10s, node1.active.list_recently_cemented ().size () == 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4197,7 +4206,7 @@ TEST (node, dependency_graph)
|
|||
}
|
||||
|
||||
// Confirm a complex dependency graph starting from a frontier
|
||||
TEST (node, dependency_graph_frontier)
|
||||
TEST (node, DISABLED_dependency_graph_frontier)
|
||||
{
|
||||
nano::system system;
|
||||
nano::node_config config (nano::get_available_port (), system.logging);
|
||||
|
|
|
@ -273,7 +273,6 @@ void nano::active_transactions::request_confirm (nano::unique_lock<std::mutex> &
|
|||
solicitor.flush ();
|
||||
generator_session.flush ();
|
||||
lock_a.lock ();
|
||||
activate_dependencies (lock_a);
|
||||
|
||||
// This is updated after the loop to ensure slow machines don't do the full check often
|
||||
if (check_all_elections_l)
|
||||
|
@ -312,100 +311,6 @@ void nano::active_transactions::frontiers_confirmation (nano::unique_lock<std::m
|
|||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::activate_dependencies (nano::unique_lock<std::mutex> & lock_a)
|
||||
{
|
||||
debug_assert (lock_a.owns_lock ());
|
||||
decltype (pending_dependencies) pending_l;
|
||||
pending_l.swap (pending_dependencies);
|
||||
lock_a.unlock ();
|
||||
|
||||
auto first_unconfirmed = [this](nano::transaction const & transaction_a, nano::account const & account_a, nano::block_hash const & confirmed_frontier_a) {
|
||||
if (!confirmed_frontier_a.is_zero ())
|
||||
{
|
||||
return this->node.store.block_successor (transaction_a, confirmed_frontier_a);
|
||||
}
|
||||
else
|
||||
{
|
||||
nano::account_info account_info_l;
|
||||
auto error = node.store.account_get (transaction_a, account_a, account_info_l);
|
||||
(void)error;
|
||||
debug_assert (!error);
|
||||
return account_info_l.open_block;
|
||||
}
|
||||
};
|
||||
|
||||
// Store blocks to activate when the lock is re-acquired, adding the hash of the original election as a dependency
|
||||
std::vector<std::pair<std::shared_ptr<nano::block>, nano::block_hash>> activate_l;
|
||||
{
|
||||
auto transaction = node.store.tx_begin_read ();
|
||||
for (auto const & entry_l : pending_l)
|
||||
{
|
||||
auto const & hash_l (entry_l.first);
|
||||
auto const block_l (node.store.block_get (transaction, hash_l));
|
||||
if (block_l)
|
||||
{
|
||||
auto const height_l (entry_l.second);
|
||||
auto const previous_hash_l (block_l->previous ());
|
||||
if (!previous_hash_l.is_zero ())
|
||||
{
|
||||
/* Insert first unconfirmed block (pessimistic) and bisect the chain (likelihood) */
|
||||
auto const account (node.store.block_account_calculated (*block_l));
|
||||
nano::confirmation_height_info conf_info_l;
|
||||
if (!node.store.confirmation_height_get (transaction, account, conf_info_l))
|
||||
{
|
||||
if (height_l > conf_info_l.height + 1)
|
||||
{
|
||||
auto const successor_hash_l = first_unconfirmed (transaction, account, conf_info_l.frontier);
|
||||
if (!confirmation_height_processor.is_processing_block (successor_hash_l))
|
||||
{
|
||||
auto const successor_l = node.store.block_get (transaction, successor_hash_l);
|
||||
debug_assert (successor_l != nullptr);
|
||||
if (successor_l != nullptr)
|
||||
{
|
||||
activate_l.emplace_back (successor_l, hash_l);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (height_l > conf_info_l.height + 2)
|
||||
{
|
||||
auto const jumps_l = std::min<uint64_t> (128, (height_l - conf_info_l.height) / 2);
|
||||
auto const backtracked_l (node.ledger.backtrack (transaction, block_l, jumps_l));
|
||||
if (backtracked_l != nullptr)
|
||||
{
|
||||
activate_l.emplace_back (backtracked_l, hash_l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If previous block not existing/not commited yet, block_source can cause segfault for state blocks
|
||||
So source check can be done only if previous != nullptr or previous is 0 (open account) */
|
||||
if (previous_hash_l.is_zero () || node.ledger.block_exists (previous_hash_l))
|
||||
{
|
||||
auto source_hash_l (node.ledger.block_source (transaction, *block_l));
|
||||
if (!source_hash_l.is_zero () && source_hash_l != previous_hash_l && blocks.find (source_hash_l) == blocks.end ())
|
||||
{
|
||||
auto source_l (node.store.block_get (transaction, source_hash_l));
|
||||
if (source_l != nullptr && !node.block_confirmed_or_being_confirmed (transaction, source_hash_l))
|
||||
{
|
||||
activate_l.emplace_back (source_l, hash_l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lock_a.lock ();
|
||||
for (auto const & entry_l : activate_l)
|
||||
{
|
||||
auto election = insert_impl (entry_l.first);
|
||||
if (election.inserted)
|
||||
{
|
||||
election.election->transition_active ();
|
||||
election.election->dependent_blocks.insert (entry_l.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::request_loop ()
|
||||
{
|
||||
nano::unique_lock<std::mutex> lock (mutex);
|
||||
|
@ -424,7 +329,6 @@ void nano::active_transactions::request_loop ()
|
|||
// Account for the time spent in request_confirm by defining the wakeup point beforehand
|
||||
const auto wakeup_l (std::chrono::steady_clock::now () + std::chrono::milliseconds (node.network_params.network.request_interval_ms));
|
||||
|
||||
update_adjusted_multiplier ();
|
||||
// frontiers_confirmation should be above update_active_multiplier to ensure new sorted roots are updated
|
||||
frontiers_confirmation (lock);
|
||||
update_active_multiplier (lock);
|
||||
|
@ -642,9 +546,8 @@ nano::election_insertion_result nano::active_transactions::insert_impl (std::sha
|
|||
double multiplier (normalized_multiplier (*block_a));
|
||||
bool prioritized = roots.size () < prioritized_cutoff || multiplier > last_prioritized_multiplier.value_or (0);
|
||||
result.election = nano::make_shared<nano::election> (node, block_a, confirmation_action_a, prioritized);
|
||||
roots.get<tag_root> ().emplace (nano::active_transactions::conflict_info{ root, multiplier, multiplier, result.election, epoch, previous_balance });
|
||||
roots.get<tag_root> ().emplace (nano::active_transactions::conflict_info{ root, multiplier, result.election, epoch, previous_balance });
|
||||
blocks.emplace (hash, result.election);
|
||||
add_adjust_difficulty (hash);
|
||||
result.election->insert_inactive_votes_cache (hash);
|
||||
node.stats.inc (nano::stat::type::election, prioritized ? nano::stat::detail::election_priority : nano::stat::detail::election_non_priority);
|
||||
}
|
||||
|
@ -835,7 +738,6 @@ bool nano::active_transactions::update_difficulty_impl (nano::active_transaction
|
|||
roots.get<tag_root> ().modify (root_it_a, [multiplier](nano::active_transactions::conflict_info & info_a) {
|
||||
info_a.multiplier = multiplier;
|
||||
});
|
||||
add_adjust_difficulty (block_a.hash ());
|
||||
node.stats.inc (nano::stat::type::election, nano::stat::detail::election_difficulty_update);
|
||||
}
|
||||
return error;
|
||||
|
@ -922,97 +824,6 @@ double nano::active_transactions::normalized_multiplier (nano::block const & blo
|
|||
return multiplier;
|
||||
}
|
||||
|
||||
void nano::active_transactions::add_adjust_difficulty (nano::block_hash const & hash_a)
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
adjust_difficulty_list.push_back (hash_a);
|
||||
}
|
||||
|
||||
void nano::active_transactions::update_adjusted_multiplier ()
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
std::unordered_set<nano::block_hash> processed_blocks;
|
||||
while (!adjust_difficulty_list.empty ())
|
||||
{
|
||||
auto const & adjust_difficulty_item (adjust_difficulty_list.front ());
|
||||
std::deque<std::pair<nano::block_hash, int64_t>> remaining_blocks;
|
||||
remaining_blocks.emplace_back (adjust_difficulty_item, 0);
|
||||
adjust_difficulty_list.pop_front ();
|
||||
std::vector<std::pair<nano::qualified_root, int64_t>> elections_list;
|
||||
double sum (0.);
|
||||
int64_t highest_level (0);
|
||||
int64_t lowest_level (0);
|
||||
while (!remaining_blocks.empty ())
|
||||
{
|
||||
auto const & item (remaining_blocks.front ());
|
||||
auto hash (item.first);
|
||||
auto level (item.second);
|
||||
if (processed_blocks.find (hash) == processed_blocks.end ())
|
||||
{
|
||||
auto existing (blocks.find (hash));
|
||||
if (existing != blocks.end () && !existing->second->confirmed () && existing->second->status.winner->hash () == hash)
|
||||
{
|
||||
auto previous (existing->second->status.winner->previous ());
|
||||
if (!previous.is_zero ())
|
||||
{
|
||||
remaining_blocks.emplace_back (previous, level + 1);
|
||||
}
|
||||
auto source (existing->second->status.winner->source ());
|
||||
if (!source.is_zero () && source != previous)
|
||||
{
|
||||
remaining_blocks.emplace_back (source, level + 1);
|
||||
}
|
||||
auto link (existing->second->status.winner->link ());
|
||||
if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous)
|
||||
{
|
||||
remaining_blocks.emplace_back (link, level + 1);
|
||||
}
|
||||
for (auto & dependent_block : existing->second->dependent_blocks)
|
||||
{
|
||||
remaining_blocks.emplace_back (dependent_block, level - 1);
|
||||
}
|
||||
processed_blocks.insert (hash);
|
||||
nano::qualified_root root (previous, existing->second->status.winner->root ());
|
||||
auto existing_root (roots.get<tag_root> ().find (root));
|
||||
if (existing_root != roots.get<tag_root> ().end ())
|
||||
{
|
||||
sum += existing_root->multiplier;
|
||||
elections_list.emplace_back (root, level);
|
||||
if (level > highest_level)
|
||||
{
|
||||
highest_level = level;
|
||||
}
|
||||
else if (level < lowest_level)
|
||||
{
|
||||
lowest_level = level;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remaining_blocks.pop_front ();
|
||||
}
|
||||
if (!elections_list.empty ())
|
||||
{
|
||||
double avg_multiplier = sum / elections_list.size ();
|
||||
double min_unit = 32.0 * avg_multiplier * std::numeric_limits<double>::epsilon ();
|
||||
debug_assert (min_unit > 0);
|
||||
|
||||
// Set adjusted multiplier
|
||||
for (auto & item : elections_list)
|
||||
{
|
||||
auto existing_root (roots.get<tag_root> ().find (item.first));
|
||||
double multiplier_a = avg_multiplier + (double)item.second * min_unit;
|
||||
if (existing_root->adjusted_multiplier != multiplier_a)
|
||||
{
|
||||
roots.get<tag_root> ().modify (existing_root, [multiplier_a](nano::active_transactions::conflict_info & info_a) {
|
||||
info_a.adjusted_multiplier = multiplier_a;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::active_transactions::update_active_multiplier (nano::unique_lock<std::mutex> & lock_a)
|
||||
{
|
||||
debug_assert (!mutex.try_lock ());
|
||||
|
@ -1028,7 +839,7 @@ void nano::active_transactions::update_active_multiplier (nano::unique_lock<std:
|
|||
{
|
||||
if (!it->election->confirmed ())
|
||||
{
|
||||
prioritized.push_back (it->adjusted_multiplier);
|
||||
prioritized.push_back (it->multiplier);
|
||||
}
|
||||
}
|
||||
if (prioritized.size () > 10 || (node.network_params.network.is_dev_network () && !prioritized.empty ()))
|
||||
|
@ -1125,7 +936,6 @@ void nano::active_transactions::erase (nano::block const & block_a)
|
|||
if (root_it != roots.get<tag_root> ().end ())
|
||||
{
|
||||
root_it->election->cleanup ();
|
||||
root_it->election->adjust_dependent_difficulty ();
|
||||
roots.get<tag_root> ().erase (root_it);
|
||||
lock.unlock ();
|
||||
node.logger.try_log (boost::str (boost::format ("Election erased for block block %1% root %2%") % block_a.hash ().to_string () % block_a.root ().to_string ()));
|
||||
|
|
|
@ -117,7 +117,6 @@ class active_transactions final
|
|||
public:
|
||||
nano::qualified_root root;
|
||||
double multiplier;
|
||||
double adjusted_multiplier;
|
||||
std::shared_ptr<nano::election> election;
|
||||
nano::epoch epoch;
|
||||
nano::uint128_t previous_balance;
|
||||
|
@ -142,7 +141,7 @@ public:
|
|||
mi::hashed_unique<mi::tag<tag_root>,
|
||||
mi::member<conflict_info, nano::qualified_root, &conflict_info::root>>,
|
||||
mi::ordered_non_unique<mi::tag<tag_difficulty>,
|
||||
mi::member<conflict_info, double, &conflict_info::adjusted_multiplier>,
|
||||
mi::member<conflict_info, double, &conflict_info::multiplier>,
|
||||
std::greater<double>>>>;
|
||||
// clang-format on
|
||||
ordered_roots roots;
|
||||
|
@ -169,8 +168,6 @@ public:
|
|||
// Returns false if the election was restarted
|
||||
bool restart (std::shared_ptr<nano::block> const &, nano::write_transaction const &);
|
||||
double normalized_multiplier (nano::block const &, boost::optional<roots_iterator> const & = boost::none) const;
|
||||
void add_adjust_difficulty (nano::block_hash const &);
|
||||
void update_adjusted_multiplier ();
|
||||
void update_active_multiplier (nano::unique_lock<std::mutex> &);
|
||||
uint64_t active_difficulty ();
|
||||
uint64_t limited_active_difficulty (nano::block const &);
|
||||
|
@ -229,8 +226,6 @@ private:
|
|||
void frontiers_confirmation (nano::unique_lock<std::mutex> &);
|
||||
nano::account next_frontier_account{ 0 };
|
||||
std::chrono::steady_clock::time_point next_frontier_check{ std::chrono::steady_clock::now () };
|
||||
void activate_dependencies (nano::unique_lock<std::mutex> &);
|
||||
std::vector<std::pair<nano::block_hash, uint64_t>> pending_dependencies;
|
||||
nano::condition_variable condition;
|
||||
bool started{ false };
|
||||
std::atomic<bool> stopped{ false };
|
||||
|
@ -273,7 +268,6 @@ private:
|
|||
void prioritize_account_for_confirmation (prioritize_num_uncemented &, size_t &, nano::account const &, nano::account_info const &, uint64_t);
|
||||
static size_t constexpr max_priority_cementable_frontiers{ 100000 };
|
||||
static size_t constexpr confirmed_frontiers_max_pending_size{ 10000 };
|
||||
std::deque<nano::block_hash> adjust_difficulty_list;
|
||||
// clang-format off
|
||||
using ordered_cache = boost::multi_index_container<nano::inactive_cache_information,
|
||||
mi::indexed_by<
|
||||
|
@ -289,15 +283,12 @@ private:
|
|||
friend class election;
|
||||
friend std::unique_ptr<container_info_component> collect_container_info (active_transactions &, const std::string &);
|
||||
|
||||
friend class active_transactions_activate_dependencies_invalid_Test;
|
||||
friend class active_transactions_dropped_cleanup_dev;
|
||||
friend class active_transactions_vote_replays_Test;
|
||||
friend class confirmation_height_prioritize_frontiers_Test;
|
||||
friend class confirmation_height_prioritize_frontiers_overwrite_Test;
|
||||
friend class active_transactions_confirmation_consistency_Test;
|
||||
friend class node_deferred_dependent_elections_Test;
|
||||
friend class election_bisect_dependencies_Test;
|
||||
friend class election_dependencies_open_link_Test;
|
||||
};
|
||||
|
||||
std::unique_ptr<container_info_component> collect_container_info (active_transactions & active_transactions, const std::string & name);
|
||||
|
|
|
@ -9,7 +9,6 @@ using namespace std::chrono;
|
|||
|
||||
int constexpr nano::election::passive_duration_factor;
|
||||
int constexpr nano::election::active_request_count_min;
|
||||
int constexpr nano::election::active_broadcasting_duration_factor;
|
||||
int constexpr nano::election::confirmed_duration_factor;
|
||||
|
||||
std::chrono::milliseconds nano::election::base_latency () const
|
||||
|
@ -33,7 +32,6 @@ root (block_a->root ())
|
|||
{
|
||||
last_votes.emplace (node.network_params.random.not_an_account, nano::vote_info{ std::chrono::steady_clock::now (), 0, block_a->hash () });
|
||||
blocks.emplace (block_a->hash (), block_a);
|
||||
update_dependent ();
|
||||
}
|
||||
|
||||
void nano::election::confirm_once (nano::election_status_type type_a)
|
||||
|
@ -58,7 +56,6 @@ void nano::election::confirm_once (nano::election_status_type type_a)
|
|||
node.background ([node_l, status_l, confirmation_action_l]() {
|
||||
confirmation_action_l (status_l.winner);
|
||||
});
|
||||
adjust_dependent_difficulty ();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,18 +89,6 @@ bool nano::election::valid_change (nano::election::state_t expected_a, nano::ele
|
|||
}
|
||||
break;
|
||||
case nano::election::state_t::broadcasting:
|
||||
switch (desired_a)
|
||||
{
|
||||
case nano::election::state_t::backtracking:
|
||||
case nano::election::state_t::confirmed:
|
||||
case nano::election::state_t::expired_unconfirmed:
|
||||
result = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case nano::election::state_t::backtracking:
|
||||
switch (desired_a)
|
||||
{
|
||||
case nano::election::state_t::confirmed:
|
||||
|
@ -178,12 +163,6 @@ bool nano::election::confirmed () const
|
|||
return state_m == nano::election::state_t::confirmed || state_m == nano::election::state_t::expired_confirmed;
|
||||
}
|
||||
|
||||
void nano::election::activate_dependencies ()
|
||||
{
|
||||
debug_assert (!node.active.mutex.try_lock ());
|
||||
node.active.pending_dependencies.emplace_back (status.winner->hash (), height);
|
||||
}
|
||||
|
||||
void nano::election::broadcast_block (nano::confirmation_solicitor & solicitor_a)
|
||||
{
|
||||
if (base_latency () * 15 < std::chrono::steady_clock::now () - last_block)
|
||||
|
@ -216,15 +195,6 @@ bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a
|
|||
}
|
||||
break;
|
||||
case nano::election::state_t::broadcasting:
|
||||
broadcast_block (solicitor_a);
|
||||
send_confirm_req (solicitor_a);
|
||||
if (base_latency () * active_broadcasting_duration_factor < std::chrono::steady_clock::now () - state_start)
|
||||
{
|
||||
state_change (nano::election::state_t::broadcasting, nano::election::state_t::backtracking);
|
||||
activate_dependencies ();
|
||||
}
|
||||
break;
|
||||
case nano::election::state_t::backtracking:
|
||||
broadcast_block (solicitor_a);
|
||||
send_confirm_req (solicitor_a);
|
||||
break;
|
||||
|
@ -303,8 +273,6 @@ void nano::election::confirm_if_quorum ()
|
|||
status.winner = block_l;
|
||||
remove_votes (status_winner_hash_l);
|
||||
node.block_processor.force (block_l);
|
||||
update_dependent ();
|
||||
node.active.add_adjust_difficulty (winner_hash_l);
|
||||
}
|
||||
if (have_quorum (tally_l, sum))
|
||||
{
|
||||
|
@ -433,47 +401,6 @@ size_t nano::election::last_votes_size ()
|
|||
return last_votes.size ();
|
||||
}
|
||||
|
||||
void nano::election::update_dependent ()
|
||||
{
|
||||
debug_assert (!node.active.mutex.try_lock ());
|
||||
std::vector<nano::block_hash> blocks_search;
|
||||
auto hash (status.winner->hash ());
|
||||
auto previous (status.winner->previous ());
|
||||
if (!previous.is_zero ())
|
||||
{
|
||||
blocks_search.push_back (previous);
|
||||
}
|
||||
auto source (status.winner->source ());
|
||||
if (!source.is_zero () && source != previous)
|
||||
{
|
||||
blocks_search.push_back (source);
|
||||
}
|
||||
auto link (status.winner->link ());
|
||||
if (!link.is_zero () && !node.ledger.is_epoch_link (link) && link != previous)
|
||||
{
|
||||
blocks_search.push_back (link);
|
||||
}
|
||||
for (auto & block_search : blocks_search)
|
||||
{
|
||||
auto existing (node.active.blocks.find (block_search));
|
||||
if (existing != node.active.blocks.end () && !existing->second->confirmed ())
|
||||
{
|
||||
if (existing->second->dependent_blocks.find (hash) == existing->second->dependent_blocks.end ())
|
||||
{
|
||||
existing->second->dependent_blocks.insert (hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void nano::election::adjust_dependent_difficulty ()
|
||||
{
|
||||
for (auto & dependent_block : dependent_blocks)
|
||||
{
|
||||
node.active.add_adjust_difficulty (dependent_block);
|
||||
}
|
||||
}
|
||||
|
||||
void nano::election::cleanup ()
|
||||
{
|
||||
bool unconfirmed (!confirmed ());
|
||||
|
|
|
@ -42,14 +42,12 @@ private: // State management
|
|||
passive, // only listening for incoming votes
|
||||
active, // actively request confirmations
|
||||
broadcasting, // request confirmations and broadcast the winner
|
||||
backtracking, // start an election for unconfirmed dependent blocks
|
||||
confirmed, // confirmed but still listening for votes
|
||||
expired_confirmed,
|
||||
expired_unconfirmed
|
||||
};
|
||||
static int constexpr passive_duration_factor = 5;
|
||||
static int constexpr active_request_count_min = 2;
|
||||
static int constexpr active_broadcasting_duration_factor = 30;
|
||||
static int constexpr confirmed_duration_factor = 5;
|
||||
std::atomic<nano::election::state_t> state_m = { state_t::passive };
|
||||
|
||||
|
@ -63,7 +61,6 @@ private: // State management
|
|||
bool state_change (nano::election::state_t, nano::election::state_t);
|
||||
void broadcast_block (nano::confirmation_solicitor &);
|
||||
void send_confirm_req (nano::confirmation_solicitor &);
|
||||
void activate_dependencies ();
|
||||
// Calculate votes for local representatives
|
||||
void generate_votes ();
|
||||
void remove_votes (nano::block_hash const &);
|
||||
|
@ -81,8 +78,6 @@ public:
|
|||
void log_votes (nano::tally_t const &) const;
|
||||
bool publish (std::shared_ptr<nano::block> block_a);
|
||||
size_t last_votes_size ();
|
||||
void update_dependent ();
|
||||
void adjust_dependent_difficulty ();
|
||||
size_t insert_inactive_votes_cache (nano::block_hash const &);
|
||||
bool prioritized () const;
|
||||
void prioritize_election (nano::vote_generator_session &);
|
||||
|
@ -105,14 +100,10 @@ public:
|
|||
nano::election_status status;
|
||||
unsigned confirmation_request_count{ 0 };
|
||||
std::unordered_map<nano::block_hash, nano::uint128_t> last_tally;
|
||||
std::unordered_set<nano::block_hash> dependent_blocks;
|
||||
std::chrono::seconds late_blocks_delay{ 5 };
|
||||
uint64_t const height;
|
||||
nano::root const root;
|
||||
|
||||
friend class active_transactions;
|
||||
|
||||
friend class election_bisect_dependencies_Test;
|
||||
friend class election_dependencies_open_link_Test;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1197,19 +1197,6 @@ std::shared_ptr<nano::block> nano::ledger::forked_block (nano::transaction const
|
|||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<nano::block> nano::ledger::backtrack (nano::transaction const & transaction_a, std::shared_ptr<nano::block> const & start_a, uint64_t jumps_a)
|
||||
{
|
||||
auto block = start_a;
|
||||
while (jumps_a > 0 && block != nullptr && !block->previous ().is_zero ())
|
||||
{
|
||||
block = store.block_get (transaction_a, block->previous ());
|
||||
debug_assert (block != nullptr);
|
||||
--jumps_a;
|
||||
}
|
||||
debug_assert (block == nullptr || block->previous ().is_zero () || jumps_a == 0);
|
||||
return block;
|
||||
}
|
||||
|
||||
bool nano::ledger::block_confirmed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
|
||||
{
|
||||
auto confirmed (false);
|
||||
|
|
|
@ -25,7 +25,6 @@ public:
|
|||
nano::uint128_t weight (nano::account const &);
|
||||
std::shared_ptr<nano::block> successor (nano::transaction const &, nano::qualified_root const &);
|
||||
std::shared_ptr<nano::block> forked_block (nano::transaction const &, nano::block const &);
|
||||
std::shared_ptr<nano::block> backtrack (nano::transaction const &, std::shared_ptr<nano::block> const &, uint64_t);
|
||||
bool block_confirmed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const;
|
||||
bool block_not_confirmed_or_not_exists (nano::block const & block_a) const;
|
||||
nano::block_hash latest (nano::transaction const &, nano::account const &);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue