diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 0a95577add..506bbdd034 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -115,6 +115,7 @@ TEST (active_transactions, adjusted_multiplier_priority) node1.process_active (send2); // genesis node1.process_active (open1); // key1 node1.process_active (open2); // key2 + nano::blocks_confirm (node1, { send1, send2, open1, open2 }); system.deadline_set (10s); while (node1.active.size () != 4) { @@ -162,6 +163,7 @@ TEST (active_transactions, adjusted_multiplier_priority) 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 }); system.deadline_set (10s); while (node1.active.size () != 6) @@ -265,6 +267,7 @@ TEST (active_transactions, prioritize_chains) node1.process_active (send1); node1.process_active (open1); node1.process_active (send5); + nano::blocks_confirm (node1, { send1, open1, send5 }); system.deadline_set (10s); while (node1.active.size () != 3) { @@ -282,7 +285,7 @@ TEST (active_transactions, prioritize_chains) node1.process_active (send3); node1.process_active (send4); node1.process_active (send6); - + nano::blocks_confirm (node1, { send2, send3, send4, send6 }); system.deadline_set (10s); while (node1.active.size () != 4) { @@ -551,7 +554,7 @@ TEST (active_transactions, vote_replays) ASSERT_NE (nullptr, open1); node.process_active (send1); node.process_active (open1); - node.block_processor.flush (); + nano::blocks_confirm (node, { send1, open1 }); ASSERT_EQ (2, node.active.size ()); // First vote is not a replay and confirms the election, second vote should be a replay since the election has confirmed but not yet removed auto vote_send1 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, send1)); @@ -573,7 +576,7 @@ TEST (active_transactions, vote_replays) auto send2 (std::make_shared (key.pub, open1->hash (), key.pub, nano::Gxrb_ratio - 1, key.pub, key.prv, key.pub, *system.work.generate (open1->hash ()))); ASSERT_NE (nullptr, send2); node.process_active (send2); - node.block_processor.flush (); + nano::blocks_confirm (node, { send2 }); ASSERT_EQ (1, node.active.size ()); auto vote1_send2 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 0, send2)); auto vote2_send2 (std::make_shared (key.pub, key.prv, 0, send2)); @@ -660,6 +663,7 @@ TEST (active_transactions, activate_dependencies) .build (); node2->process_active (block2); node2->block_processor.flush (); + node2->block_confirm (block2); system.deadline_set (10s); while (node1->block (block2->hash ()) == nullptr) { @@ -991,6 +995,19 @@ TEST (active_transactions, election_difficulty_update_fork) ASSERT_EQ (nano::process_result::progress, node.process (*send1).code); ASSERT_EQ (nano::process_result::progress, node.process (*open1).code); ASSERT_EQ (nano::process_result::progress, node.process (*send2).code); + // Confirm blocks so far to allow starting elections for upcoming blocks + for (auto block : { open1, send2 }) + { + node.block_confirm (block); + { + auto election = node.active.election (block->qualified_root ()); + ASSERT_NE (nullptr, election); + nano::lock_guard guard (node.active.mutex); + election->confirm_once (); + } + ASSERT_TIMELY (2s, node.block_confirmed (block->hash ())); + node.active.erase (*block); + } // Verify an election with multiple blocks is correctly updated on arrival of another block // Each subsequent block has difficulty at least higher than the previous one diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index e5adee1c8c..f42996393d 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -154,6 +154,7 @@ TEST (confirmation_height, multiple_accounts) auto receive3 = std::make_shared (open3.hash (), send6.hash (), key3.prv, key3.pub, *system.work.generate (open3.hash ())); node->process_active (receive3); node->block_processor.flush (); + node->block_confirm (receive3); { auto election = node->active.election (receive3->qualified_root ()); ASSERT_NE (nullptr, election); @@ -358,6 +359,7 @@ TEST (confirmation_height, gap_live) // Now complete the chain where the block comes in on the live network node->process_active (open1); node->block_processor.flush (); + node->block_confirm (open1); { auto election = node->active.election (open1->qualified_root ()); ASSERT_NE (nullptr, election); @@ -447,6 +449,7 @@ TEST (confirmation_height, send_receive_between_2_accounts) node->process_active (receive4); node->block_processor.flush (); + node->block_confirm (receive4); { auto election = node->active.election (receive4->qualified_root ()); ASSERT_NE (nullptr, election); @@ -1237,7 +1240,7 @@ TEST (confirmation_height, callback_confirmed_history) node->process_active (send1); node->block_processor.flush (); - + node->block_confirm (send1); { node->process_active (send); node->block_processor.flush (); diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 26ccf2a343..522e5df765 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -219,8 +219,10 @@ TEST (conflicts, dependency) TEST (conflicts, adjusted_multiplier) { - nano::system system (1); - auto & node1 (*system.nodes[0]); + 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; @@ -250,7 +252,7 @@ TEST (conflicts, adjusted_multiplier) nano::keypair key4; auto send5 (std::make_shared (key3.pub, change1->hash (), nano::test_genesis_key.pub, 0, key4.pub, key3.prv, key3.pub, *system.work.generate (change1->hash ()))); // Pending for open epoch block node1.process_active (send5); - node1.block_processor.flush (); + nano::blocks_confirm (node1, { send1, send2, receive1, open1, send3, send4, open_epoch1, receive2, open2, change1, send5 }); system.deadline_set (3s); while (node1.active.size () != 11) { @@ -285,6 +287,7 @@ TEST (conflicts, adjusted_multiplier) 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); system.deadline_set (3s); while (node1.active.size () != 12) { diff --git a/nano/core_test/gap_cache.cpp b/nano/core_test/gap_cache.cpp index f3318225a1..766c993d25 100644 --- a/nano/core_test/gap_cache.cpp +++ b/nano/core_test/gap_cache.cpp @@ -63,38 +63,38 @@ TEST (gap_cache, comparison) ASSERT_EQ (arrival, cache.blocks.get<1> ().begin ()->arrival); } +// Upon receiving enough votes for a gapped block, a lazy bootstrap should be initiated TEST (gap_cache, gap_bootstrap) { - nano::system system (2); + nano::node_flags node_flags; + node_flags.disable_legacy_bootstrap = true; + node_flags.disable_request_loop = true; // to avoid fallback behavior of broadcasting blocks + nano::system system (2, nano::transport::transport_type::tcp, node_flags); + auto & node1 (*system.nodes[0]); auto & node2 (*system.nodes[1]); nano::block_hash latest (node1.latest (nano::test_genesis_key.pub)); nano::keypair key; auto send (std::make_shared (latest, key.pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (latest))); - { - nano::block_post_events events; - auto transaction (node1.store.tx_begin_write ()); - ASSERT_EQ (nano::process_result::progress, node1.block_processor.process_one (transaction, events, send).code); - } + node1.process (*send); ASSERT_EQ (nano::genesis_amount - 100, node1.balance (nano::genesis_account)); ASSERT_EQ (nano::genesis_amount, node2.balance (nano::genesis_account)); + // Confirm send block, allowing voting on the upcoming block + node1.block_confirm (send); + { + auto election = node1.active.election (send->qualified_root ()); + ASSERT_NE (nullptr, election); + nano::lock_guard guard (node1.active.mutex); + election->confirm_once (); + } + ASSERT_TIMELY (2s, node1.block_confirmed (send->hash ())); + node1.active.erase (*send); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - system.wallet (0)->insert_adhoc (key.prv); auto latest_block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key.pub, 100)); ASSERT_NE (nullptr, latest_block); ASSERT_EQ (nano::genesis_amount - 200, node1.balance (nano::genesis_account)); ASSERT_EQ (nano::genesis_amount, node2.balance (nano::genesis_account)); - system.deadline_set (10s); - { - // The separate publish and vote system doesn't work very well here because it's instantly confirmed. - // We help it get the block and vote out here. - auto transaction (node1.store.tx_begin_read ()); - node1.network.flood_block (latest_block); - } - while (node2.balance (nano::genesis_account) != nano::genesis_amount - 200) - { - ASSERT_NO_ERROR (system.poll ()); - } + ASSERT_TIMELY (10s, node2.balance (nano::genesis_account) == nano::genesis_amount - 200); } TEST (gap_cache, two_dependencies) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 4e610c429d..5de3c44c84 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -930,8 +930,9 @@ TEST (votes, add_old_different_account) node1.work_generate_blocking (*send1); auto send2 (std::make_shared (send1->hash (), key1.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); node1.work_generate_blocking (*send2); - ASSERT_EQ (nano::process_result::progress, node1.process_local (send1).code); - ASSERT_EQ (nano::process_result::progress, node1.process_local (send2).code); + ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code); + ASSERT_EQ (nano::process_result::progress, node1.process (*send2).code); + nano::blocks_confirm (node1, { send1, send2 }); auto election1 = node1.active.election (send1->qualified_root ()); ASSERT_NE (nullptr, election1); auto election2 = node1.active.election (send2->qualified_root ()); @@ -2662,10 +2663,11 @@ TEST (ledger, block_hash_account_conflict) node1.work_generate_blocking (*receive1); node1.work_generate_blocking (*send2); node1.work_generate_blocking (*open_epoch1); - ASSERT_EQ (nano::process_result::progress, node1.process_local (send1).code); - ASSERT_EQ (nano::process_result::progress, node1.process_local (receive1).code); - ASSERT_EQ (nano::process_result::progress, node1.process_local (send2).code); - ASSERT_EQ (nano::process_result::progress, node1.process_local (open_epoch1).code); + ASSERT_EQ (nano::process_result::progress, node1.process (*send1).code); + ASSERT_EQ (nano::process_result::progress, node1.process (*receive1).code); + ASSERT_EQ (nano::process_result::progress, node1.process (*send2).code); + ASSERT_EQ (nano::process_result::progress, node1.process (*open_epoch1).code); + nano::blocks_confirm (node1, { send1, receive1, send2, open_epoch1 }); auto election1 = node1.active.election (send1->qualified_root ()); ASSERT_NE (nullptr, election1); auto election2 = node1.active.election (receive1->qualified_root ()); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index e558687056..8024e2421f 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -1396,6 +1396,7 @@ TEST (node, fork_multi_flip) std::vector types{ nano::transport::transport_type::tcp, nano::transport::transport_type::udp }; for (auto & type : types) { + nano::system system; nano::node_flags node_flags; if (type == nano::transport::transport_type::udp) { @@ -1403,9 +1404,11 @@ TEST (node, fork_multi_flip) node_flags.disable_bootstrap_listener = true; node_flags.disable_udp = false; } - nano::system system (2, type, node_flags); - auto & node1 (*system.nodes[0]); - auto & node2 (*system.nodes[1]); + 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, node_flags, type)); + node_config.peering_port = nano::get_available_port (); + auto & node2 (*system.add_node (node_config, node_flags, type)); ASSERT_EQ (1, node1.network.size ()); nano::keypair key1; nano::genesis genesis; @@ -1417,12 +1420,12 @@ TEST (node, fork_multi_flip) auto send3 (std::make_shared (publish2.block->hash (), key2.pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (publish2.block->hash ()))); nano::publish publish3 (send3); node1.network.process_message (publish1, node1.network.udp_channels.create (node1.network.endpoint ())); - node1.block_processor.flush (); node2.network.process_message (publish2, node2.network.udp_channels.create (node2.network.endpoint ())); node2.network.process_message (publish3, node2.network.udp_channels.create (node2.network.endpoint ())); + node1.block_processor.flush (); node2.block_processor.flush (); ASSERT_EQ (1, node1.active.size ()); - ASSERT_EQ (2, node2.active.size ()); + ASSERT_EQ (1, node2.active.size ()); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); node1.network.process_message (publish2, node1.network.udp_channels.create (node1.network.endpoint ())); node1.network.process_message (publish3, node1.network.udp_channels.create (node1.network.endpoint ())); @@ -1527,16 +1530,31 @@ TEST (node, fork_open) auto channel1 (node1.network.udp_channels.create (node1.network.endpoint ())); node1.network.process_message (publish1, channel1); node1.block_processor.flush (); + { + auto election = node1.active.election (publish1.block->qualified_root ()); + nano::lock_guard guard (node1.active.mutex); + election->confirm_once (); + } + ASSERT_TIMELY (3s, node1.active.empty () && node1.block_confirmed (publish1.block->hash ())); auto open1 (std::make_shared (publish1.block->hash (), 1, key1.pub, key1.prv, key1.pub, *system.work.generate (key1.pub))); nano::publish publish2 (open1); node1.network.process_message (publish2, channel1); node1.block_processor.flush (); + ASSERT_EQ (1, node1.active.size ()); auto open2 (std::make_shared (publish1.block->hash (), 2, key1.pub, key1.prv, key1.pub, *system.work.generate (key1.pub))); nano::publish publish3 (open2); - ASSERT_EQ (2, node1.active.size ()); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); node1.network.process_message (publish3, channel1); node1.block_processor.flush (); + { + auto election = node1.active.election (publish3.block->qualified_root ()); + nano::lock_guard guard (node1.active.mutex); + ASSERT_EQ (2, election->blocks.size ()); + ASSERT_EQ (publish2.block->hash (), election->status.winner->hash ()); + ASSERT_FALSE (election->confirmed ()); + } + ASSERT_TRUE (node1.block (publish2.block->hash ())); + ASSERT_FALSE (node1.block (publish3.block->hash ())); } TEST (node, fork_open_flip) @@ -1562,9 +1580,11 @@ TEST (node, fork_open_flip) // node1 gets copy that will remain node1.process_active (open1); node1.block_processor.flush (); + node1.block_confirm (open1); // node2 gets copy that will be evicted node2.process_active (open2); node2.block_processor.flush (); + node2.block_confirm (open2); ASSERT_EQ (2, node1.active.size ()); ASSERT_EQ (2, node2.active.size ()); system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); @@ -1996,13 +2016,17 @@ TEST (node, bootstrap_fork_open) // Both know about send0 ASSERT_EQ (nano::process_result::progress, node0->process (send0).code); ASSERT_EQ (nano::process_result::progress, node1->process (send0).code); - // Confirm send0 to allow voting on the following blocks - node0->block_confirm (node0->block (node0->latest (nano::test_genesis_key.pub))); + // Confirm send0 to allow starting and voting on the following blocks + for (auto node : system.nodes) { - auto election = node0->active.election (send0.qualified_root ()); - ASSERT_NE (nullptr, election); - nano::lock_guard guard (node0->active.mutex); - election->confirm_once (); + node->block_confirm (node->block (node->latest (nano::test_genesis_key.pub))); + { + auto election = node->active.election (send0.qualified_root ()); + ASSERT_NE (nullptr, election); + nano::lock_guard guard (node->active.mutex); + election->confirm_once (); + } + ASSERT_TIMELY (2s, node->active.empty ()); } ASSERT_TIMELY (3s, node0->block_confirmed (send0.hash ())); // They disagree about open0/open1 @@ -3081,17 +3105,13 @@ TEST (node, epoch_conflict_confirm) } node0->process_active (change); node0->process_active (epoch_open); - node0->block_processor.flush (); system.deadline_set (5s); while (!node0->block (change->hash ()) || !node0->block (epoch_open->hash ()) || !node1->block (change->hash ()) || !node1->block (epoch_open->hash ())) { ASSERT_NO_ERROR (system.poll ()); } - system.deadline_set (5s); - while (node0->active.size () != 2) - { - ASSERT_NO_ERROR (system.poll ()); - } + nano::blocks_confirm (*node0, { change, epoch_open }); + ASSERT_EQ (2, node0->active.size ()); { nano::lock_guard lock (node0->active.mutex); ASSERT_TRUE (node0->active.blocks.find (change->hash ()) != node0->active.blocks.end ()); @@ -3368,7 +3388,7 @@ TEST (node, confirm_back) node.process_active (send1); node.process_active (open); node.process_active (send2); - node.block_processor.flush (); + nano::blocks_confirm (node, { send1, open, send2 }); ASSERT_EQ (3, node.active.size ()); std::vector vote_blocks; vote_blocks.push_back (send2->hash ()); @@ -4278,6 +4298,156 @@ TEST (node, dependency_graph_frontier) ASSERT_TIMELY (5s, node1.active.empty () && node2.active.empty ()); } +namespace nano +{ +TEST (node, deferred_dependent_elections) +{ + nano::system system; + nano::node_flags flags; + flags.disable_request_loop = true; + auto & node = *system.add_node (flags); + auto & node2 = *system.add_node (flags); // node2 will be used to ensure all blocks are being propagated + + nano::state_block_builder builder; + nano::keypair key; + std::shared_ptr send1 = builder.make_block () + .account (nano::test_genesis_key.pub) + .previous (nano::genesis_hash) + .representative (nano::test_genesis_key.pub) + .link (key.pub) + .balance (nano::genesis_amount - 1) + .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) + .work (*system.work.generate (nano::genesis_hash)) + .build (); + std::shared_ptr open = builder.make_block () + .account (key.pub) + .previous (0) + .representative (key.pub) + .link (send1->hash ()) + .balance (1) + .sign (key.prv, key.pub) + .work (*system.work.generate (key.pub)) + .build (); + std::shared_ptr send2 = builder.make_block () + .from (*send1) + .previous (send1->hash ()) + .balance (send1->balance ().number () - 1) + .link (key.pub) + .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build (); + std::shared_ptr receive = builder.make_block () + .from (*open) + .previous (open->hash ()) + .link (send2->hash ()) + .balance (2) + .sign (key.prv, key.pub) + .work (*system.work.generate (open->hash ())) + .build (); + std::shared_ptr fork = builder.make_block () + .from (*receive) + .representative (nano::test_genesis_key.pub) + .sign (key.prv, key.pub) + .build (); + node.process_active (send1); + node.block_processor.flush (); + auto election_send1 = node.active.election (send1->qualified_root ()); + ASSERT_NE (nullptr, election_send1); + + // Should process and republish but not start an election for any dependent blocks + node.process_active (open); + node.process_active (send2); + node.block_processor.flush (); + ASSERT_TRUE (node.block (open->hash ())); + ASSERT_TRUE (node.block (send2->hash ())); + ASSERT_FALSE (node.active.active (open->qualified_root ())); + ASSERT_FALSE (node.active.active (send2->qualified_root ())); + ASSERT_TIMELY (2s, node2.block (open->hash ())); + ASSERT_TIMELY (2s, node2.block (send2->hash ())); + + // Re-processing older blocks with updated work also does not start an election + node.work_generate_blocking (*open, open->difficulty ()); + node.process_active (open); + node.block_processor.flush (); + ASSERT_FALSE (node.active.active (open->qualified_root ())); + + // It is however possible to manually start an election from elsewhere + node.block_confirm (open); + ASSERT_TRUE (node.active.active (open->qualified_root ())); + + // Dropping an election allows restarting it [with higher work] + node.active.erase (*open); + ASSERT_FALSE (node.active.active (open->qualified_root ())); + ASSERT_NE (std::chrono::steady_clock::time_point{}, node.active.recently_dropped.find (open->qualified_root ())); + node.process_active (open); + node.block_processor.flush (); + ASSERT_TRUE (node.active.active (open->qualified_root ())); + + // Frontier confirmation also starts elections + ASSERT_NO_ERROR (system.poll_until_true (5s, [&node, &send2] { + nano::unique_lock lock (node.active.mutex); + node.active.frontiers_confirmation (lock); + lock.unlock (); + return node.active.election (send2->qualified_root ()) != nullptr; + })); + + // Drop both elections + node.active.erase (*open); + ASSERT_FALSE (node.active.active (open->qualified_root ())); + node.active.erase (*send2); + ASSERT_FALSE (node.active.active (send2->qualified_root ())); + + // Confirming send1 will automatically start elections for the dependents + { + nano::lock_guard guard (node.active.mutex); + election_send1->confirm_once (); + } + ASSERT_TIMELY (2s, node.block_confirmed (send1->hash ())); + ASSERT_TIMELY (2s, node.active.active (open->qualified_root ()) && node.active.active (send2->qualified_root ())); + auto election_open = node.active.election (open->qualified_root ()); + ASSERT_NE (nullptr, election_open); + auto election_send2 = node.active.election (send2->qualified_root ()); + ASSERT_NE (nullptr, election_open); + + // Confirm one of the dependents of the receive but not the other, to ensure both have to be confirmed to start an election on processing + ASSERT_EQ (nano::process_result::progress, node.process (*receive).code); + ASSERT_FALSE (node.active.active (receive->qualified_root ())); + { + nano::lock_guard guard (node.active.mutex); + election_open->confirm_once (); + } + ASSERT_TIMELY (2s, node.block_confirmed (open->hash ())); + ASSERT_FALSE (node.ledger.can_vote (node.store.tx_begin_read (), *receive)); + std::this_thread::sleep_for (500ms); + ASSERT_FALSE (node.active.active (receive->qualified_root ())); + ASSERT_FALSE (node.ledger.rollback (node.store.tx_begin_write (), receive->hash ())); + ASSERT_FALSE (node.block (receive->hash ())); + node.process_active (receive); + node.block_processor.flush (); + ASSERT_TRUE (node.block (receive->hash ())); + ASSERT_FALSE (node.active.active (receive->qualified_root ())); + + // Processing a fork will also not start an election + ASSERT_EQ (nano::process_result::fork, node.process (*fork).code); + node.process_active (fork); + node.block_processor.flush (); + ASSERT_FALSE (node.active.active (receive->qualified_root ())); + + // Confirming the other dependency allows starting an election from a fork + { + nano::lock_guard guard (node.active.mutex); + election_send2->confirm_once (); + } + ASSERT_TIMELY (2s, node.block_confirmed (send2->hash ())); + ASSERT_TIMELY (2s, node.active.active (receive->qualified_root ())); + node.active.erase (*receive); + ASSERT_FALSE (node.active.active (receive->qualified_root ())); + node.process_active (fork); + node.block_processor.flush (); + ASSERT_TRUE (node.active.active (receive->qualified_root ())); +} +} + namespace { void add_required_children_node_config_tree (nano::jsonconfig & tree) diff --git a/nano/core_test/vote_processor.cpp b/nano/core_test/vote_processor.cpp index 716ce2ae60..a6317f7636 100644 --- a/nano/core_test/vote_processor.cpp +++ b/nano/core_test/vote_processor.cpp @@ -242,6 +242,7 @@ TEST (vote_processor, no_broadcast_local) ASSERT_FALSE (ec); ASSERT_EQ (nano::process_result::progress, node.process_local (send2).code); ASSERT_EQ (node.config.vote_minimum, node.weight (nano::test_genesis_key.pub)); + node.block_confirm (send2); // Process a vote auto vote2 (node.store.vote_generate (node.store.tx_begin_read (), nano::test_genesis_key.pub, nano::test_genesis_key.prv, { send2->hash () })); ASSERT_EQ (nano::vote_code::vote, node.active.vote (vote2)); @@ -269,6 +270,7 @@ TEST (vote_processor, no_broadcast_local) ASSERT_FALSE (ec); ASSERT_EQ (nano::process_result::progress, node.process_local (open).code); ASSERT_EQ (nano::genesis_amount - node.config.vote_minimum.number (), node.weight (nano::test_genesis_key.pub)); + node.block_confirm (open); // Insert account in wallet system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); node.wallets.compute_reps (); diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 56cba12d7a..31976369a3 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -278,6 +278,7 @@ class active_transactions final friend class active_transactions_confirmation_consistency_Test; friend class active_transactions_vote_generator_session_Test; friend class node_vote_by_hash_bundle_Test; + friend class node_deferred_dependent_elections_Test; friend class election_bisect_dependencies_Test; friend class election_dependencies_open_link_Test; }; diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 08628c8344..d0d8e3283f 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -295,14 +295,17 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std:: } // Start collecting quorum on block - auto election = node.active.insert (block_a, process_return_a.previous_balance.number ()); - if (election.inserted) + if (watch_work_a || node.ledger.can_vote (node.store.tx_begin_read (), *block_a)) { - election.election->transition_passive (); - } - else if (election.election) - { - election.election->try_generate_votes (block_a->hash ()); + auto election = node.active.insert (block_a, process_return_a.previous_balance.number ()); + if (election.inserted) + { + election.election->transition_passive (); + } + else if (election.election) + { + election.election->try_generate_votes (block_a->hash ()); + } } // Announce block contents to the network diff --git a/nano/node/node.cpp b/nano/node/node.cpp index dcac33eaff..2c1af35bb4 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -525,7 +525,7 @@ void nano::node::process_fork (nano::transaction const & transaction_a, std::sha if (!store.block_exists (transaction_a, block_a->type (), block_a->hash ()) && store.root_exists (transaction_a, block_a->root ())) { std::shared_ptr ledger_block (ledger.forked_block (transaction_a, *block_a)); - if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ())) + if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ()) && ledger.can_vote (transaction_a, *ledger_block)) { std::weak_ptr this_w (shared_from_this ()); auto election = active.insert (ledger_block, boost::none, [this_w, root](std::shared_ptr) { diff --git a/nano/node/testing.cpp b/nano/node/testing.cpp index 0bb20d45dc..461abec1d7 100644 --- a/nano/node/testing.cpp +++ b/nano/node/testing.cpp @@ -201,6 +201,18 @@ std::unique_ptr nano::upgrade_epoch (nano::work_pool & pool_a return !error ? std::move (epoch) : nullptr; } +void nano::blocks_confirm (nano::node & node_a, std::vector> const & blocks_a) +{ + // Finish processing all blocks + node_a.block_processor.flush (); + for (auto const & block : blocks_a) + { + // A sideband is required to start an election + debug_assert (block->has_sideband ()); + node_a.block_confirm (block); + } +} + std::unique_ptr nano::system::upgrade_genesis_epoch (nano::node & node_a, nano::epoch const epoch_a) { return upgrade_epoch (work, node_a.ledger, epoch_a); diff --git a/nano/node/testing.hpp b/nano/node/testing.hpp index 3fa7e323df..b981f0336f 100644 --- a/nano/node/testing.hpp +++ b/nano/node/testing.hpp @@ -56,5 +56,6 @@ class system final unsigned node_sequence{ 0 }; }; std::unique_ptr upgrade_epoch (nano::work_pool &, nano::ledger &, nano::epoch); +void blocks_confirm (nano::node &, std::vector> const &); } REGISTER_ERROR_CODES (nano, error_system); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index e623fee4e9..866b85aa86 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -7718,9 +7718,7 @@ TEST (rpc, block_confirmed) ASSERT_EQ (std::error_code (nano::error_blocks::not_found).message (), response1.json.get ("error")); scoped_thread_name_io.reset (); - system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); nano::keypair key; - system.wallet (0)->insert_adhoc (key.prv); // Open an account directly in the ledger { @@ -7751,15 +7749,14 @@ TEST (rpc, block_confirmed) auto send = std::make_shared (latest, key.pub, 10, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (latest)); node->process_active (send); node->block_processor.flush (); - - // Wait until the confirmation height has been set - system.deadline_set (10s); - auto transaction = node->store.tx_begin_read (); - while (!node->ledger.block_confirmed (transaction, send->hash ()) || node->confirmation_height_processor.is_processing_block (send->hash ())) + node->block_confirm (send); { - ASSERT_NO_ERROR (system.poll ()); - transaction.refresh (); + auto election = node->active.election (send->qualified_root ()); + ASSERT_NE (nullptr, election); + nano::lock_guard guard (node->active.mutex); + election->confirm_once (); } + ASSERT_TIMELY (3s, node->block_confirmed (send->hash ())); // Requesting confirmation for this should now succeed request.put ("hash", send->hash ().to_string ()); @@ -8960,7 +8957,7 @@ TEST (rpc, confirmation_active) auto send2 (std::make_shared (send1->hash (), nano::public_key (), nano::genesis_amount - 200, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ()))); node1.process_active (send1); node1.process_active (send2); - node1.block_processor.flush (); + nano::blocks_confirm (node1, { send1, send2 }); ASSERT_EQ (2, node1.active.size ()); { nano::lock_guard guard (node1.active.mutex);