diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index e935997679..fc3e1d97b5 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -308,7 +308,7 @@ TEST (active_transactions, inactive_votes_cache_existing_vote) // Insert vote auto vote1 (std::make_shared (key.pub, key.prv, 1, std::vector (1, send->hash ()))); node.vote_processor.vote (vote1, std::make_shared (node)); - ASSERT_TIMELY (5s, election->votes ().size () == 2) + ASSERT_TIMELY (5s, election->votes ().size () == 2); ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::vote_new)); auto last_vote1 (election->votes ()[key.pub]); ASSERT_EQ (send->hash (), last_vote1.hash); @@ -388,49 +388,28 @@ TEST (active_transactions, inactive_votes_cache_election_start) node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; auto & node = *system.add_node (node_config); nano::block_hash latest (node.latest (nano::dev_genesis_key.pub)); - nano::keypair key1, key2, key3, key4, key5; + nano::keypair key1, key2; nano::send_block_builder send_block_builder; nano::state_block_builder state_block_builder; auto send1 = send_block_builder.make_block () .previous (latest) .destination (key1.pub) - .balance (nano::genesis_amount - 2000 * nano::Gxrb_ratio) + .balance (nano::genesis_amount - 5000 * nano::Gxrb_ratio) .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) .work (*system.work.generate (latest)) .build_shared (); auto send2 = send_block_builder.make_block () .previous (send1->hash ()) .destination (key2.pub) - .balance (nano::genesis_amount - 4000 * nano::Gxrb_ratio) - .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) - .work (*system.work.generate (send1->hash ())) - .build_shared (); - auto send3 = send_block_builder.make_block () - .previous (send2->hash ()) - .destination (key3.pub) - .balance (nano::genesis_amount - 6000 * nano::Gxrb_ratio) - .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) - .work (*system.work.generate (send2->hash ())) - .build_shared (); - auto send4 = send_block_builder.make_block () - .previous (send3->hash ()) - .destination (key4.pub) - .balance (nano::genesis_amount - 8000 * nano::Gxrb_ratio) - .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) - .work (*system.work.generate (send3->hash ())) - .build_shared (); - auto send5 = send_block_builder.make_block () - .previous (send4->hash ()) - .destination (key5.pub) .balance (nano::genesis_amount - 10000 * nano::Gxrb_ratio) .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) - .work (*system.work.generate (send4->hash ())) + .work (*system.work.generate (send1->hash ())) .build_shared (); auto open1 = state_block_builder.make_block () .account (key1.pub) .previous (0) .representative (key1.pub) - .balance (2000 * nano::Gxrb_ratio) + .balance (5000 * nano::Gxrb_ratio) .link (send1->hash ()) .sign (key1.prv, key1.pub) .work (*system.work.generate (key1.pub)) @@ -439,107 +418,69 @@ TEST (active_transactions, inactive_votes_cache_election_start) .account (key2.pub) .previous (0) .representative (key2.pub) - .balance (2000 * nano::Gxrb_ratio) + .balance (5000 * nano::Gxrb_ratio) .link (send2->hash ()) .sign (key2.prv, key2.pub) .work (*system.work.generate (key2.pub)) .build_shared (); - auto open3 = state_block_builder.make_block () - .account (key3.pub) - .previous (0) - .representative (key3.pub) - .balance (2000 * nano::Gxrb_ratio) - .link (send3->hash ()) - .sign (key3.prv, key3.pub) - .work (*system.work.generate (key3.pub)) - .build_shared (); - auto open4 = state_block_builder.make_block () - .account (key4.pub) - .previous (0) - .representative (key4.pub) - .balance (2000 * nano::Gxrb_ratio) - .link (send4->hash ()) - .sign (key4.prv, key4.pub) - .work (*system.work.generate (key4.pub)) - .build_shared (); - auto open5 = state_block_builder.make_block () - .account (key5.pub) - .previous (0) - .representative (key5.pub) - .balance (2000 * nano::Gxrb_ratio) - .link (send5->hash ()) - .sign (key5.prv, key5.pub) - .work (*system.work.generate (key5.pub)) - .build_shared (); node.block_processor.add (send1); node.block_processor.add (send2); - node.block_processor.add (send3); - node.block_processor.add (send4); - node.block_processor.add (send5); node.block_processor.add (open1); node.block_processor.add (open2); - node.block_processor.add (open3); - node.block_processor.add (open4); - node.block_processor.add (open5); node.block_processor.flush (); - ASSERT_TIMELY (5s, 11 == node.ledger.cache.block_count); + ASSERT_TIMELY (5s, 5 == node.ledger.cache.block_count); ASSERT_TRUE (node.active.empty ()); ASSERT_EQ (1, node.ledger.cache.cemented_count); // These blocks will be processed later - auto send6 = send_block_builder.make_block () - .previous (send5->hash ()) + auto send3 = send_block_builder.make_block () + .previous (send2->hash ()) .destination (nano::keypair ().pub) - .balance (send5->balance ().number () - 1) + .balance (send2->balance ().number () - 1) .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) - .work (*system.work.generate (send5->hash ())) + .work (*system.work.generate (send2->hash ())) .build_shared (); - auto send7 = send_block_builder.make_block () - .previous (send6->hash ()) + auto send4 = send_block_builder.make_block () + .previous (send3->hash ()) .destination (nano::keypair ().pub) - .balance (send6->balance ().number () - 1) + .balance (send3->balance ().number () - 1) .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) - .work (*system.work.generate (send6->hash ())) + .work (*system.work.generate (send3->hash ())) .build_shared (); // Inactive votes - std::vector hashes{ open1->hash (), open2->hash (), open3->hash (), open4->hash (), open5->hash (), send7->hash () }; + std::vector hashes{ open1->hash (), open2->hash (), send4->hash () }; auto vote1 (std::make_shared (key1.pub, key1.prv, 0, hashes)); node.vote_processor.vote (vote1, std::make_shared (node)); - auto vote2 (std::make_shared (key2.pub, key2.prv, 0, hashes)); - node.vote_processor.vote (vote2, std::make_shared (node)); - auto vote3 (std::make_shared (key3.pub, key3.prv, 0, hashes)); - node.vote_processor.vote (vote3, std::make_shared (node)); - auto vote4 (std::make_shared (key4.pub, key4.prv, 0, hashes)); - node.vote_processor.vote (vote4, std::make_shared (node)); - ASSERT_TIMELY (5s, node.active.inactive_votes_cache_size () == 6); + ASSERT_TIMELY (5s, node.active.inactive_votes_cache_size () == 3); ASSERT_TRUE (node.active.empty ()); ASSERT_EQ (1, node.ledger.cache.cemented_count); - // 5 votes are required to start election - auto vote5 (std::make_shared (key5.pub, key5.prv, 0, hashes)); - node.vote_processor.vote (vote5, std::make_shared (node)); - ASSERT_TIMELY (5s, 5 == node.active.size ()); + // 2 votes are required to start election (dev network) + auto vote2 (std::make_shared (key2.pub, key2.prv, 0, hashes)); + node.vote_processor.vote (vote2, std::make_shared (node)); + // Only open1 & open2 blocks elections should start (send4 is missing previous block in ledger) + ASSERT_TIMELY (5s, 2 == node.active.size ()); // Confirm elections with weight quorum auto vote0 (std::make_shared (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, std::numeric_limits::max (), hashes)); // Final vote for confirmation node.vote_processor.vote (vote0, std::make_shared (node)); ASSERT_TIMELY (5s, node.active.empty ()); - ASSERT_TIMELY (5s, 11 == node.ledger.cache.cemented_count); + ASSERT_TIMELY (5s, 5 == node.ledger.cache.cemented_count); // A late block arrival also checks the inactive votes cache ASSERT_TRUE (node.active.empty ()); - auto send7_cache (node.active.find_inactive_votes_cache (send7->hash ())); - ASSERT_EQ (6, send7_cache.voters.size ()); - ASSERT_TRUE (send7_cache.status.bootstrap_started); - ASSERT_TRUE (send7_cache.status.confirmed); - ASSERT_TRUE (send7_cache.status.election_started); // already marked even though the block does not exist - node.process_active (send6); + auto send4_cache (node.active.find_inactive_votes_cache (send4->hash ())); + ASSERT_EQ (3, send4_cache.voters.size ()); + ASSERT_TRUE (send4_cache.status.bootstrap_started); + ASSERT_TRUE (send4_cache.status.confirmed); + ASSERT_TRUE (send4_cache.status.election_started); // already marked even though the block does not exist + node.process_active (send3); node.block_processor.flush (); // An election is started for send6 but does not confirm ASSERT_TIMELY (5s, 1 == node.active.size ()); node.vote_processor.flush (); - ASSERT_FALSE (node.block_confirmed_or_being_confirmed (node.store.tx_begin_read (), send6->hash ())); + ASSERT_FALSE (node.block_confirmed_or_being_confirmed (node.store.tx_begin_read (), send3->hash ())); // send7 cannot be voted on but an election should be started from inactive votes - ASSERT_FALSE (node.ledger.dependents_confirmed (node.store.tx_begin_read (), *send7)); - node.process_active (send7); + ASSERT_FALSE (node.ledger.dependents_confirmed (node.store.tx_begin_read (), *send4)); + node.process_active (send4); node.block_processor.flush (); - ASSERT_TIMELY (5s, 13 == node.ledger.cache.cemented_count); + ASSERT_TIMELY (5s, 7 == node.ledger.cache.cemented_count); } namespace nano @@ -1046,6 +987,7 @@ TEST (active_transactions, restart_dropped) ASSERT_EQ (0, node.active.size ()); node.process_active (send); node.block_processor.flush (); + node.scheduler.flush (); ASSERT_EQ (1, node.active.size ()); ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::election_restart)); auto ledger_block (node.store.block_get (node.store.tx_begin_read (), send->hash ())); @@ -1058,6 +1000,7 @@ TEST (active_transactions, restart_dropped) // Try to restart election with the same difficulty node.process_active (send); node.block_processor.flush (); + node.scheduler.flush (); ASSERT_EQ (0, node.active.size ()); ASSERT_EQ (1, node.stats.count (nano::stat::type::election, nano::stat::detail::election_restart)); // Generate even higher difficulty work @@ -1068,6 +1011,7 @@ TEST (active_transactions, restart_dropped) ASSERT_EQ (0, node.active.size ()); node.process_active (send); node.block_processor.flush (); + node.scheduler.flush (); ASSERT_EQ (1, node.active.size ()); ASSERT_EQ (1, node.ledger.cache.cemented_count); ASSERT_EQ (2, node.stats.count (nano::stat::type::election, nano::stat::detail::election_restart)); diff --git a/nano/core_test/confirmation_height.cpp b/nano/core_test/confirmation_height.cpp index 671804b1bd..56dd5aa698 100644 --- a/nano/core_test/confirmation_height.cpp +++ b/nano/core_test/confirmation_height.cpp @@ -304,17 +304,18 @@ TEST (confirmation_height, gap_live) node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; auto node = system.add_node (node_config, node_flags); node_config.peering_port = nano::get_available_port (); + node_config.receive_minimum = nano::genesis_amount; // Prevent auto-receive & open1/receive1/receive2 blocks conflicts system.add_node (node_config, node_flags); nano::keypair destination; system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv); system.wallet (1)->insert_adhoc (destination.prv); nano::genesis genesis; - auto send1 (std::make_shared (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, destination.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); + auto send1 (std::make_shared (nano::genesis_account, genesis.hash (), nano::genesis_account, nano::genesis_amount - 1, destination.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); node->work_generate_blocking (*send1); - auto send2 (std::make_shared (nano::genesis_account, send1->hash (), nano::genesis_account, nano::genesis_amount - 2 * nano::Gxrb_ratio, destination.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); + auto send2 (std::make_shared (nano::genesis_account, send1->hash (), nano::genesis_account, nano::genesis_amount - 2, destination.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); node->work_generate_blocking (*send2); - auto send3 (std::make_shared (nano::genesis_account, send2->hash (), nano::genesis_account, nano::genesis_amount - 3 * nano::Gxrb_ratio, destination.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); + auto send3 (std::make_shared (nano::genesis_account, send2->hash (), nano::genesis_account, nano::genesis_amount - 3, destination.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); node->work_generate_blocking (*send3); auto open1 (std::make_shared (send1->hash (), destination.pub, destination.pub, destination.prv, destination.pub, 0)); diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 2fe1ce8349..1bbb1a8c45 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -40,11 +40,12 @@ TEST (conflicts, add_existing) auto send2 (std::make_shared (genesis.hash (), key2.pub, 0, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, 0)); send2->sideband_set ({}); node1.scheduler.activate (nano::dev_genesis_key.pub, node1.store.tx_begin_read ()); + node1.scheduler.flush (); auto election1 = node1.active.election (send2->qualified_root ()); + ASSERT_NE (nullptr, election1); ASSERT_EQ (1, node1.active.size ()); auto vote1 (std::make_shared (key2.pub, key2.prv, 0, send2)); node1.active.vote (vote1); - ASSERT_NE (nullptr, election1); ASSERT_EQ (2, election1->votes ().size ()); auto votes (election1->votes ()); ASSERT_NE (votes.end (), votes.find (key2.pub)); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 0c4244fe8d..e1b0e9d714 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -4055,6 +4055,99 @@ TEST (node, rollback_vote_self) ASSERT_EQ (fork->hash (), vote->second.hash); } +TEST (node, rollback_gap_source) +{ + nano::system system; + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + auto & node = *system.add_node (node_config); + nano::state_block_builder builder; + nano::keypair key; + auto send1 = 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_shared (); + auto fork = 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_shared (); + auto send2 = builder.make_block () + .from (*send1) + .previous (send1->hash ()) + .balance (send1->balance ().number () - 1) + .link (key.pub) + .sign (nano::dev_genesis_key.prv, nano::dev_genesis_key.pub) + .work (*system.work.generate (send1->hash ())) + .build_shared (); + auto open = builder.make_block () + .from (*fork) + .link (send2->hash ()) + .sign (key.prv, key.pub) + .build_shared (); + ASSERT_EQ (nano::process_result::progress, node.process (*send1).code); + ASSERT_EQ (nano::process_result::progress, node.process (*fork).code); + // Node has fork & doesn't have source for correct block open (send2) + ASSERT_EQ (nullptr, node.block (send2->hash ())); + // Start election for fork + nano::blocks_confirm (node, { fork }); + { + auto election = node.active.election (fork->qualified_root ()); + ASSERT_NE (nullptr, election); + // Process conflicting block for election + node.process_active (open); + node.block_processor.flush (); + ASSERT_EQ (2, election->blocks ().size ()); + ASSERT_EQ (1, election->votes ().size ()); + // Confirm open + auto vote1 (std::make_shared (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, std::numeric_limits::max (), std::vector (1, open->hash ()))); + node.vote_processor.vote (vote1, std::make_shared (node)); + ASSERT_TIMELY (5s, election->votes ().size () == 2); + ASSERT_TIMELY (3s, election->confirmed ()); + } + // Wait for the rollback (attempt to replace fork with open) + ASSERT_TIMELY (5s, node.stats.count (nano::stat::type::rollback, nano::stat::detail::open) == 1); + ASSERT_TIMELY (5s, node.active.empty ()); + // But replacing is not possible (missing source block - send2) + node.block_processor.flush (); + ASSERT_EQ (nullptr, node.block (open->hash ())); + ASSERT_EQ (nullptr, node.block (fork->hash ())); + // Fork can be returned by some other forked node or attacker + node.process_active (fork); + node.block_processor.flush (); + ASSERT_NE (nullptr, node.block (fork->hash ())); + // With send2 block in ledger election can start again to remove fork block + ASSERT_EQ (nano::process_result::progress, node.process (*send2).code); + nano::blocks_confirm (node, { fork }); + { + auto election = node.active.election (fork->qualified_root ()); + ASSERT_NE (nullptr, election); + // Process conflicting block for election + node.process_active (open); + node.block_processor.flush (); + ASSERT_EQ (2, election->blocks ().size ()); + // Confirm open (again) + auto vote1 (std::make_shared (nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, std::numeric_limits::max (), std::vector (1, open->hash ()))); + node.vote_processor.vote (vote1, std::make_shared (node)); + ASSERT_TIMELY (5s, election->votes ().size () == 2); + } + // Wait for new rollback + ASSERT_TIMELY (5s, node.stats.count (nano::stat::type::rollback, nano::stat::detail::open) == 2); + // Now fork block should be replaced with open + node.block_processor.flush (); + ASSERT_NE (nullptr, node.block (open->hash ())); + ASSERT_EQ (nullptr, node.block (fork->hash ())); +} + // Confirm a complex dependency graph starting from the first block TEST (node, dependency_graph) { diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 97cce2c220..6fa31b080f 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -53,7 +53,6 @@ void nano::election::confirm_once (nano::unique_lock & lock_a, nano status.type = type_a; auto const status_l = status; lock_a.unlock (); - node.active.add_recently_confirmed (status_l.winner->qualified_root (), status_l.winner->hash ()); node.process_confirmed (status_l); node.background ([node_l = node.shared (), status_l, confirmation_action_l = confirmation_action] () { if (confirmation_action_l) diff --git a/nano/node/node.cpp b/nano/node/node.cpp index d87af0f933..4662eb2abd 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -1353,6 +1353,7 @@ void nano::node::process_confirmed (nano::election_status const & status_a, uint const auto num_iters = (config.block_processor_batch_max_time / network_params.node.process_confirmed_interval) * 4; if (auto block_l = ledger.store.block_get (ledger.store.tx_begin_read (), hash)) { + active.add_recently_confirmed (block_l->qualified_root (), hash); confirmation_height_processor.add (block_l); } else if (iteration_a < num_iters) diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index e2b2df364a..1e7273e813 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -118,7 +118,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const work_peers_l->push_back (boost::str (boost::format ("%1%:%2%") % i->first % i->second)); } - auto preconfigured_peers_l (toml.create_array ("preconfigured_peers", "A list of \"address\" (hostname or ip address) entries to identify preconfigured peers.")); + auto preconfigured_peers_l (toml.create_array ("preconfigured_peers", "A list of \"address\" (hostname or ipv6 notation ip address) entries to identify preconfigured peers.")); for (auto i (preconfigured_peers.begin ()), n (preconfigured_peers.end ()); i != n; ++i) { preconfigured_peers_l->push_back (*i);