diff --git a/.github/workflows/code_sanitizers.yml b/.github/workflows/code_sanitizers.yml index c82c31b280..4c8d632f76 100644 --- a/.github/workflows/code_sanitizers.yml +++ b/.github/workflows/code_sanitizers.yml @@ -15,8 +15,12 @@ jobs: - { name: UBSAN, ignore_errors: false } - { name: ASAN, ignore_errors: false, leak_check: false } - { name: ASAN_INT, ignore_errors: true, leak_check: false } - - { name: TSAN, ignore_errors: true } + - { name: TSAN, ignore_errors: false } - { name: LEAK, ignore_errors: true, leak_check: true } + exclude: + # Bug when running with TSAN: "ThreadSanitizer: CHECK failed: sanitizer_deadlock_detector" + - BACKEND: rocksdb + SANITIZER: { name: TSAN } runs-on: ubuntu-22.04 env: COMPILER: ${{ matrix.COMPILER }} @@ -72,7 +76,7 @@ jobs: SANITIZER: - { name: UBSAN, ignore_errors: false } - { name: ASAN, ignore_errors: false } - - { name: TSAN, ignore_errors: true } + - { name: TSAN, ignore_errors: false } runs-on: macos-14 env: COMPILER: ${{ matrix.COMPILER }} diff --git a/nano/core_test/CMakeLists.txt b/nano/core_test/CMakeLists.txt index df6ef3ccd8..6a7013660a 100644 --- a/nano/core_test/CMakeLists.txt +++ b/nano/core_test/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable( election_scheduler.cpp enums.cpp epochs.cpp + fair_queue.cpp frontiers_confirmation.cpp ipc.cpp ledger.cpp diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 734eeeb705..086195ef74 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -149,7 +149,7 @@ TEST (active_transactions, confirm_frontier) std::shared_ptr election2; ASSERT_TIMELY (5s, election2 = node2.active.election (send->qualified_root ())); ASSERT_TIMELY (5s, nano::test::confirmed (node2, { send })); - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 2); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 2); ASSERT_TIMELY (5s, node2.active.empty ()); ASSERT_GT (election2->confirmation_request_count, 0u); } @@ -486,9 +486,9 @@ TEST (inactive_votes_cache, election_start) ASSERT_EQ (nano::block_status::progress, node.process (send2)); ASSERT_EQ (nano::block_status::progress, node.process (open1)); ASSERT_EQ (nano::block_status::progress, node.process (open2)); - ASSERT_TIMELY_EQ (5s, 5, node.ledger.cache.block_count); + ASSERT_TIMELY_EQ (5s, 5, node.ledger.block_count ()); ASSERT_TRUE (node.active.empty ()); - ASSERT_EQ (1, node.ledger.cache.cemented_count); + ASSERT_EQ (1, node.ledger.cemented_count ()); // These blocks will be processed later auto send3 = send_block_builder.make_block () .previous (send2->hash ()) @@ -510,7 +510,7 @@ TEST (inactive_votes_cache, election_start) node.vote_processor.vote (vote1, std::make_shared (node, node)); ASSERT_TIMELY_EQ (5s, node.vote_cache.size (), 3); ASSERT_TRUE (node.active.empty ()); - ASSERT_EQ (1, node.ledger.cache.cemented_count); + ASSERT_EQ (1, node.ledger.cemented_count ()); // 2 votes are required to start election (dev network) auto vote2 = nano::test::make_vote (key2, { open1, open2, send4 }); @@ -523,7 +523,7 @@ TEST (inactive_votes_cache, election_start) auto vote0 = nano::test::make_final_vote (nano::dev::genesis_key, { open1, open2, send4 }); node.vote_processor.vote (vote0, std::make_shared (node, node)); ASSERT_TIMELY_EQ (5s, 0, node.active.size ()); - ASSERT_TIMELY_EQ (5s, 5, node.ledger.cache.cemented_count); + ASSERT_TIMELY_EQ (5s, 5, node.ledger.cemented_count ()); ASSERT_TRUE (nano::test::confirmed (node, { send1, send2, open1, open2 })); // A late block arrival also checks the inactive votes cache @@ -536,7 +536,7 @@ TEST (inactive_votes_cache, election_start) // 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 (), *send4)); node.process_active (send4); - ASSERT_TIMELY_EQ (5s, 7, node.ledger.cache.cemented_count); + ASSERT_TIMELY_EQ (5s, 7, node.ledger.cemented_count ()); } namespace nano @@ -829,8 +829,8 @@ TEST (active_transactions, fork_filter_cleanup) // how about node1 picking up "send1" from node2? we know it does because we assert at // the end that it is within node1's AEC, but why node1.block_count doesn't increase? // - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.block_count, 2); - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.block_count, 2); + ASSERT_TIMELY_EQ (5s, node2.ledger.block_count (), 2); + ASSERT_TIMELY_EQ (5s, node1.ledger.block_count (), 2); // Block is erased from the duplicate filter ASSERT_TIMELY (5s, node1.network.publish_filter.apply (send_block_bytes.data (), send_block_bytes.size ())); @@ -890,7 +890,7 @@ TEST (active_transactions, fork_replacement_tally) auto vote = nano::test::make_final_vote (nano::dev::genesis_key, { send, open }); node1.vote_processor.vote (vote, std::make_shared (node1, node1)); } - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.cemented_count, 1 + 2 * reps_count); + ASSERT_TIMELY_EQ (5s, node1.ledger.cemented_count (), 1 + 2 * reps_count); nano::keypair key; auto send_last = builder.make_block () @@ -1050,8 +1050,8 @@ TEST (active_transactions, confirm_new) // Let node2 know about the block ASSERT_TIMELY (5s, node2.block (send->hash ())); // Wait confirmation - ASSERT_TIMELY (5s, node1.ledger.cache.cemented_count == 2); - ASSERT_TIMELY (5s, node2.ledger.cache.cemented_count == 2); + ASSERT_TIMELY (5s, node1.ledger.cemented_count () == 2); + ASSERT_TIMELY (5s, node2.ledger.cemented_count () == 2); } // Ensures votes are tallied on election::publish even if no vote is inserted through inactive_votes_cache diff --git a/nano/core_test/block_store.cpp b/nano/core_test/block_store.cpp index 42106adcbd..c1cf35ab34 100644 --- a/nano/core_test/block_store.cpp +++ b/nano/core_test/block_store.cpp @@ -480,7 +480,6 @@ TEST (block_store, frontier_retrieval) nano::account account1{}; nano::account_info info1 (0, 0, 0, 0, 0, 0, nano::epoch::epoch_0); auto transaction (store->tx_begin_write ()); - store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) }); store->account.put (transaction, account1, info1); nano::account_info info2; store->account.get (transaction, account1, info2); @@ -604,10 +603,6 @@ TEST (block_store, latest_find) nano::account account2 (3); nano::block_hash hash2 (4); auto transaction (store->tx_begin_write ()); - store->confirmation_height.put (transaction, account1, { 0, nano::block_hash (0) }); - store->account.put (transaction, account1, { hash1, account1, hash1, 100, 0, 300, nano::epoch::epoch_0 }); - store->confirmation_height.put (transaction, account2, { 0, nano::block_hash (0) }); - store->account.put (transaction, account2, { hash2, account2, hash2, 200, 0, 400, nano::epoch::epoch_0 }); auto first (store->account.begin (transaction)); auto second (store->account.begin (transaction)); ++second; @@ -768,7 +763,6 @@ TEST (block_store, latest_exists) nano::account two (2); nano::account_info info; auto transaction (store->tx_begin_write ()); - store->confirmation_height.put (transaction, two, { 0, nano::block_hash (0) }); store->account.put (transaction, two, info); nano::account one (1); ASSERT_FALSE (store->account.exists (transaction, one)); @@ -786,7 +780,6 @@ TEST (block_store, large_iteration) nano::account account; nano::random_pool::generate_block (account.bytes.data (), account.bytes.size ()); accounts1.insert (account); - store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) }); store->account.put (transaction, account, nano::account_info ()); } std::unordered_set accounts2; @@ -889,7 +882,6 @@ TEST (block_store, account_count) auto transaction (store->tx_begin_write ()); ASSERT_EQ (0, store->account.count (transaction)); nano::account account (200); - store->confirmation_height.put (transaction, account, { 0, nano::block_hash (0) }); store->account.put (transaction, account, nano::account_info ()); } auto transaction (store->tx_begin_read ()); @@ -902,9 +894,10 @@ TEST (block_store, cemented_count_cache) auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants); ASSERT_TRUE (!store->init_error ()); auto transaction (store->tx_begin_write ()); - nano::ledger_cache ledger_cache{ store->rep_weight }; - store->initialize (transaction, ledger_cache, nano::dev::constants); - ASSERT_EQ (1, ledger_cache.cemented_count); + nano::stats stats; + nano::ledger ledger (*store, stats, nano::dev::constants); + store->initialize (transaction, ledger.cache, nano::dev::constants); + ASSERT_EQ (1, ledger.cemented_count ()); } TEST (block_store, block_random) diff --git a/nano/core_test/bootstrap.cpp b/nano/core_test/bootstrap.cpp index 22c48327ad..39214953ee 100644 --- a/nano/core_test/bootstrap.cpp +++ b/nano/core_test/bootstrap.cpp @@ -652,6 +652,7 @@ TEST (bootstrap_processor, push_diamond_pruning) { auto transaction (node1->store.tx_begin_write ()); + node1->ledger.confirm (transaction, open->hash ()); ASSERT_EQ (1, node1->ledger.pruning_action (transaction, send1->hash (), 2)); ASSERT_EQ (1, node1->ledger.pruning_action (transaction, open->hash (), 1)); ASSERT_TRUE (node1->ledger.block_exists (transaction, nano::dev::genesis->hash ())); @@ -661,8 +662,8 @@ TEST (bootstrap_processor, push_diamond_pruning) ASSERT_TRUE (node1->store.pruned.exists (transaction, open->hash ())); ASSERT_TRUE (node1->ledger.block_exists (transaction, send2->hash ())); ASSERT_TRUE (node1->ledger.block_exists (transaction, receive->hash ())); - ASSERT_EQ (2, node1->ledger.cache.pruned_count); - ASSERT_EQ (5, node1->ledger.cache.block_count); + ASSERT_EQ (2, node1->ledger.pruned_count ()); + ASSERT_EQ (5, node1->ledger.block_count ()); } // 2nd bootstrap @@ -973,22 +974,22 @@ TEST (bootstrap_processor, lazy_hash_pruning) ASSERT_TIMELY (5s, node1->block_confirmed (change1->hash ())); ASSERT_TIMELY (5s, node1->block_confirmed (change2->hash ())); ASSERT_TIMELY (5s, node1->active.empty ()); - ASSERT_EQ (5, node1->ledger.cache.block_count); - ASSERT_EQ (5, node1->ledger.cache.cemented_count); + ASSERT_EQ (5, node1->ledger.block_count ()); + ASSERT_EQ (5, node1->ledger.cemented_count ()); // Pruning action node1->ledger_pruning (2, false); - ASSERT_EQ (9, node0->ledger.cache.block_count); - ASSERT_EQ (0, node0->ledger.cache.pruned_count); - ASSERT_EQ (5, node1->ledger.cache.block_count); - ASSERT_EQ (3, node1->ledger.cache.pruned_count); + ASSERT_EQ (9, node0->ledger.block_count ()); + ASSERT_EQ (0, node0->ledger.pruned_count ()); + ASSERT_EQ (5, node1->ledger.block_count ()); + ASSERT_EQ (3, node1->ledger.pruned_count ()); // Start lazy bootstrap with last block in chain known nano::test::establish_tcp (system, *node1, node0->network.endpoint ()); node1->bootstrap_initiator.bootstrap_lazy (receive3->hash (), true); // Check processed blocks - ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 9); + ASSERT_TIMELY_EQ (5s, node1->ledger.block_count (), 9); ASSERT_TIMELY (5s, node1->balance (key2.pub) != 0); ASSERT_TIMELY (5s, !node1->bootstrap_initiator.in_progress ()); } @@ -1369,14 +1370,14 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) // Confirm last block to prune previous ASSERT_TRUE (nano::test::start_elections (system, *node1, { send1, send2, open, state_open }, true)); ASSERT_TIMELY (5s, nano::test::confirmed (*node1, { send2, open, state_open })); - ASSERT_EQ (5, node1->ledger.cache.block_count); - ASSERT_EQ (5, node1->ledger.cache.cemented_count); + ASSERT_EQ (5, node1->ledger.block_count ()); + ASSERT_EQ (5, node1->ledger.cemented_count ()); // Pruning action, send1 should get pruned - ASSERT_EQ (0, node1->ledger.cache.pruned_count); + ASSERT_EQ (0, node1->ledger.pruned_count ()); node1->ledger_pruning (2, false); - ASSERT_EQ (1, node1->ledger.cache.pruned_count); - ASSERT_EQ (5, node1->ledger.cache.block_count); + ASSERT_EQ (1, node1->ledger.pruned_count ()); + ASSERT_EQ (5, node1->ledger.block_count ()); ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send1->hash ())); ASSERT_TRUE (nano::test::exists (*node1, { send2, open, state_open })); @@ -1392,7 +1393,7 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) ASSERT_TIMELY (5s, lazy_attempt->stopped || lazy_attempt->requeued_pulls >= 4); // Some blocks cannot be retrieved from pruned node - ASSERT_EQ (1, node2->ledger.cache.block_count); + ASSERT_EQ (1, node2->ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_none_exists (*node2, { send1, send2, open, state_open })); { auto transaction (node2->store.tx_begin_read ()); @@ -1401,7 +1402,7 @@ TEST (bootstrap_processor, lazy_pruning_missing_block) // Insert missing block node2->process_active (send1); - ASSERT_TIMELY_EQ (5s, 3, node2->ledger.cache.block_count); + ASSERT_TIMELY_EQ (5s, 3, node2->ledger.block_count ()); ASSERT_TIMELY (5s, nano::test::exists (*node2, { send1, send2 })); ASSERT_TRUE (nano::test::block_or_pruned_none_exists (*node2, { open, state_open })); } @@ -2050,18 +2051,18 @@ TEST (bulk, genesis_pruning) ASSERT_TRUE (nano::test::start_elections (system, *node1, { send1 }, true)); ASSERT_TIMELY (5s, node1->active.active (send2->qualified_root ())); - ASSERT_EQ (0, node1->ledger.cache.pruned_count); + ASSERT_EQ (0, node1->ledger.pruned_count ()); ASSERT_TRUE (nano::test::start_elections (system, *node1, { send2 }, true)); ASSERT_TIMELY (5s, node1->active.active (send3->qualified_root ())); - ASSERT_EQ (0, node1->ledger.cache.pruned_count); + ASSERT_EQ (0, node1->ledger.pruned_count ()); ASSERT_TRUE (nano::test::start_elections (system, *node1, { send3 }, true)); ASSERT_TIMELY (5s, nano::test::confirmed (*node1, { send3 })); node1->ledger_pruning (2, false); - ASSERT_EQ (2, node1->ledger.cache.pruned_count); - ASSERT_EQ (4, node1->ledger.cache.block_count); + ASSERT_EQ (2, node1->ledger.pruned_count ()); + ASSERT_EQ (4, node1->ledger.block_count ()); ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send1->hash ())); ASSERT_FALSE (nano::test::exists (*node1, { send1 })); ASSERT_TRUE (node1->ledger.store.pruned.exists (node1->ledger.store.tx_begin_read (), send2->hash ())); @@ -2077,7 +2078,7 @@ TEST (bulk, genesis_pruning) ASSERT_TIMELY (5s, !node2->bootstrap_initiator.in_progress ()); // node2 still missing blocks - ASSERT_EQ (1, node2->ledger.cache.block_count); + ASSERT_EQ (1, node2->ledger.block_count ()); { auto transaction (node2->store.tx_begin_write ()); node2->unchecked.clear (); @@ -2086,7 +2087,7 @@ TEST (bulk, genesis_pruning) // Insert pruned blocks node2->process_active (send1); node2->process_active (send2); - ASSERT_TIMELY_EQ (5s, 3, node2->ledger.cache.block_count); + ASSERT_TIMELY_EQ (5s, 3, node2->ledger.block_count ()); // New bootstrap to sync up everything ASSERT_TIMELY_EQ (5s, node2->bootstrap_initiator.connections->connections_count, 0); diff --git a/nano/core_test/confirming_set.cpp b/nano/core_test/confirming_set.cpp index d7137eb54e..7cc7ae91f6 100644 --- a/nano/core_test/confirming_set.cpp +++ b/nano/core_test/confirming_set.cpp @@ -18,15 +18,13 @@ using namespace std::chrono_literals; TEST (confirming_set, construction) { auto ctx = nano::test::context::ledger_empty (); - nano::write_database_queue write_queue{ false }; - nano::confirming_set confirming_set (ctx.ledger (), write_queue); + nano::confirming_set confirming_set (ctx.ledger ()); } TEST (confirming_set, add_exists) { auto ctx = nano::test::context::ledger_send_receive (); - nano::write_database_queue write_queue{ false }; - nano::confirming_set confirming_set (ctx.ledger (), write_queue); + nano::confirming_set confirming_set (ctx.ledger ()); auto send = ctx.blocks ()[0]; confirming_set.add (send->hash ()); ASSERT_TRUE (confirming_set.exists (send->hash ())); @@ -35,8 +33,7 @@ TEST (confirming_set, add_exists) TEST (confirming_set, process_one) { auto ctx = nano::test::context::ledger_send_receive (); - nano::write_database_queue write_queue{ false }; - nano::confirming_set confirming_set (ctx.ledger (), write_queue); + nano::confirming_set confirming_set (ctx.ledger ()); std::atomic count = 0; std::mutex mutex; std::condition_variable condition; @@ -46,14 +43,13 @@ TEST (confirming_set, process_one) std::unique_lock lock{ mutex }; ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 1; })); ASSERT_EQ (1, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, ctx.ledger ().cache.cemented_count); + ASSERT_EQ (2, ctx.ledger ().cemented_count ()); } TEST (confirming_set, process_multiple) { auto ctx = nano::test::context::ledger_send_receive (); - nano::write_database_queue write_queue{ false }; - nano::confirming_set confirming_set (ctx.ledger (), write_queue); + nano::confirming_set confirming_set (ctx.ledger ()); std::atomic count = 0; std::mutex mutex; std::condition_variable condition; @@ -64,7 +60,7 @@ TEST (confirming_set, process_multiple) std::unique_lock lock{ mutex }; ASSERT_TRUE (condition.wait_for (lock, 5s, [&] () { return count == 2; })); ASSERT_EQ (2, ctx.stats ().count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (3, ctx.ledger ().cache.cemented_count); + ASSERT_EQ (3, ctx.ledger ().cemented_count ()); } TEST (confirmation_callback, observer_callbacks) @@ -109,7 +105,7 @@ TEST (confirmation_callback, observer_callbacks) ASSERT_TIMELY_EQ (5s, 2, node->ledger.stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (3, node->ledger.cache.cemented_count); + ASSERT_EQ (3, node->ledger.cemented_count ()); ASSERT_EQ (0, node->active.election_winner_details_size ()); } @@ -118,7 +114,7 @@ TEST (confirmation_callback, confirmed_history) { nano::test::system system; nano::node_flags node_flags; - node_flags.force_use_write_database_queue = true; + node_flags.force_use_write_queue = true; node_flags.disable_ascending_bootstrap = true; nano::node_config node_config = system.default_config (); node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; @@ -155,7 +151,7 @@ TEST (confirmation_callback, confirmed_history) ASSERT_TIMELY (5s, election = nano::test::start_election (system, *node, send1->hash ())); { // The write guard prevents the confirmation height processor doing any writes - auto write_guard = node->write_database_queue.wait (nano::writer::testing); + auto write_guard = node->store.write_queue.wait (nano::store::writer::testing); // Confirm send1 election->force_confirm (); @@ -166,13 +162,13 @@ TEST (confirmation_callback, confirmed_history) auto transaction = node->store.tx_begin_read (); ASSERT_FALSE (node->ledger.block_confirmed (transaction, send->hash ())); - ASSERT_TIMELY (10s, node->write_database_queue.contains (nano::writer::confirmation_height)); + ASSERT_TIMELY (10s, node->store.write_queue.contains (nano::store::writer::confirmation_height)); // Confirm that no inactive callbacks have been called when the confirmation height processor has already iterated over it, waiting to write ASSERT_EQ (0, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); } - ASSERT_TIMELY (10s, !node->write_database_queue.contains (nano::writer::confirmation_height)); + ASSERT_TIMELY (10s, !node->store.write_queue.contains (nano::store::writer::confirmation_height)); auto transaction = node->store.tx_begin_read (); ASSERT_TRUE (node->ledger.block_confirmed (transaction, send->hash ())); @@ -188,7 +184,7 @@ TEST (confirmation_callback, confirmed_history) ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); ASSERT_EQ (2, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (3, node->ledger.cache.cemented_count); + ASSERT_EQ (3, node->ledger.cemented_count ()); ASSERT_EQ (0, node->active.election_winner_details_size ()); } @@ -196,7 +192,7 @@ TEST (confirmation_callback, dependent_election) { nano::test::system system; nano::node_flags node_flags; - node_flags.force_use_write_database_queue = true; + node_flags.force_use_write_queue = true; nano::node_config node_config = system.default_config (); node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; auto node = system.add_node (node_config, node_flags); @@ -251,7 +247,7 @@ TEST (confirmation_callback, dependent_election) ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_quorum, nano::stat::dir::out)); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::active_conf_height, nano::stat::dir::out)); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::inactive_conf_height, nano::stat::dir::out)); - ASSERT_EQ (4, node->ledger.cache.cemented_count); + ASSERT_EQ (4, node->ledger.cemented_count ()); ASSERT_EQ (0, node->active.election_winner_details_size ()); } diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index 323154046f..a4175a7f3b 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -99,7 +99,7 @@ TEST (conflicts, add_two) // create 2 new accounts, that receive 1 raw each, all blocks are force confirmed auto [send1, open1] = nano::test::setup_new_account (system, *node, 1, gk, key1, gk.pub, true); auto [send2, open2] = nano::test::setup_new_account (system, *node, 1, gk, key2, gk.pub, true); - ASSERT_EQ (5, node->ledger.cache.cemented_count); + ASSERT_EQ (5, node->ledger.cemented_count ()); // send 1 raw to account key3 from key1 auto send_a = nano::state_block_builder () diff --git a/nano/core_test/election.cpp b/nano/core_test/election.cpp index ac4c47a63f..140052cee1 100644 --- a/nano/core_test/election.cpp +++ b/nano/core_test/election.cpp @@ -238,13 +238,13 @@ TEST (election, quorum_minimum_update_weight_before_quorum_checks) .work (*system.work.generate (open1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, node1.process (send2)); - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.block_count, 4); + ASSERT_TIMELY_EQ (5s, node1.ledger.block_count (), 4); node_config.peering_port = system.get_available_port (); auto & node2 = *system.add_node (node_config); system.wallet (1)->insert_adhoc (key1.prv); - ASSERT_TIMELY_EQ (10s, node2.ledger.cache.block_count, 4); + ASSERT_TIMELY_EQ (10s, node2.ledger.block_count (), 4); std::shared_ptr election; ASSERT_TIMELY (5s, (election = node1.active.election (send1->qualified_root ())) != nullptr); diff --git a/nano/core_test/fair_queue.cpp b/nano/core_test/fair_queue.cpp new file mode 100644 index 0000000000..178365b171 --- /dev/null +++ b/nano/core_test/fair_queue.cpp @@ -0,0 +1,276 @@ +#include +#include +#include + +#include + +#include + +using namespace std::chrono_literals; + +namespace +{ +enum class source_enum +{ + unknown = 0, + live, + bootstrap, + bootstrap_legacy, + unchecked, + local, + forced, +}; +} + +TEST (fair_queue, construction) +{ + nano::fair_queue queue; + ASSERT_EQ (queue.total_size (), 0); + ASSERT_TRUE (queue.empty ()); +} + +TEST (fair_queue, process_one) +{ + nano::fair_queue queue; + queue.priority_query = [] (auto const &) { return 1; }; + queue.max_size_query = [] (auto const &) { return 1; }; + + queue.push (7, { source_enum::live }); + ASSERT_EQ (queue.total_size (), 1); + ASSERT_EQ (queue.queues_size (), 1); + ASSERT_EQ (queue.size ({ source_enum::live }), 1); + ASSERT_EQ (queue.size ({ source_enum::bootstrap }), 0); + + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 7); + ASSERT_EQ (origin.source, source_enum::live); + ASSERT_EQ (origin.channel, nullptr); + + ASSERT_TRUE (queue.empty ()); +} + +TEST (fair_queue, fifo) +{ + nano::fair_queue queue; + queue.priority_query = [] (auto const &) { return 1; }; + queue.max_size_query = [] (auto const &) { return 999; }; + + queue.push (7, { source_enum::live }); + queue.push (8, { source_enum::live }); + queue.push (9, { source_enum::live }); + ASSERT_EQ (queue.total_size (), 3); + ASSERT_EQ (queue.queues_size (), 1); + ASSERT_EQ (queue.size ({ source_enum::live }), 3); + + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 7); + ASSERT_EQ (origin.source, source_enum::live); + } + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 8); + ASSERT_EQ (origin.source, source_enum::live); + } + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 9); + ASSERT_EQ (origin.source, source_enum::live); + } + + ASSERT_TRUE (queue.empty ()); +} + +TEST (fair_queue, process_many) +{ + nano::fair_queue queue; + queue.priority_query = [] (auto const &) { return 1; }; + queue.max_size_query = [] (auto const &) { return 1; }; + + queue.push (7, { source_enum::live }); + queue.push (8, { source_enum::bootstrap }); + queue.push (9, { source_enum::unchecked }); + ASSERT_EQ (queue.total_size (), 3); + ASSERT_EQ (queue.queues_size (), 3); + ASSERT_EQ (queue.size ({ source_enum::live }), 1); + ASSERT_EQ (queue.size ({ source_enum::bootstrap }), 1); + ASSERT_EQ (queue.size ({ source_enum::unchecked }), 1); + + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 7); + ASSERT_EQ (origin.source, source_enum::live); + } + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 8); + ASSERT_EQ (origin.source, source_enum::bootstrap); + } + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 9); + ASSERT_EQ (origin.source, source_enum::unchecked); + } + + ASSERT_TRUE (queue.empty ()); +} + +TEST (fair_queue, max_queue_size) +{ + nano::fair_queue queue; + queue.priority_query = [] (auto const &) { return 1; }; + queue.max_size_query = [] (auto const &) { return 2; }; + + queue.push (7, { source_enum::live }); + queue.push (8, { source_enum::live }); + queue.push (9, { source_enum::live }); + ASSERT_EQ (queue.total_size (), 2); + ASSERT_EQ (queue.queues_size (), 1); + ASSERT_EQ (queue.size ({ source_enum::live }), 2); + + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 7); + ASSERT_EQ (origin.source, source_enum::live); + } + { + auto [result, origin] = queue.next (); + ASSERT_EQ (result, 8); + ASSERT_EQ (origin.source, source_enum::live); + } + + ASSERT_TRUE (queue.empty ()); +} + +TEST (fair_queue, round_robin_with_priority) +{ + nano::fair_queue queue; + queue.priority_query = [] (auto const & origin) { + switch (origin.source) + { + case source_enum::live: + return 1; + case source_enum::bootstrap: + return 2; + case source_enum::unchecked: + return 3; + default: + return 0; + } + }; + queue.max_size_query = [] (auto const &) { return 999; }; + + queue.push (7, { source_enum::live }); + queue.push (8, { source_enum::live }); + queue.push (9, { source_enum::live }); + queue.push (10, { source_enum::bootstrap }); + queue.push (11, { source_enum::bootstrap }); + queue.push (12, { source_enum::bootstrap }); + queue.push (13, { source_enum::unchecked }); + queue.push (14, { source_enum::unchecked }); + queue.push (15, { source_enum::unchecked }); + ASSERT_EQ (queue.total_size (), 9); + + // Processing 1x live, 2x bootstrap, 3x unchecked before moving to the next source + ASSERT_EQ (queue.next ().second.source, source_enum::live); + ASSERT_EQ (queue.next ().second.source, source_enum::bootstrap); + ASSERT_EQ (queue.next ().second.source, source_enum::bootstrap); + ASSERT_EQ (queue.next ().second.source, source_enum::unchecked); + ASSERT_EQ (queue.next ().second.source, source_enum::unchecked); + ASSERT_EQ (queue.next ().second.source, source_enum::unchecked); + ASSERT_EQ (queue.next ().second.source, source_enum::live); + ASSERT_EQ (queue.next ().second.source, source_enum::bootstrap); + ASSERT_EQ (queue.next ().second.source, source_enum::live); + + ASSERT_TRUE (queue.empty ()); +} + +TEST (fair_queue, source_channel) +{ + nano::test::system system{ 1 }; + + nano::fair_queue queue; + queue.priority_query = [] (auto const &) { return 1; }; + queue.max_size_query = [] (auto const &) { return 999; }; + + auto channel1 = nano::test::fake_channel (system.node (0)); + auto channel2 = nano::test::fake_channel (system.node (0)); + auto channel3 = nano::test::fake_channel (system.node (0)); + + queue.push (6, { source_enum::live, channel1 }); + queue.push (7, { source_enum::live, channel2 }); + queue.push (8, { source_enum::live, channel3 }); + queue.push (9, { source_enum::live, channel1 }); // Channel 1 has multiple entries + ASSERT_EQ (queue.total_size (), 4); + ASSERT_EQ (queue.queues_size (), 3); // Each pair is a separate queue + + ASSERT_EQ (queue.size ({ source_enum::live, channel1 }), 2); + ASSERT_EQ (queue.size ({ source_enum::live, channel2 }), 1); + ASSERT_EQ (queue.size ({ source_enum::live, channel3 }), 1); + + auto all = queue.next_batch (999); + ASSERT_EQ (all.size (), 4); + + auto filtered = [&] (auto const & channel) { + auto r = all | std::views::filter ([&] (auto const & entry) { + return entry.second.channel == channel; + }); + std::vector vec (r.begin (), r.end ()); + return vec; + }; + + auto channel1_results = filtered (channel1); + ASSERT_EQ (channel1_results.size (), 2); + + { + auto [result, origin] = channel1_results[0]; + ASSERT_EQ (result, 6); + ASSERT_EQ (origin.source, source_enum::live); + ASSERT_EQ (origin.channel, channel1); + } + { + auto [result, origin] = channel1_results[1]; + ASSERT_EQ (result, 9); + ASSERT_EQ (origin.source, source_enum::live); + ASSERT_EQ (origin.channel, channel1); + } + + ASSERT_TRUE (queue.empty ()); +} + +TEST (fair_queue, cleanup) +{ + nano::test::system system{ 1 }; + + nano::fair_queue queue; + queue.priority_query = [] (auto const &) { return 1; }; + queue.max_size_query = [] (auto const &) { return 999; }; + + auto channel1 = nano::test::fake_channel (system.node (0)); + auto channel2 = nano::test::fake_channel (system.node (0)); + auto channel3 = nano::test::fake_channel (system.node (0)); + + queue.push (7, { source_enum::live, channel1 }); + queue.push (8, { source_enum::live, channel2 }); + queue.push (9, { source_enum::live, channel3 }); + ASSERT_EQ (queue.total_size (), 3); + ASSERT_EQ (queue.queues_size (), 3); + + ASSERT_EQ (queue.size ({ source_enum::live, channel1 }), 1); + ASSERT_EQ (queue.size ({ source_enum::live, channel2 }), 1); + ASSERT_EQ (queue.size ({ source_enum::live, channel3 }), 1); + + // Either closing or resetting the channel should remove it from the queue + channel1->close (); + channel2.reset (); + + ASSERT_TRUE (queue.periodic_update ()); + + // Only channel 3 should remain + ASSERT_EQ (queue.total_size (), 1); + ASSERT_EQ (queue.queues_size (), 1); + + ASSERT_EQ (queue.size ({ source_enum::live, channel1 }), 0); + ASSERT_EQ (queue.size ({ source_enum::live, channel2 }), 0); + ASSERT_EQ (queue.size ({ source_enum::live, channel3 }), 1); +} diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index af04a99be2..35d1f419d5 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -56,7 +56,7 @@ TEST (ledger, genesis_balance) ASSERT_EQ (nano::dev::constants.genesis_amount, balance); auto info = ledger.account_info (transaction, nano::dev::genesis_key.pub); ASSERT_TRUE (info); - ASSERT_EQ (1, ledger.cache.account_count); + ASSERT_EQ (1, ledger.account_count ()); // Frontier time should have been updated when genesis balance was added ASSERT_GE (nano::seconds_since_epoch (), info->modified); ASSERT_LT (nano::seconds_since_epoch () - info->modified, 10); @@ -189,7 +189,7 @@ TEST (ledger, process_send) ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key (key2.pub, hash1))); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.account_receivable (transaction, key2.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, process_receive) @@ -272,7 +272,7 @@ TEST (ledger, process_receive) ASSERT_TRUE (pending1); ASSERT_EQ (nano::dev::genesis_key.pub, pending1->source); ASSERT_EQ (25, pending1->amount.number ()); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, rollback_receiver) @@ -320,7 +320,7 @@ TEST (ledger, rollback_receiver) ASSERT_EQ (0, ledger.weight (key2.pub)); ASSERT_EQ (0, ledger.weight (key3.pub)); ASSERT_FALSE (ledger.account_info (transaction, key2.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key2.pub, hash1 })); } @@ -1626,7 +1626,7 @@ TEST (ledger, fail_open_fork_previous) .work (*pool.generate (key1.pub)) .build (); ASSERT_EQ (nano::block_status::fork, ledger.process (transaction, block4)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, fail_open_account_mismatch) @@ -1657,7 +1657,7 @@ TEST (ledger, fail_open_account_mismatch) .work (*pool.generate (badkey.pub)) .build (); ASSERT_NE (nano::block_status::progress, ledger.process (transaction, block2)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, fail_receive_old) @@ -2265,7 +2265,7 @@ TEST (ledger, bootstrap_rep_weight) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); } - ASSERT_EQ (2, ledger.cache.block_count); + ASSERT_EQ (2, ledger.block_count ()); { ledger.bootstrap_weight_max_blocks = 3; ledger.bootstrap_weights[key2.pub] = 1000; @@ -2286,7 +2286,7 @@ TEST (ledger, bootstrap_rep_weight) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send)); } - ASSERT_EQ (3, ledger.cache.block_count); + ASSERT_EQ (3, ledger.block_count ()); ASSERT_EQ (0, ledger.weight (key2.pub)); } @@ -2452,7 +2452,7 @@ TEST (ledger, state_send_receive) ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, receive1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); ASSERT_EQ (3, receive2->sideband ().height); ASSERT_FALSE (receive2->is_send ()); ASSERT_TRUE (receive2->is_receive ()); @@ -2588,7 +2588,7 @@ TEST (ledger, state_open) ASSERT_EQ (nano::Gxrb_ratio, ledger.balance (transaction, open1->hash ())); ASSERT_EQ (nano::Gxrb_ratio, ledger.amount (transaction, open1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_EQ (ledger.cache.account_count, store.account.count (transaction)); + ASSERT_EQ (ledger.account_count (), store.account.count (transaction)); ASSERT_EQ (1, open2->sideband ().height); ASSERT_FALSE (open2->is_send ()); ASSERT_TRUE (open2->is_receive ()); @@ -2918,7 +2918,7 @@ TEST (ledger, state_state_open_fork) .build (); ASSERT_EQ (nano::block_status::fork, ledger.process (transaction, open2)); ASSERT_EQ (open1->root (), open2->root ()); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_open_previous_fail) @@ -3195,7 +3195,7 @@ TEST (ledger, state_rollback_send) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); ASSERT_FALSE (ledger.successor (transaction, nano::dev::genesis->hash ())); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_rollback_receive) @@ -3237,7 +3237,7 @@ TEST (ledger, state_rollback_receive) ASSERT_FALSE (ledger.block_exists (transaction, receive1->hash ())); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis_key.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_rollback_received_send) @@ -3280,7 +3280,7 @@ TEST (ledger, state_rollback_received_send) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.account_balance (transaction, key.pub)); ASSERT_EQ (0, ledger.weight (key.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_rep_change_rollback) @@ -3349,7 +3349,7 @@ TEST (ledger, state_open_rollback) ASSERT_TRUE (info); ASSERT_EQ (nano::dev::genesis_key.pub, info->source); ASSERT_EQ (nano::Gxrb_ratio, info->amount.number ()); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_send_change_rollback) @@ -3377,7 +3377,7 @@ TEST (ledger, state_send_change_rollback) ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.weight (rep.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, state_receive_change_rollback) @@ -3416,7 +3416,7 @@ TEST (ledger, state_receive_change_rollback) ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.account_balance (transaction, nano::dev::genesis_key.pub)); ASSERT_EQ (nano::dev::constants.genesis_amount - nano::Gxrb_ratio, ledger.weight (nano::dev::genesis_key.pub)); ASSERT_EQ (0, ledger.weight (rep.pub)); - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, epoch_blocks_v1_general) @@ -3932,7 +3932,7 @@ TEST (ledger, epoch_blocks_receive_upgrade) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, epoch4)); ASSERT_EQ (nano::epoch::epoch_2, epoch4->sideband ().details.epoch); ASSERT_EQ (nano::epoch::epoch_0, epoch4->sideband ().source_epoch); // Not used for epoch blocks - ASSERT_EQ (store.account.count (transaction), ledger.cache.account_count); + ASSERT_EQ (store.account.count (transaction), ledger.account_count ()); } TEST (ledger, epoch_blocks_fork) @@ -4667,10 +4667,7 @@ TEST (ledger, dependents_confirmed) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive1)); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - height.height += 1; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send1->hash ()); ASSERT_TRUE (ledger.dependents_confirmed (transaction, *receive1)); auto receive2 = builder.state () .account (key1.pub) @@ -4683,13 +4680,9 @@ TEST (ledger, dependents_confirmed) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive2)); ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive2)); - ASSERT_TRUE (ledger.store.confirmation_height.get (transaction, key1.pub, height)); - height.height += 1; - ledger.store.confirmation_height.put (transaction, key1.pub, height); + ledger.confirm (transaction, receive1->hash ()); ASSERT_FALSE (ledger.dependents_confirmed (transaction, *receive2)); - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - height.height += 1; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send2->hash ()); ASSERT_TRUE (ledger.dependents_confirmed (transaction, *receive2)); } @@ -4726,10 +4719,7 @@ TEST (ledger, dependents_confirmed_pruning) .work (*pool.generate (send1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - height.height = 3; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send2->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, send1->hash ())); ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 1)); auto receive1 = builder.state () @@ -4767,10 +4757,7 @@ TEST (ledger, block_confirmed) ASSERT_FALSE (ledger.block_confirmed (transaction, send1->hash ())); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); ASSERT_FALSE (ledger.block_confirmed (transaction, send1->hash ())); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - ++height.height; - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send1->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, send1->hash ())); } @@ -4794,12 +4781,12 @@ TEST (ledger, cache) auto genesis_weight = nano::dev::constants.genesis_amount - i; auto pruned_count = i; - auto cache_check = [&, i] (nano::ledger_cache const & cache_a) { - ASSERT_EQ (account_count, cache_a.account_count); - ASSERT_EQ (block_count, cache_a.block_count); - ASSERT_EQ (cemented_count, cache_a.cemented_count); - ASSERT_EQ (genesis_weight, cache_a.rep_weights.representation_get (nano::dev::genesis_key.pub)); - ASSERT_EQ (pruned_count, cache_a.pruned_count); + auto cache_check = [&, i] (nano::ledger const & ledger) { + ASSERT_EQ (account_count, ledger.account_count ()); + ASSERT_EQ (block_count, ledger.block_count ()); + ASSERT_EQ (cemented_count, ledger.cemented_count ()); + ASSERT_EQ (genesis_weight, ledger.cache.rep_weights.representation_get (nano::dev::genesis_key.pub)); + ASSERT_EQ (pruned_count, ledger.pruned_count ()); }; nano::keypair key; @@ -4829,8 +4816,8 @@ TEST (ledger, cache) ++block_count; --genesis_weight; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); @@ -4839,47 +4826,36 @@ TEST (ledger, cache) ++block_count; ++account_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); - nano::confirmation_height_info height; - ASSERT_FALSE (ledger.store.confirmation_height.get (transaction, nano::dev::genesis_key.pub, height)); - ++height.height; - height.frontier = send->hash (); - ledger.store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, height); + ledger.confirm (transaction, send->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, send->hash ())); - ++ledger.cache.cemented_count; } ++cemented_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); - nano::confirmation_height_info height; - ledger.store.confirmation_height.get (transaction, key.pub, height); - height.height += 1; - height.frontier = open->hash (); - ledger.store.confirmation_height.put (transaction, key.pub, height); + ledger.confirm (transaction, open->hash ()); ASSERT_TRUE (ledger.block_confirmed (transaction, open->hash ())); - ++ledger.cache.cemented_count; } ++cemented_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); { auto transaction (store.tx_begin_write ()); - ledger.store.pruned.put (transaction, open->hash ()); - ++ledger.cache.pruned_count; + ledger.pruning_action (transaction, open->hash (), 1); } ++pruned_count; - cache_check (ledger.cache); - cache_check (nano::ledger (store, stats, nano::dev::constants).cache); + cache_check (ledger); + cache_check (nano::ledger (store, stats, nano::dev::constants)); } } @@ -4924,6 +4900,7 @@ TEST (ledger, pruning_action) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); // Pruning action + ledger.confirm (transaction, send1->hash ()); ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_EQ (0, ledger.pruning_action (transaction, nano::dev::genesis->hash (), 1)); ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); @@ -4959,12 +4936,13 @@ TEST (ledger, pruning_action) ASSERT_FALSE (receive1_stored->sideband ().details.is_epoch); // Middle block pruning ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send2->hash ()); ASSERT_EQ (1, ledger.pruning_action (transaction, send2->hash (), 1)); ASSERT_TRUE (store->pruned.exists (transaction, send2->hash ())); ASSERT_FALSE (store->block.exists (transaction, send2->hash ())); - ASSERT_EQ (store->account.count (transaction), ledger.cache.account_count); - ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count); - ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count); + ASSERT_EQ (store->account.count (transaction), ledger.account_count ()); + ASSERT_EQ (store->pruned.count (transaction), ledger.pruned_count ()); + ASSERT_EQ (store->block.count (transaction), ledger.block_count () - ledger.pruned_count ()); } TEST (ledger, pruning_large_chain) @@ -5011,13 +4989,14 @@ TEST (ledger, pruning_large_chain) } ASSERT_EQ (0, store->pruned.count (transaction)); ASSERT_EQ (send_receive_pairs * 2 + 1, store->block.count (transaction)); + ledger.confirm (transaction, last_hash); // Pruning action ASSERT_EQ (send_receive_pairs * 2, ledger.pruning_action (transaction, last_hash, 5)); ASSERT_TRUE (store->pruned.exists (transaction, last_hash)); ASSERT_TRUE (store->block.exists (transaction, nano::dev::genesis->hash ())); ASSERT_FALSE (store->block.exists (transaction, last_hash)); - ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count); - ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count); + ASSERT_EQ (store->pruned.count (transaction), ledger.pruned_count ()); + ASSERT_EQ (store->block.count (transaction), ledger.block_count () - ledger.pruned_count ()); ASSERT_EQ (send_receive_pairs * 2, store->pruned.count (transaction)); ASSERT_EQ (1, store->block.count (transaction)); // Genesis } @@ -5069,6 +5048,7 @@ TEST (ledger, pruning_source_rollback) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send1->hash ()); // Pruning action ASSERT_EQ (2, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); @@ -5094,8 +5074,8 @@ TEST (ledger, pruning_source_rollback) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); // Rollback receive block ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ())); auto info2 = ledger.pending_info (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ())); @@ -5106,8 +5086,8 @@ TEST (ledger, pruning_source_rollback) // Process receive block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); } TEST (ledger, pruning_source_rollback_legacy) @@ -5155,6 +5135,7 @@ TEST (ledger, pruning_source_rollback_legacy) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); ASSERT_TRUE (store->block.exists (transaction, send3->hash ())); ASSERT_TRUE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send3->hash () })); + ledger.confirm (transaction, send2->hash ()); // Pruning action ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send2->hash ())); @@ -5182,8 +5163,8 @@ TEST (ledger, pruning_source_rollback_legacy) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); // Rollback receive block ASSERT_FALSE (ledger.rollback (transaction, receive1->hash ())); auto info3 = ledger.pending_info (transaction, nano::pending_key (nano::dev::genesis_key.pub, send1->hash ())); @@ -5194,8 +5175,8 @@ TEST (ledger, pruning_source_rollback_legacy) // Process receive block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ nano::dev::genesis_key.pub, send1->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (5, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (5, ledger.block_count ()); // Receiving pruned block (open) auto open1 = builder .open () @@ -5207,8 +5188,8 @@ TEST (ledger, pruning_source_rollback_legacy) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key1.pub, send2->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (6, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (6, ledger.block_count ()); // Rollback open block ASSERT_FALSE (ledger.rollback (transaction, open1->hash ())); auto info4 = ledger.pending_info (transaction, nano::pending_key (key1.pub, send2->hash ())); @@ -5219,55 +5200,8 @@ TEST (ledger, pruning_source_rollback_legacy) // Process open block again ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open1)); ASSERT_FALSE (ledger.pending_info (transaction, nano::pending_key{ key1.pub, send2->hash () })); - ASSERT_EQ (2, ledger.cache.pruned_count); - ASSERT_EQ (6, ledger.cache.block_count); -} - -TEST (ledger, pruning_process_error) -{ - nano::logger logger; - auto store = nano::make_store (logger, nano::unique_path (), nano::dev::constants); - ASSERT_TRUE (!store->init_error ()); - nano::stats stats; - nano::ledger ledger (*store, stats, nano::dev::constants); - ledger.pruning = true; - auto transaction (store->tx_begin_write ()); - store->initialize (transaction, ledger.cache, ledger.constants); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - nano::block_builder builder; - auto send1 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (nano::dev::genesis->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio) - .link (nano::dev::genesis_key.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (nano::dev::genesis->hash ())) - .build (); - ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send1)); - ASSERT_EQ (0, ledger.cache.pruned_count); - ASSERT_EQ (2, ledger.cache.block_count); - // Pruning action for latest block (not valid action) - ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); - ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); - ASSERT_TRUE (store->pruned.exists (transaction, send1->hash ())); - // Attempt to process pruned block again - ASSERT_EQ (nano::block_status::old, ledger.process (transaction, send1)); - // Attept to process new block after pruned - auto send2 = builder - .state () - .account (nano::dev::genesis_key.pub) - .previous (send1->hash ()) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - nano::Gxrb_ratio * 2) - .link (nano::dev::genesis_key.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (send1->hash ())) - .build (); - ASSERT_EQ (nano::block_status::gap_previous, ledger.process (transaction, send2)); - ASSERT_EQ (1, ledger.cache.pruned_count); - ASSERT_EQ (2, ledger.cache.block_count); + ASSERT_EQ (2, ledger.pruned_count ()); + ASSERT_EQ (6, ledger.block_count ()); } TEST (ledger, pruning_legacy_blocks) @@ -5336,6 +5270,7 @@ TEST (ledger, pruning_legacy_blocks) .work (*pool.generate (open1->hash ())) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); + ledger.confirm (transaction, open1->hash ()); // Pruning action ASSERT_EQ (3, ledger.pruning_action (transaction, change1->hash (), 2)); ASSERT_EQ (1, ledger.pruning_action (transaction, open1->hash (), 1)); @@ -5350,10 +5285,10 @@ TEST (ledger, pruning_legacy_blocks) ASSERT_FALSE (store->block.exists (transaction, open1->hash ())); ASSERT_TRUE (store->pruned.exists (transaction, open1->hash ())); ASSERT_TRUE (store->block.exists (transaction, send3->hash ())); - ASSERT_EQ (4, ledger.cache.pruned_count); - ASSERT_EQ (7, ledger.cache.block_count); - ASSERT_EQ (store->pruned.count (transaction), ledger.cache.pruned_count); - ASSERT_EQ (store->block.count (transaction), ledger.cache.block_count - ledger.cache.pruned_count); + ASSERT_EQ (4, ledger.pruned_count ()); + ASSERT_EQ (7, ledger.block_count ()); + ASSERT_EQ (store->pruned.count (transaction), ledger.pruned_count ()); + ASSERT_EQ (store->block.count (transaction), ledger.block_count () - ledger.pruned_count ()); } TEST (ledger, pruning_safe_functions) @@ -5392,6 +5327,7 @@ TEST (ledger, pruning_safe_functions) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send1->hash ()); // Pruning action ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); @@ -5443,6 +5379,7 @@ TEST (ledger, hash_root_random) .build (); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_TRUE (store->block.exists (transaction, send2->hash ())); + ledger.confirm (transaction, send1->hash ()); // Pruning action ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 1)); ASSERT_FALSE (store->block.exists (transaction, send1->hash ())); @@ -5541,41 +5478,6 @@ TEST (ledger, migrate_lmdb_to_rocksdb) ASSERT_EQ (rocksdb_store.final_vote.get (rocksdb_transaction, nano::root (send->previous ()))[0], nano::block_hash (2)); } -TEST (ledger, unconfirmed_frontiers) -{ - auto ctx = nano::test::context::ledger_empty (); - auto & ledger = ctx.ledger (); - auto & store = ctx.store (); - nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; - - auto unconfirmed_frontiers = ledger.unconfirmed_frontiers (); - ASSERT_TRUE (unconfirmed_frontiers.empty ()); - - nano::state_block_builder builder; - nano::keypair key; - auto const latest = ledger.latest (store.tx_begin_read (), nano::dev::genesis_key.pub); - auto send = builder.make_block () - .account (nano::dev::genesis_key.pub) - .previous (latest) - .representative (nano::dev::genesis_key.pub) - .balance (nano::dev::constants.genesis_amount - 100) - .link (key.pub) - .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) - .work (*pool.generate (latest)) - .build (); - - ASSERT_EQ (nano::block_status::progress, ledger.process (store.tx_begin_write (), send)); - - unconfirmed_frontiers = ledger.unconfirmed_frontiers (); - ASSERT_EQ (unconfirmed_frontiers.size (), 1); - ASSERT_EQ (unconfirmed_frontiers.begin ()->first, 1); - nano::uncemented_info uncemented_info1{ latest, send->hash (), nano::dev::genesis_key.pub }; - auto uncemented_info2 = unconfirmed_frontiers.begin ()->second; - ASSERT_EQ (uncemented_info1.account, uncemented_info2.account); - ASSERT_EQ (uncemented_info1.cemented_frontier, uncemented_info2.cemented_frontier); - ASSERT_EQ (uncemented_info1.frontier, uncemented_info2.frontier); -} - TEST (ledger, is_send_genesis) { auto ctx = nano::test::context::ledger_empty (); diff --git a/nano/core_test/ledger_confirm.cpp b/nano/core_test/ledger_confirm.cpp index f22e36b0ee..45dccb0f1f 100644 --- a/nano/core_test/ledger_confirm.cpp +++ b/nano/core_test/ledger_confirm.cpp @@ -48,7 +48,7 @@ TEST (ledger_confirm, single) ASSERT_TRUE (node->ledger.rollback (transaction, latest1)); ASSERT_TRUE (node->ledger.rollback (transaction, send1->hash ())); ASSERT_EQ (1, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, node->ledger.cache.cemented_count); + ASSERT_EQ (2, node->ledger.cemented_count ()); } TEST (ledger_confirm, multiple_accounts) @@ -193,7 +193,7 @@ TEST (ledger_confirm, multiple_accounts) auto confirmed = node->ledger.confirm (transaction, receive3->hash ()); ASSERT_EQ (10, confirmed.size ()); ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (11, node->ledger.cache.cemented_count); + ASSERT_EQ (11, node->ledger.cemented_count ()); ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive3->hash ())); ASSERT_EQ (4, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); @@ -343,7 +343,7 @@ TEST (ledger_confirm, send_receive_between_2_accounts) auto confirmed = node->ledger.confirm (transaction, receive4->hash ()); ASSERT_EQ (10, confirmed.size ()); ASSERT_EQ (10, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (11, node->ledger.cache.cemented_count); + ASSERT_EQ (11, node->ledger.cemented_count ()); ASSERT_TRUE (node->ledger.block_confirmed (transaction, receive4->hash ())); ASSERT_EQ (7, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); @@ -440,7 +440,7 @@ TEST (ledger_confirm, send_receive_self) ASSERT_EQ (8, node->ledger.account_info (transaction, nano::dev::genesis_key.pub).value ().block_count); ASSERT_EQ (7, node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().height); ASSERT_EQ (receive3->hash (), node->store.confirmation_height.get (transaction, nano::dev::genesis_key.pub).value ().frontier); - ASSERT_EQ (7, node->ledger.cache.cemented_count); + ASSERT_EQ (7, node->ledger.cemented_count ()); } TEST (ledger_confirm, all_block_types) @@ -660,7 +660,7 @@ TEST (ledger_confirm, all_block_types) auto confirmed = node->ledger.confirm (transaction, state_send2->hash ()); ASSERT_EQ (15, confirmed.size ()); ASSERT_EQ (15, node->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (16, node->ledger.cache.cemented_count); + ASSERT_EQ (16, node->ledger.cemented_count ()); ASSERT_TRUE (node->ledger.block_confirmed (transaction, state_send2->hash ())); nano::confirmation_height_info confirmation_height_info; @@ -749,7 +749,7 @@ TEST (ledger_confirm, observers) node1->ledger.confirm (transaction, send1->hash ()); ASSERT_TRUE (node1->ledger.block_confirmed (transaction, send1->hash ())); ASSERT_EQ (1, node1->stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in)); - ASSERT_EQ (2, node1->ledger.cache.cemented_count); + ASSERT_EQ (2, node1->ledger.cemented_count ()); } TEST (ledger_confirm, election_winner_details_clearing_node_process_confirmed) @@ -784,7 +784,7 @@ TEST (ledger_confirm, pruned_source) nano::stats stats; nano::ledger ledger (*store, stats, nano::dev::constants); ledger.pruning = true; - nano::write_database_queue write_database_queue (false); + nano::store::write_queue write_queue (false); nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; nano::keypair key1, key2; nano::block_builder builder; @@ -845,6 +845,7 @@ TEST (ledger_confirm, pruned_source) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send2)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, send3)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open2)); + ledger.confirm (transaction, send2->hash ()); ASSERT_EQ (2, ledger.pruning_action (transaction, send2->hash (), 2)); ASSERT_FALSE (ledger.block_exists (transaction, send2->hash ())); ASSERT_FALSE (ledger.block_confirmed (transaction, open2->hash ())); @@ -867,7 +868,7 @@ TEST (ledger_confirmDeathTest, rollback_added_block) ASSERT_TRUE (!store->init_error ()); nano::stats stats; nano::ledger ledger (*store, stats, nano::dev::constants); - nano::write_database_queue write_database_queue (false); + nano::store::write_queue write_queue (false); nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; nano::keypair key1; nano::block_builder builder; diff --git a/nano/core_test/network.cpp b/nano/core_test/network.cpp index aaf475029d..871bd872af 100644 --- a/nano/core_test/network.cpp +++ b/nano/core_test/network.cpp @@ -712,10 +712,9 @@ TEST (network, duplicate_detection) TEST (network, duplicate_revert_publish) { nano::test::system system; - nano::node_flags node_flags; - node_flags.block_processor_full_size = 0; - auto & node (*system.add_node (node_flags)); - ASSERT_TRUE (node.block_processor.full ()); + nano::node_config node_config = system.default_config (); + node_config.block_processor.max_peer_queue = 0; + auto & node (*system.add_node (node_config)); nano::publish publish{ nano::dev::network_params.network, nano::dev::genesis }; std::vector bytes; { diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 388508983b..2bc7435b7e 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -278,9 +279,9 @@ TEST (node, auto_bootstrap) ASSERT_TIMELY (10s, !node1->bootstrap_initiator.in_progress ()); ASSERT_TRUE (node1->ledger.block_or_pruned_exists (send1->hash ())); // Wait block receive - ASSERT_TIMELY_EQ (5s, node1->ledger.cache.block_count, 3); + ASSERT_TIMELY_EQ (5s, node1->ledger.block_count (), 3); // Confirmation for all blocks - ASSERT_TIMELY_EQ (5s, node1->ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node1->ledger.cemented_count (), 3); } TEST (node, auto_bootstrap_reverse) @@ -428,7 +429,7 @@ TEST (node, search_receivable_pruned) // Confirmation ASSERT_TIMELY (10s, node1->active.empty () && node2->active.empty ()); ASSERT_TIMELY (5s, node1->ledger.block_confirmed (node1->store.tx_begin_read (), send2->hash ())); - ASSERT_TIMELY_EQ (5s, node2->ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node2->ledger.cemented_count (), 3); system.wallet (0)->store.erase (node1->wallets.tx_begin_write (), nano::dev::genesis_key.pub); // Pruning @@ -436,7 +437,7 @@ TEST (node, search_receivable_pruned) auto transaction (node2->store.tx_begin_write ()); ASSERT_EQ (1, node2->ledger.pruning_action (transaction, send1->hash (), 1)); } - ASSERT_EQ (1, node2->ledger.cache.pruned_count); + ASSERT_EQ (1, node2->ledger.pruned_count ()); ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send1->hash ())); // true for pruned // Receive pruned block @@ -526,6 +527,7 @@ TEST (node, expire) ASSERT_TRUE (node0.expired ()); } +// This test is racy, there is no guarantee that the election won't be confirmed until all forks are fully processed TEST (node, fork_publish) { nano::test::system system (1); @@ -672,6 +674,7 @@ TEST (node, fork_keep) ASSERT_TRUE (node2.ledger.block_exists (transaction1, send1->hash ())); } +// This test is racy, there is no guarantee that the election won't be confirmed until all forks are fully processed TEST (node, fork_flip) { nano::test::system system (2); @@ -697,8 +700,7 @@ TEST (node, fork_flip) .work (*system.work.generate (nano::dev::genesis->hash ())) .build (); nano::publish publish2{ nano::dev::network_params.network, send2 }; - auto ignored_channel{ std::make_shared (node1, std::weak_ptr ()) }; - + auto ignored_channel = nano::test::fake_channel (node1); node1.network.inbound (publish1, ignored_channel); node2.network.inbound (publish2, ignored_channel); ASSERT_TIMELY_EQ (5s, 1, node1.active.size ()); @@ -1278,7 +1280,7 @@ TEST (node, DISABLED_broadcast_elected) auto election (node->active.election (block->qualified_root ())); ASSERT_NE (nullptr, election); election->force_confirm (); - ASSERT_TIMELY_EQ (5s, 4, node->ledger.cache.cemented_count) + ASSERT_TIMELY_EQ (5s, 4, node->ledger.cemented_count ()) } system.wallet (0)->insert_adhoc (rep_big.prv); @@ -1593,7 +1595,7 @@ TEST (node, unconfirmed_send) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); ASSERT_TIMELY (5s, node2.block_confirmed (send3->hash ())); ASSERT_TIMELY (5s, node1.block_confirmed (send3->hash ())); - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 7); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 7); ASSERT_TIMELY_EQ (5s, node1.balance (nano::dev::genesis_key.pub), nano::dev::constants.genesis_amount); } @@ -1915,7 +1917,7 @@ TEST (node, local_votes_cache) std::shared_ptr election; ASSERT_TIMELY (5s, election = node.active.election (send2->qualified_root ())); election->force_confirm (); - ASSERT_TIMELY_EQ (3s, node.ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (3s, node.ledger.cemented_count (), 3); system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); nano::confirm_req message1{ nano::dev::network_params.network, send1->hash (), send1->root () }; nano::confirm_req message2{ nano::dev::network_params.network, send2->hash (), send2->root () }; @@ -2663,7 +2665,7 @@ TEST (node, block_processor_full) { nano::test::system system; nano::node_flags node_flags; - node_flags.force_use_write_database_queue = true; + node_flags.force_use_write_queue = true; node_flags.block_processor_full_size = 3; auto & node = *system.add_node (nano::node_config (system.get_available_port ()), node_flags); nano::state_block_builder builder; @@ -2709,7 +2711,7 @@ TEST (node, block_processor_half_full) nano::test::system system; nano::node_flags node_flags; node_flags.block_processor_full_size = 6; - node_flags.force_use_write_database_queue = true; + node_flags.force_use_write_queue = true; auto & node = *system.add_node (nano::node_config (system.get_available_port ()), node_flags); nano::state_block_builder builder; auto send1 = builder.make_block () @@ -2740,7 +2742,7 @@ TEST (node, block_processor_half_full) .work (*node.work_generate_blocking (send2->hash ())) .build (); // The write guard prevents block processor doing any writes - auto write_guard = node.write_database_queue.wait (nano::writer::testing); + auto write_guard = node.store.write_queue.wait (nano::store::writer::testing); node.block_processor.add (send1); ASSERT_FALSE (node.block_processor.half_full ()); node.block_processor.add (send2); @@ -3082,7 +3084,7 @@ TEST (node, rollback_vote_self) // Process and mark the first 2 blocks as confirmed to allow voting ASSERT_TRUE (nano::test::process (node, { send1, open })); ASSERT_TRUE (nano::test::start_elections (system, node, { send1, open }, true)); - ASSERT_TIMELY_EQ (5s, node.ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node.ledger.cemented_count (), 3); // wait until the rep weights have caught up with the weight transfer ASSERT_TIMELY_EQ (5s, nano::dev::constants.genesis_amount / 2, node.weight (key.pub)); @@ -3097,7 +3099,7 @@ TEST (node, rollback_vote_self) { // The write guard prevents the block processor from performing the rollback - auto write_guard = node.write_database_queue.wait (nano::writer::testing); + auto write_guard = node.store.write_queue.wait (nano::store::writer::testing); ASSERT_EQ (0, election->votes_with_weight ().size ()); // Vote with key to switch the winner @@ -3364,7 +3366,7 @@ TEST (node, dependency_graph) { key3_receive->hash (), { key3_open->hash (), key1_send2->hash () } }, { key3_epoch->hash (), { key3_receive->hash () } }, }; - ASSERT_EQ (node.ledger.cache.block_count - 2, dependency_graph.size ()); + ASSERT_EQ (node.ledger.block_count () - 2, dependency_graph.size ()); // Start an election for the first block of the dependency graph, and ensure all blocks are eventually confirmed system.wallet (0)->insert_adhoc (nano::dev::genesis_key.prv); @@ -3390,9 +3392,9 @@ TEST (node, dependency_graph) }); EXPECT_FALSE (error); - return error || node.ledger.cache.cemented_count == node.ledger.cache.block_count; + return error || node.ledger.cemented_count () == node.ledger.block_count (); })); - ASSERT_EQ (node.ledger.cache.cemented_count, node.ledger.cache.block_count); + ASSERT_EQ (node.ledger.cemented_count (), node.ledger.block_count ()); ASSERT_TIMELY (5s, node.active.empty ()); } @@ -3559,8 +3561,8 @@ TEST (node, dependency_graph_frontier) ASSERT_TIMELY (10s, node2.active.active (gen_send1->qualified_root ())); node1.start_election (gen_send1); - ASSERT_TIMELY_EQ (15s, node1.ledger.cache.cemented_count, node1.ledger.cache.block_count); - ASSERT_TIMELY_EQ (15s, node2.ledger.cache.cemented_count, node2.ledger.cache.block_count); + ASSERT_TIMELY_EQ (15s, node1.ledger.cemented_count (), node1.ledger.block_count ()); + ASSERT_TIMELY_EQ (15s, node2.ledger.cemented_count (), node2.ledger.block_count ()); } namespace nano @@ -3734,11 +3736,11 @@ TEST (node, pruning_automatic) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); // Check pruning result - ASSERT_EQ (3, node1.ledger.cache.block_count); - ASSERT_TIMELY_EQ (5s, node1.ledger.cache.pruned_count, 1); + ASSERT_EQ (3, node1.ledger.block_count ()); + ASSERT_TIMELY_EQ (5s, node1.ledger.pruned_count (), 1); ASSERT_TIMELY_EQ (5s, node1.store.pruned.count (node1.store.tx_begin_read ()), 1); - ASSERT_EQ (1, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 })); } @@ -3785,19 +3787,19 @@ TEST (node, pruning_age) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); // Three blocks in total, nothing pruned yet - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with default age 1 day node1.ledger_pruning (1, true); - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with max age 0 node1.config.max_pruning_age = std::chrono::seconds{ 0 }; node1.ledger_pruning (1, true); - ASSERT_EQ (1, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 })); } @@ -3846,19 +3848,19 @@ TEST (node, pruning_depth) ASSERT_TIMELY (5s, node1.block_confirmed (send2->hash ())); // Three blocks in total, nothing pruned yet - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with default depth (unlimited) node1.ledger_pruning (1, true); - ASSERT_EQ (0, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (0, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); // Pruning with max depth 1 node1.config.max_pruning_depth = 1; node1.ledger_pruning (1, true); - ASSERT_EQ (1, node1.ledger.cache.pruned_count); - ASSERT_EQ (3, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.pruned_count ()); + ASSERT_EQ (3, node1.ledger.block_count ()); ASSERT_TRUE (nano::test::block_or_pruned_all_exists (node1, { nano::dev::genesis, send1, send2 })); } diff --git a/nano/core_test/rep_crawler.cpp b/nano/core_test/rep_crawler.cpp index 1addde26ca..293733a76a 100644 --- a/nano/core_test/rep_crawler.cpp +++ b/nano/core_test/rep_crawler.cpp @@ -252,7 +252,7 @@ TEST (rep_crawler, recently_confirmed) { nano::test::system system (1); auto & node1 (*system.nodes[0]); - ASSERT_EQ (1, node1.ledger.cache.block_count); + ASSERT_EQ (1, node1.ledger.block_count ()); auto const block = nano::dev::genesis; node1.active.recently_confirmed.put (block->qualified_root (), block->hash ()); auto & node2 (*system.add_node ()); diff --git a/nano/core_test/request_aggregator.cpp b/nano/core_test/request_aggregator.cpp index 99d429ab0b..0730a49da6 100644 --- a/nano/core_test/request_aggregator.cpp +++ b/nano/core_test/request_aggregator.cpp @@ -293,7 +293,7 @@ TEST (request_aggregator, split) std::shared_ptr election; ASSERT_TIMELY (5s, election = node.active.election (blocks.back ()->qualified_root ())); election->force_confirm (); - ASSERT_TIMELY_EQ (5s, max_vbh + 2, node.ledger.cache.cemented_count); + ASSERT_TIMELY_EQ (5s, max_vbh + 2, node.ledger.cemented_count ()); ASSERT_EQ (max_vbh + 1, request.size ()); auto client = std::make_shared (node); std::shared_ptr dummy_channel = std::make_shared (node, client); diff --git a/nano/core_test/socket.cpp b/nano/core_test/socket.cpp index 51501e3cf1..857242f3b1 100644 --- a/nano/core_test/socket.cpp +++ b/nano/core_test/socket.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/core_test/toml.cpp b/nano/core_test/toml.cpp index ae6f432a7e..7de673fbd8 100644 --- a/nano/core_test/toml.cpp +++ b/nano/core_test/toml.cpp @@ -117,6 +117,7 @@ TEST (toml, daemon_config_deserialize_defaults) std::stringstream ss; ss << R"toml( [node] + [node.block_processor] [node.diagnostics.txn_tracking] [node.httpcallback] [node.ipc.local] @@ -254,6 +255,12 @@ TEST (toml, daemon_config_deserialize_defaults) ASSERT_EQ (conf.node.vote_cache.max_size, defaults.node.vote_cache.max_size); ASSERT_EQ (conf.node.vote_cache.max_voters, defaults.node.vote_cache.max_voters); + + ASSERT_EQ (conf.node.block_processor.max_peer_queue, defaults.node.block_processor.max_peer_queue); + ASSERT_EQ (conf.node.block_processor.max_system_queue, defaults.node.block_processor.max_system_queue); + ASSERT_EQ (conf.node.block_processor.priority_live, defaults.node.block_processor.priority_live); + ASSERT_EQ (conf.node.block_processor.priority_bootstrap, defaults.node.block_processor.priority_bootstrap); + ASSERT_EQ (conf.node.block_processor.priority_local, defaults.node.block_processor.priority_local); } TEST (toml, optional_child) @@ -432,6 +439,13 @@ TEST (toml, daemon_config_deserialize_no_defaults) backlog_scan_batch_size = 999 backlog_scan_frequency = 999 + [node.block_processor] + max_peer_queue = 999 + max_system_queue = 999 + priority_live = 999 + priority_bootstrap = 999 + priority_local = 999 + [node.diagnostics.txn_tracking] enable = true ignore_writes_below_block_processor_max_time = false @@ -680,6 +694,12 @@ TEST (toml, daemon_config_deserialize_no_defaults) ASSERT_NE (conf.node.vote_cache.max_size, defaults.node.vote_cache.max_size); ASSERT_NE (conf.node.vote_cache.max_voters, defaults.node.vote_cache.max_voters); + + ASSERT_NE (conf.node.block_processor.max_peer_queue, defaults.node.block_processor.max_peer_queue); + ASSERT_NE (conf.node.block_processor.max_system_queue, defaults.node.block_processor.max_system_queue); + ASSERT_NE (conf.node.block_processor.priority_live, defaults.node.block_processor.priority_live); + ASSERT_NE (conf.node.block_processor.priority_bootstrap, defaults.node.block_processor.priority_bootstrap); + ASSERT_NE (conf.node.block_processor.priority_local, defaults.node.block_processor.priority_local); } /** There should be no required values **/ diff --git a/nano/core_test/wallet.cpp b/nano/core_test/wallet.cpp index ef40fe8c72..05d49392fa 100644 --- a/nano/core_test/wallet.cpp +++ b/nano/core_test/wallet.cpp @@ -1186,7 +1186,7 @@ TEST (wallet, search_receivable) wallet.insert_adhoc (nano::dev::genesis_key.prv); // Pending search should create the receive block - ASSERT_EQ (2, node.ledger.cache.block_count); + ASSERT_EQ (2, node.ledger.block_count ()); ASSERT_FALSE (wallet.search_receivable (wallet.wallets.tx_begin_read ())); ASSERT_TIMELY_EQ (3s, node.balance (nano::dev::genesis_key.pub), nano::dev::constants.genesis_amount); auto receive_hash = node.ledger.latest (node.store.tx_begin_read (), nano::dev::genesis_key.pub); @@ -1220,12 +1220,12 @@ TEST (wallet, receive_pruned) auto send2 = wallet1.send_action (nano::dev::genesis_key.pub, key.pub, 1, 1); // Pruning - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 3); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 3); { auto transaction = node2.store.tx_begin_write (); ASSERT_EQ (1, node2.ledger.pruning_action (transaction, send1->hash (), 2)); } - ASSERT_EQ (1, node2.ledger.cache.pruned_count); + ASSERT_EQ (1, node2.ledger.pruned_count ()); ASSERT_TRUE (node2.ledger.block_or_pruned_exists (send1->hash ())); ASSERT_FALSE (node2.ledger.block_exists (node2.store.tx_begin_read (), send1->hash ())); @@ -1234,5 +1234,5 @@ TEST (wallet, receive_pruned) auto open1 = wallet2.receive_action (send1->hash (), key.pub, amount, send1->destination (), 1); ASSERT_NE (nullptr, open1); ASSERT_EQ (amount, node2.ledger.balance (node2.store.tx_begin_read (), open1->hash ())); - ASSERT_TIMELY_EQ (5s, node2.ledger.cache.cemented_count, 4); + ASSERT_TIMELY_EQ (5s, node2.ledger.cemented_count (), 4); } diff --git a/nano/core_test/wallets.cpp b/nano/core_test/wallets.cpp index fd5c54318b..c692b177f2 100644 --- a/nano/core_test/wallets.cpp +++ b/nano/core_test/wallets.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -243,7 +244,7 @@ TEST (wallets, search_receivable) wallet->insert_adhoc (nano::dev::genesis_key.prv); // Pending search should create the receive block - ASSERT_EQ (2, node.ledger.cache.block_count); + ASSERT_EQ (2, node.ledger.block_count ()); if (search_all) { node.wallets.search_receivable_all (); diff --git a/nano/lib/stats_enums.hpp b/nano/lib/stats_enums.hpp index 9d1b6920ba..3bfc0e0097 100644 --- a/nano/lib/stats_enums.hpp +++ b/nano/lib/stats_enums.hpp @@ -41,6 +41,7 @@ enum class type : uint8_t blockprocessor, blockprocessor_source, blockprocessor_result, + blockprocessor_overfill, bootstrap_server, active, active_started, @@ -86,6 +87,7 @@ enum class detail : uint8_t success, unknown, cache, + queue_overflow, // processing queue queue, diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 55e6d7f5c9..06fe4b9a96 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -169,7 +170,7 @@ int main (int argc, char * const * argv) auto const & hardcoded = bootstrap_weights.second; auto const hardcoded_height = bootstrap_weights.first; auto const ledger_unfiltered = node->ledger.cache.rep_weights.get_rep_amounts (); - auto const ledger_height = node->ledger.cache.block_count.load (); + auto const ledger_height = node->ledger.block_count (); auto get_total = [] (decltype (bootstrap_weights.second) const & reps) -> nano::uint128_union { return std::accumulate (reps.begin (), reps.end (), nano::uint128_t{ 0 }, [] (auto sum, auto const & rep) { return sum + rep.second; }); @@ -327,7 +328,7 @@ int main (int argc, char * const * argv) node_flags.generate_cache.block_count = true; nano::inactive_node inactive_node (data_path, node_flags); auto node = inactive_node.node; - std::cout << boost::str (boost::format ("Block count: %1%\n") % node->ledger.cache.block_count); + std::cout << boost::str (boost::format ("Block count: %1%\n") % node->ledger.block_count ()); } else if (vm.count ("debug_bootstrap_generate")) { @@ -448,7 +449,7 @@ int main (int argc, char * const * argv) nano::update_flags (node_flags, vm); node_flags.generate_cache.account_count = true; nano::inactive_node inactive_node (data_path, node_flags); - std::cout << boost::str (boost::format ("Frontier count: %1%\n") % inactive_node.node->ledger.cache.account_count); + std::cout << boost::str (boost::format ("Frontier count: %1%\n") % inactive_node.node->ledger.account_count ()); } else if (vm.count ("debug_profile_kdf")) { @@ -987,14 +988,14 @@ int main (int argc, char * const * argv) blocks.pop_front (); } nano::timer timer_l (nano::timer_state::started); - while (node->ledger.cache.block_count != max_blocks + 1) + while (node->ledger.block_count () != max_blocks + 1) { std::this_thread::sleep_for (std::chrono::milliseconds (10)); // Message each 15 seconds if (timer_l.after_deadline (std::chrono::seconds (15))) { timer_l.restart (); - std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked), %3% remaining") % node->ledger.cache.block_count % node->unchecked.count () % node->block_processor.size ()) << std::endl; + std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked), %3% remaining") % node->ledger.block_count () % node->unchecked.count () % node->block_processor.size ()) << std::endl; } } @@ -1002,7 +1003,7 @@ int main (int argc, char * const * argv) auto time (std::chrono::duration_cast (end - begin).count ()); node->stop (); std::cout << boost::str (boost::format ("%|1$ 12d| us \n%2% blocks per second\n") % time % (max_blocks * 1000000 / time)); - release_assert (node->ledger.cache.block_count == max_blocks + 1); + release_assert (node->ledger.block_count () == max_blocks + 1); } else if (vm.count ("debug_profile_votes")) { @@ -1202,12 +1203,12 @@ int main (int argc, char * const * argv) node1->block_processor.add (block); } auto iteration (0); - while (node1->ledger.cache.block_count != count * 2 + 1) + while (node1->ledger.block_count () != count * 2 + 1) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); if (++iteration % 60 == 0) { - std::cout << boost::str (boost::format ("%1% blocks processed\n") % node1->ledger.cache.block_count); + std::cout << boost::str (boost::format ("%1% blocks processed\n") % node1->ledger.block_count ()); } } // Confirm blocks for node1 @@ -1215,12 +1216,12 @@ int main (int argc, char * const * argv) { node1->confirming_set.add (block->hash ()); } - while (node1->ledger.cache.cemented_count != node1->ledger.cache.block_count) + while (node1->ledger.cemented_count () != node1->ledger.block_count ()) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); if (++iteration % 60 == 0) { - std::cout << boost::str (boost::format ("%1% blocks cemented\n") % node1->ledger.cache.cemented_count); + std::cout << boost::str (boost::format ("%1% blocks cemented\n") % node1->ledger.cemented_count ()); } } @@ -1250,12 +1251,12 @@ int main (int argc, char * const * argv) node2->block_processor.add (block); blocks.pop_front (); } - while (node2->ledger.cache.block_count != count * 2 + 1) + while (node2->ledger.block_count () != count * 2 + 1) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); if (++iteration % 60 == 0) { - std::cout << boost::str (boost::format ("%1% blocks processed\n") % node2->ledger.cache.block_count); + std::cout << boost::str (boost::format ("%1% blocks processed\n") % node2->ledger.block_count ()); } } // Insert representative @@ -1274,12 +1275,12 @@ int main (int argc, char * const * argv) auto begin (std::chrono::high_resolution_clock::now ()); std::cout << boost::str (boost::format ("Starting confirming %1% frontiers (test node)\n") % (count + 1)); // Wait for full frontiers confirmation - while (node2->ledger.cache.cemented_count != node2->ledger.cache.block_count) + while (node2->ledger.cemented_count () != node2->ledger.block_count ()) { std::this_thread::sleep_for (std::chrono::milliseconds (25)); if (++iteration % 1200 == 0) { - std::cout << boost::str (boost::format ("%1% blocks confirmed\n") % node2->ledger.cache.cemented_count); + std::cout << boost::str (boost::format ("%1% blocks confirmed\n") % node2->ledger.cemented_count ()); } } auto end (std::chrono::high_resolution_clock::now ()); @@ -1793,7 +1794,7 @@ int main (int argc, char * const * argv) nano::inactive_node inactive_node (data_path, node_flags); auto source_node = inactive_node.node; auto transaction (source_node->store.tx_begin_read ()); - block_count = source_node->ledger.cache.block_count; + block_count = source_node->ledger.block_count (); std::cout << boost::str (boost::format ("Performing bootstrap emulation, %1% blocks in ledger...") % block_count) << std::endl; for (auto i (source_node->store.account.begin (transaction)), n (source_node->store.account.end ()); i != n; ++i) { @@ -1824,7 +1825,7 @@ int main (int argc, char * const * argv) } } nano::timer timer_l (nano::timer_state::started); - while (node.node->ledger.cache.block_count != block_count) + while (node.node->ledger.block_count () != block_count) { std::this_thread::sleep_for (std::chrono::milliseconds (500)); // Add epoch open blocks again if required @@ -1839,7 +1840,7 @@ int main (int argc, char * const * argv) if (timer_l.after_deadline (std::chrono::seconds (60))) { timer_l.restart (); - std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked)") % node.node->ledger.cache.block_count % node.node->unchecked.count ()) << std::endl; + std::cout << boost::str (boost::format ("%1% (%2%) blocks processed (unchecked)") % node.node->ledger.block_count () % node.node->unchecked.count ()) << std::endl; } } @@ -1849,7 +1850,7 @@ int main (int argc, char * const * argv) auto seconds (time / us_in_second); nano::remove_temporary_directories (); std::cout << boost::str (boost::format ("%|1$ 12d| seconds \n%2% blocks per second") % seconds % (block_count * us_in_second / time)) << std::endl; - release_assert (node.node->ledger.cache.block_count == block_count); + release_assert (node.node->ledger.block_count () == block_count); } else if (vm.count ("debug_peers")) { @@ -1868,7 +1869,7 @@ int main (int argc, char * const * argv) node_flags.generate_cache.cemented_count = true; nano::update_flags (node_flags, vm); nano::inactive_node node (data_path, node_flags); - std::cout << "Total cemented block count: " << node.node->ledger.cache.cemented_count << std::endl; + std::cout << "Total cemented block count: " << node.node->ledger.cemented_count () << std::endl; } else if (vm.count ("debug_prune")) { @@ -1993,20 +1994,6 @@ int main (int argc, char * const * argv) output_account_version_number (i, unopened_account_version_totals[i]); } } - else if (vm.count ("debug_unconfirmed_frontiers")) - { - auto inactive_node = nano::default_inactive_node (data_path, vm); - auto node = inactive_node->node; - - auto unconfirmed_frontiers = node->ledger.unconfirmed_frontiers (); - std::cout << "Account: Height delta | Frontier | Confirmed frontier\n"; - for (auto const & [height_delta, unconfirmed_info] : unconfirmed_frontiers) - { - std::cout << (boost::format ("%1%: %2% %3% %4%\n") % unconfirmed_info.account.to_account () % height_delta % unconfirmed_info.frontier.to_string () % unconfirmed_info.cemented_frontier.to_string ()).str (); - } - - std::cout << "\nNumber of unconfirmed frontiers: " << unconfirmed_frontiers.size () << std::endl; - } else if (vm.count ("version")) { std::cout << "Version " << NANO_VERSION_STRING << "\n" diff --git a/nano/node/CMakeLists.txt b/nano/node/CMakeLists.txt index 7600ef9d18..aa7b9a774a 100644 --- a/nano/node/CMakeLists.txt +++ b/nano/node/CMakeLists.txt @@ -76,6 +76,7 @@ add_library( election_status.hpp epoch_upgrader.hpp epoch_upgrader.cpp + fair_queue.hpp ipc/action_handler.hpp ipc/action_handler.cpp ipc/flatbuffers_handler.hpp @@ -106,6 +107,10 @@ add_library( node_observers.cpp node_rpc_config.hpp node_rpc_config.cpp + node_wrapper.hpp + node_wrapper.cpp + inactive_node.hpp + inactive_node.cpp node.hpp node.cpp online_reps.hpp @@ -179,8 +184,6 @@ add_library( websocketconfig.cpp websocket_stream.hpp websocket_stream.cpp - write_database_queue.hpp - write_database_queue.cpp messages.hpp messages.cpp xorshift.hpp) diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 708078a53a..5a95e0b8d6 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -111,7 +111,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr= node.ledger.bootstrap_weight_max_blocks; + bool cemented_bootstrap_count_reached = node.ledger.cemented_count () >= node.ledger.bootstrap_weight_max_blocks; bool was_active = status.type == nano::election_status_type::active_confirmed_quorum || status.type == nano::election_status_type::active_confirmation_height; // Next-block activations are only done for blocks with previously active elections diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 5e1e4b8cda..bc10d115c8 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include @@ -17,7 +17,7 @@ */ nano::block_processor::context::context (std::shared_ptr block, nano::block_source source_a) : - block{ block }, + block{ std::move (block) }, source{ source_a } { debug_assert (source != nano::block_source::unknown); @@ -37,9 +37,9 @@ void nano::block_processor::context::set_result (result_t const & result) * block_processor */ -nano::block_processor::block_processor (nano::node & node_a, nano::write_database_queue & write_database_queue_a) : +nano::block_processor::block_processor (nano::node & node_a) : + config{ node_a.config.block_processor }, node (node_a), - write_database_queue (write_database_queue_a), next_log (std::chrono::steady_clock::now ()) { batch_processed.add ([this] (auto const & items) { @@ -49,6 +49,32 @@ nano::block_processor::block_processor (nano::node & node_a, nano::write_databas block_processed.notify (result, context); } }); + + queue.max_size_query = [this] (auto const & origin) { + switch (origin.source) + { + case nano::block_source::live: + return config.max_peer_queue; + default: + return config.max_system_queue; + } + }; + + queue.priority_query = [this] (auto const & origin) -> size_t { + switch (origin.source) + { + case nano::block_source::live: + return config.priority_live; + case nano::block_source::bootstrap: + case nano::block_source::bootstrap_legacy: + case nano::block_source::unchecked: + return config.priority_bootstrap; + case nano::block_source::local: + return config.priority_local; + default: + return 1; + } + }; } nano::block_processor::~block_processor () @@ -80,39 +106,44 @@ void nano::block_processor::stop () } } -std::size_t nano::block_processor::size () +// TODO: Remove and replace all checks with calls to size (block_source) +std::size_t nano::block_processor::size () const +{ + nano::unique_lock lock{ mutex }; + return queue.total_size (); +} + +std::size_t nano::block_processor::size (nano::block_source source) const { nano::unique_lock lock{ mutex }; - return blocks.size () + forced.size (); + return queue.size ({ source }); } -bool nano::block_processor::full () +bool nano::block_processor::full () const { return size () >= node.flags.block_processor_full_size; } -bool nano::block_processor::half_full () +bool nano::block_processor::half_full () const { return size () >= node.flags.block_processor_full_size / 2; } -void nano::block_processor::add (std::shared_ptr const & block, block_source const source) +bool nano::block_processor::add (std::shared_ptr const & block, block_source const source, std::shared_ptr const & channel) { - if (full ()) - { - node.stats.inc (nano::stat::type::blockprocessor, nano::stat::detail::overfill); - return; - } if (node.network_params.work.validate_entry (*block)) // true => error { node.stats.inc (nano::stat::type::blockprocessor, nano::stat::detail::insufficient_work); - return; + return false; // Not added } node.stats.inc (nano::stat::type::blockprocessor, nano::stat::detail::process); - node.logger.debug (nano::log::type::blockprocessor, "Processing block (async): {} (source: {})", block->hash ().to_string (), to_string (source)); + node.logger.debug (nano::log::type::blockprocessor, "Processing block (async): {} (source: {} {})", + block->hash ().to_string (), + to_string (source), + channel ? channel->to_string () : ""); // TODO: Lazy eval - add_impl (context{ block, source }); + return add_impl (context{ block, source }, channel); } std::optional nano::block_processor::add_blocking (std::shared_ptr const & block, block_source const source) @@ -147,11 +178,27 @@ void nano::block_processor::force (std::shared_ptr const & block_a) node.stats.inc (nano::stat::type::blockprocessor, nano::stat::detail::force); node.logger.debug (nano::log::type::blockprocessor, "Forcing block: {}", block_a->hash ().to_string ()); + add_impl (context{ block_a, block_source::forced }); +} + +bool nano::block_processor::add_impl (context ctx, std::shared_ptr const & channel) +{ + auto const source = ctx.source; + bool added = false; { - nano::lock_guard lock{ mutex }; - forced.emplace_back (context{ block_a, block_source::forced }); + nano::lock_guard guard{ mutex }; + added = queue.push (std::move (ctx), { source, channel }); } - condition.notify_all (); + if (added) + { + condition.notify_all (); + } + else + { + node.stats.inc (nano::stat::type::blockprocessor, nano::stat::detail::overfill); + node.stats.inc (nano::stat::type::blockprocessor_overfill, to_stat_detail (source)); + } + return added; } void nano::block_processor::rollback_competitor (store::write_transaction const & transaction, nano::block const & block) @@ -196,7 +243,7 @@ void nano::block_processor::run () nano::unique_lock lock{ mutex }; while (!stopped) { - if (have_blocks_ready ()) + if (!queue.empty ()) { lock.unlock (); @@ -233,47 +280,16 @@ bool nano::block_processor::should_log () return result; } -bool nano::block_processor::have_blocks_ready () -{ - debug_assert (!mutex.try_lock ()); - return !blocks.empty () || !forced.empty (); -} - -bool nano::block_processor::have_blocks () -{ - debug_assert (!mutex.try_lock ()); - return have_blocks_ready (); -} - -void nano::block_processor::add_impl (context ctx) -{ - release_assert (ctx.source != nano::block_source::forced); - { - nano::lock_guard guard{ mutex }; - blocks.emplace_back (std::move (ctx)); - } - condition.notify_all (); -} - auto nano::block_processor::next () -> context { debug_assert (!mutex.try_lock ()); - debug_assert (!blocks.empty () || !forced.empty ()); // This should be checked before calling next - - if (!forced.empty ()) - { - auto entry = std::move (forced.front ()); - release_assert (entry.source == nano::block_source::forced); - forced.pop_front (); - return entry; - } + debug_assert (!queue.empty ()); // This should be checked before calling next - if (!blocks.empty ()) + if (!queue.empty ()) { - auto entry = std::move (blocks.front ()); - release_assert (entry.source != nano::block_source::forced); - blocks.pop_front (); - return entry; + auto [request, origin] = queue.next (); + release_assert (origin.source != nano::block_source::forced || request.source == nano::block_source::forced); + return std::move (request); } release_assert (false, "next() called when no blocks are ready"); @@ -283,25 +299,30 @@ auto nano::block_processor::process_batch (nano::unique_lock & lock { processed_batch_t processed; - auto scoped_write_guard = write_database_queue.wait (nano::writer::process_batch); + auto scoped_write_guard = node.store.write_queue.wait (nano::store::writer::process_batch); auto transaction (node.store.tx_begin_write ({ tables::accounts, tables::blocks, tables::pending, tables::rep_weights })); nano::timer timer_l; lock_a.lock (); + queue.periodic_update (); + timer_l.start (); + // Processing blocks unsigned number_of_blocks_processed (0), number_of_forced_processed (0); auto deadline_reached = [&timer_l, deadline = node.config.block_processor_batch_max_time] { return timer_l.after_deadline (deadline); }; auto processor_batch_reached = [&number_of_blocks_processed, max = node.flags.block_processor_batch_size] { return number_of_blocks_processed >= max; }; auto store_batch_reached = [&number_of_blocks_processed, max = node.store.max_block_write_batch_num ()] { return number_of_blocks_processed >= max; }; - while (have_blocks_ready () && (!deadline_reached () || !processor_batch_reached ()) && !store_batch_reached ()) + while (!queue.empty () && (!deadline_reached () || !processor_batch_reached ()) && !store_batch_reached ()) { // TODO: Cleaner periodical logging - if ((blocks.size () + forced.size () > 64) && should_log ()) + if (should_log ()) { - node.logger.debug (nano::log::type::blockprocessor, "{} blocks (+ {} forced) in processing queue", blocks.size (), forced.size ()); + node.logger.info (nano::log::type::blockprocessor, "{} blocks (+ {} forced) in processing queue", + queue.total_size (), + queue.size ({ nano::block_source::forced })); } auto ctx = next (); @@ -342,6 +363,7 @@ nano::block_status nano::block_processor::process_one (store::write_transaction node.stats.inc (nano::stat::type::blockprocessor_result, to_stat_detail (result)); node.stats.inc (nano::stat::type::blockprocessor_source, to_stat_detail (context.source)); + node.logger.trace (nano::log::type::blockprocessor, nano::log::detail::block_processed, nano::log::arg{ "result", result }, nano::log::arg{ "source", context.source }, @@ -437,18 +459,12 @@ void nano::block_processor::queue_unchecked (store::write_transaction const & tr std::unique_ptr nano::block_processor::collect_container_info (std::string const & name) { - std::size_t blocks_count; - std::size_t forced_count; - - { - nano::lock_guard guard{ mutex }; - blocks_count = blocks.size (); - forced_count = forced.size (); - } + nano::lock_guard guard{ mutex }; auto composite = std::make_unique (name); - composite->add_component (std::make_unique (container_info{ "blocks", blocks_count, sizeof (decltype (blocks)::value_type) })); - composite->add_component (std::make_unique (container_info{ "forced", forced_count, sizeof (decltype (forced)::value_type) })); + composite->add_component (std::make_unique (container_info{ "blocks", queue.total_size (), 0 })); + composite->add_component (std::make_unique (container_info{ "forced", queue.size ({ nano::block_source::forced }), 0 })); + composite->add_component (queue.collect_container_info ("queue")); return composite; } @@ -463,3 +479,33 @@ nano::stat::detail nano::to_stat_detail (nano::block_source type) debug_assert (value); return value.value_or (nano::stat::detail{}); } + +/* + * block_processor_config + */ + +nano::block_processor_config::block_processor_config (const nano::network_constants & network_constants) +{ +} + +nano::error nano::block_processor_config::serialize (nano::tomlconfig & toml) const +{ + toml.put ("max_peer_queue", max_peer_queue, "Maximum number of blocks to queue from network peers. \ntype:uint64"); + toml.put ("max_system_queue", max_system_queue, "Maximum number of blocks to queue from system components (local RPC, bootstrap). \ntype:uint64"); + toml.put ("priority_live", priority_live, "Priority for live network blocks. Higher priority gets processed more frequently. \ntype:uint64"); + toml.put ("priority_bootstrap", priority_bootstrap, "Priority for bootstrap blocks. Higher priority gets processed more frequently. \ntype:uint64"); + toml.put ("priority_local", priority_local, "Priority for local RPC blocks. Higher priority gets processed more frequently. \ntype:uint64"); + + return toml.get_error (); +} + +nano::error nano::block_processor_config::deserialize (nano::tomlconfig & toml) +{ + toml.get ("max_peer_queue", max_peer_queue); + toml.get ("max_system_queue", max_system_queue); + toml.get ("priority_live", priority_live); + toml.get ("priority_bootstrap", priority_bootstrap); + toml.get ("priority_local", priority_local); + + return toml.get_error (); +} diff --git a/nano/node/blockprocessor.hpp b/nano/node/blockprocessor.hpp index 0d020e8b16..2e01d591ca 100644 --- a/nano/node/blockprocessor.hpp +++ b/nano/node/blockprocessor.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -13,7 +14,6 @@ namespace nano { class block; class node; -class write_database_queue; } namespace nano::store @@ -23,7 +23,6 @@ class write_transaction; namespace nano { - enum class block_source { unknown = 0, @@ -38,6 +37,26 @@ enum class block_source std::string_view to_string (block_source); nano::stat::detail to_stat_detail (block_source); +class block_processor_config final +{ +public: + explicit block_processor_config (nano::network_constants const &); + + nano::error deserialize (nano::tomlconfig & toml); + nano::error serialize (nano::tomlconfig & toml) const; + +public: + // Maximum number of blocks to queue from network peers + size_t max_peer_queue{ 128 }; + // Maximum number of blocks to queue from system components (local RPC, bootstrap) + size_t max_system_queue{ 16 * 1024 }; + + // Higher priority gets processed more frequently + size_t priority_live{ 1 }; + size_t priority_bootstrap{ 8 }; + size_t priority_local{ 16 }; +}; + /** * Processing blocks is a potentially long IO operation. * This class isolates block insertion from other operations like servicing network operations @@ -66,21 +85,20 @@ class block_processor final }; public: - block_processor (nano::node &, nano::write_database_queue &); + block_processor (nano::node &); ~block_processor (); void start (); void stop (); - std::size_t size (); - bool full (); - bool half_full (); - void add (std::shared_ptr const &, block_source = block_source::live); + std::size_t size () const; + std::size_t size (block_source) const; + bool full () const; + bool half_full () const; + bool add (std::shared_ptr const &, block_source = block_source::live, std::shared_ptr const & channel = nullptr); std::optional add_blocking (std::shared_ptr const & block, block_source); void force (std::shared_ptr const &); bool should_log (); - bool have_blocks_ready (); - bool have_blocks (); std::unique_ptr collect_container_info (std::string const & name); @@ -103,21 +121,20 @@ class block_processor final void queue_unchecked (store::write_transaction const &, nano::hash_or_account const &); processed_batch_t process_batch (nano::unique_lock &); context next (); - void add_impl (context); + bool add_impl (context, std::shared_ptr const & channel = nullptr); private: // Dependencies + block_processor_config const & config; nano::node & node; - nano::write_database_queue & write_database_queue; private: - std::deque blocks; - std::deque forced; + nano::fair_queue queue; std::chrono::steady_clock::time_point next_log; bool stopped{ false }; nano::condition_variable condition; - nano::mutex mutex{ mutex_identifier (mutexes::block_processor) }; + mutable nano::mutex mutex{ mutex_identifier (mutexes::block_processor) }; std::thread thread; }; } diff --git a/nano/node/bootstrap/bootstrap_legacy.cpp b/nano/node/bootstrap/bootstrap_legacy.cpp index 9badee97cc..98d48e9ead 100644 --- a/nano/node/bootstrap/bootstrap_legacy.cpp +++ b/nano/node/bootstrap/bootstrap_legacy.cpp @@ -225,9 +225,9 @@ void nano::bootstrap_attempt_legacy::run () // TODO: This check / wait is a heuristic and should be improved. auto wait_start = std::chrono::steady_clock::now (); - while (!stopped && node->block_processor.size () != 0 && ((std::chrono::steady_clock::now () - wait_start) < std::chrono::seconds{ 10 })) + while (!stopped && node->block_processor.size (nano::block_source::bootstrap_legacy) != 0 && ((std::chrono::steady_clock::now () - wait_start) < std::chrono::seconds{ 10 })) { - condition.wait_for (lock, std::chrono::milliseconds{ 100 }, [this, node] { return stopped || node->block_processor.size () == 0; }); + condition.wait_for (lock, std::chrono::milliseconds{ 100 }, [this, node] { return stopped || node->block_processor.size (nano::block_source::bootstrap_legacy) == 0; }); } if (start_account.number () != std::numeric_limits::max ()) diff --git a/nano/node/bootstrap_ascending/service.cpp b/nano/node/bootstrap_ascending/service.cpp index c3e9bd2d15..66510ba272 100644 --- a/nano/node/bootstrap_ascending/service.cpp +++ b/nano/node/bootstrap_ascending/service.cpp @@ -177,7 +177,7 @@ void nano::bootstrap_ascending::service::inspect (store::transaction const & tx, void nano::bootstrap_ascending::service::wait_blockprocessor () { nano::unique_lock lock{ mutex }; - while (!stopped && block_processor.size () > config.bootstrap_ascending.block_wait_count) + while (!stopped && block_processor.size (nano::block_source::bootstrap) > config.bootstrap_ascending.block_wait_count) { condition.wait_for (lock, std::chrono::milliseconds{ config.bootstrap_ascending.throttle_wait }, [this] () { return stopped; }); // Blockprocessor is relatively slow, sleeping here instead of using conditions } @@ -485,7 +485,7 @@ std::size_t nano::bootstrap_ascending::service::compute_throttle_size () const { // Scales logarithmically with ledger block // Returns: config.throttle_coefficient * sqrt(block_count) - std::size_t size_new = config.bootstrap_ascending.throttle_coefficient * std::sqrt (ledger.cache.block_count.load ()); + std::size_t size_new = config.bootstrap_ascending.throttle_coefficient * std::sqrt (ledger.block_count ()); return size_new == 0 ? 16 : size_new; } diff --git a/nano/node/cli.cpp b/nano/node/cli.cpp index 449bb755a7..df480e560b 100644 --- a/nano/node/cli.cpp +++ b/nano/node/cli.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include diff --git a/nano/node/confirming_set.cpp b/nano/node/confirming_set.cpp index 5db631aed3..f9c9101e57 100644 --- a/nano/node/confirming_set.cpp +++ b/nano/node/confirming_set.cpp @@ -1,12 +1,11 @@ #include #include -#include #include #include +#include -nano::confirming_set::confirming_set (nano::ledger & ledger, nano::write_database_queue & write_queue, std::chrono::milliseconds batch_time) : +nano::confirming_set::confirming_set (nano::ledger & ledger, std::chrono::milliseconds batch_time) : ledger{ ledger }, - write_queue{ write_queue }, batch_time{ batch_time } { } @@ -72,7 +71,7 @@ void nano::confirming_set::run () for (auto i = processing.begin (), n = processing.end (); !stopped && i != n;) { lock.unlock (); // Waiting for db write is potentially slow - auto guard = write_queue.wait (nano::writer::confirmation_height); + auto guard = ledger.store.write_queue.wait (nano::store::writer::confirmation_height); auto tx = ledger.store.tx_begin_write ({ nano::tables::confirmation_height }); lock.lock (); // Process items in the back buffer within a single transaction for a limited amount of time diff --git a/nano/node/confirming_set.hpp b/nano/node/confirming_set.hpp index 5c5de14bd5..06feb52e11 100644 --- a/nano/node/confirming_set.hpp +++ b/nano/node/confirming_set.hpp @@ -13,7 +13,6 @@ namespace nano { class block; class ledger; -class write_database_queue; } namespace nano @@ -27,7 +26,7 @@ class confirming_set final friend class confirmation_height_pruned_source_Test; public: - confirming_set (nano::ledger & ledger, nano::write_database_queue & write_queue, std::chrono::milliseconds batch_time = std::chrono::milliseconds{ 500 }); + confirming_set (nano::ledger & ledger, std::chrono::milliseconds batch_time = std::chrono::milliseconds{ 500 }); ~confirming_set (); // Adds a block to the set of blocks to be confirmed void add (nano::block_hash const & hash); @@ -45,7 +44,6 @@ class confirming_set final private: void run (); nano::ledger & ledger; - nano::write_database_queue & write_queue; std::chrono::milliseconds batch_time; std::unordered_set set; std::unordered_set processing; diff --git a/nano/node/fair_queue.hpp b/nano/node/fair_queue.hpp new file mode 100644 index 0000000000..e13ed847c0 --- /dev/null +++ b/nano/node/fair_queue.hpp @@ -0,0 +1,357 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nano +{ +template +class fair_queue final +{ +public: + struct origin + { + Source source; + std::shared_ptr channel; + + origin (Source source, std::shared_ptr channel = nullptr) : + source{ source }, + channel{ channel } + { + } + }; + +private: + /** + * Holds user supplied source type(s) and an optional channel. This is used to uniquely identify and categorize the source of a request. + */ + struct origin_entry + { + Source source; + + // Optional is needed to distinguish between a source with no associated channel and a source with an expired channel + // TODO: Store channel as shared_ptr after networking fixes are done + std::optional> maybe_channel; + + origin_entry (Source source, std::shared_ptr channel = nullptr) : + source{ source } + { + if (channel) + { + maybe_channel = std::weak_ptr{ channel }; + } + } + + origin_entry (origin const & origin) : + origin_entry (origin.source, origin.channel) + { + } + + bool alive () const + { + if (maybe_channel) + { + if (auto channel_l = maybe_channel->lock ()) + { + return channel_l->alive (); + } + else + { + return false; + } + } + else + { + // Some sources (eg. local RPC) don't have an associated channel, never remove their queue + return true; + } + } + + // TODO: Store channel as shared_ptr to avoid this mess + auto operator<=> (origin_entry const & other) const + { + // First compare source + if (auto cmp = source <=> other.source; cmp != 0) + return cmp; + + if (maybe_channel && other.maybe_channel) + { + // Then compare channels by ownership, not by the channel's value or state + std::owner_less> less; + if (less (*maybe_channel, *other.maybe_channel)) + return std::strong_ordering::less; + if (less (*other.maybe_channel, *maybe_channel)) + return std::strong_ordering::greater; + + return std::strong_ordering::equivalent; + } + else + { + if (maybe_channel && !other.maybe_channel) + { + return std::strong_ordering::greater; + } + if (!maybe_channel && other.maybe_channel) + { + return std::strong_ordering::less; + } + return std::strong_ordering::equivalent; + } + } + + operator origin () const + { + return { source, maybe_channel ? maybe_channel->lock () : nullptr }; + } + }; + + struct entry + { + using queue_t = std::deque; + queue_t requests; + + size_t priority; + size_t max_size; + + entry (size_t max_size, size_t priority) : + priority{ priority }, + max_size{ max_size } + { + } + + Request pop () + { + release_assert (!requests.empty ()); + + auto request = std::move (requests.front ()); + requests.pop_front (); + return request; + } + + bool push (Request request) + { + if (requests.size () < max_size) + { + requests.push_back (std::move (request)); + return true; // Added + } + return false; // Dropped + } + + bool empty () const + { + return requests.empty (); + } + + size_t size () const + { + return requests.size (); + } + }; + +public: + using origin_type = origin; + using value_type = std::pair; + +public: + size_t size (origin_type source) const + { + auto it = queues.find (source); + return it == queues.end () ? 0 : it->second.size (); + } + + size_t max_size (origin_type source) const + { + auto it = queues.find (source); + return it == queues.end () ? 0 : it->second.max_size; + } + + size_t priority (origin_type source) const + { + auto it = queues.find (source); + return it == queues.end () ? 0 : it->second.priority; + } + + size_t total_size () const + { + return std::accumulate (queues.begin (), queues.end (), 0, [] (size_t total, auto const & queue) { + return total + queue.second.size (); + }); + }; + + bool empty () const + { + return std::all_of (queues.begin (), queues.end (), [] (auto const & queue) { + return queue.second.empty (); + }); + } + + size_t queues_size () const + { + return queues.size (); + } + + void clear () + { + queues.clear (); + } + + /** + * Should be called periodically to clean up stale channels and update queue priorities and max sizes + */ + bool periodic_update (std::chrono::milliseconds interval = std::chrono::milliseconds{ 1000 * 30 }) + { + if (elapsed (last_update, interval)) + { + last_update = std::chrono::steady_clock::now (); + + cleanup (); + update (); + + return true; // Updated + } + return false; // Not updated + } + + /** + * Push a request to the appropriate queue based on the source + * Request will be dropped if the queue is full + * @return true if added, false if dropped + */ + bool push (Request request, origin_type source) + { + auto it = queues.find (source); + + // Create a new queue if it doesn't exist + if (it == queues.end ()) + { + auto max_size = max_size_query (source); + auto priority = priority_query (source); + + // It's safe to not invalidate current iterator, since std::map container guarantees that iterators are not invalidated by insert operations + it = queues.emplace (source, entry{ max_size, priority }).first; + } + release_assert (it != queues.end ()); + + auto & queue = it->second; + return queue.push (std::move (request)); // True if added, false if dropped + } + +public: + using max_size_query_t = std::function; + using priority_query_t = std::function; + + max_size_query_t max_size_query{ [] (auto const & origin) { debug_assert (false, "max_size_query callback empty"); return 0; } }; + priority_query_t priority_query{ [] (auto const & origin) { debug_assert (false, "priority_query callback empty"); return 0; } }; + +public: + value_type next () + { + debug_assert (!empty ()); // Should be checked before calling next + + auto should_seek = [&, this] () { + if (iterator == queues.end ()) + { + return true; + } + auto & queue = iterator->second; + if (queue.empty ()) + { + return true; + } + // Allow up to `queue.priority` requests to be processed before moving to the next queue + if (counter >= queue.priority) + { + return true; + } + return false; + }; + + if (should_seek ()) + { + seek_next (); + } + + release_assert (iterator != queues.end ()); + + auto & source = iterator->first; + auto & queue = iterator->second; + + ++counter; + return { queue.pop (), source }; + } + + std::deque next_batch (size_t max_count) + { + // TODO: Naive implementation, could be optimized + std::deque result; + while (!empty () && result.size () < max_count) + { + result.emplace_back (next ()); + } + return result; + } + +private: + void seek_next () + { + counter = 0; + do + { + if (iterator != queues.end ()) + { + ++iterator; + } + if (iterator == queues.end ()) + { + iterator = queues.begin (); + } + release_assert (iterator != queues.end ()); + } while (iterator->second.empty ()); + } + + void cleanup () + { + // Invalidate the current iterator + iterator = queues.end (); + + erase_if (queues, [] (auto const & entry) { + return !entry.first.alive (); + }); + } + + void update () + { + for (auto & [source, queue] : queues) + { + queue.max_size = max_size_query (source); + queue.priority = priority_query (source); + } + } + +private: + std::map queues; + std::map::iterator iterator{ queues.end () }; + size_t counter{ 0 }; + + std::chrono::steady_clock::time_point last_update{}; + +public: + std::unique_ptr collect_container_info (std::string const & name) + { + auto composite = std::make_unique (name); + composite->add_component (std::make_unique (container_info{ "queues", queues.size (), sizeof (typename decltype (queues)::value_type) })); + composite->add_component (std::make_unique (container_info{ "total_size", total_size (), sizeof (typename decltype (queues)::value_type) })); + return composite; + } +}; +} diff --git a/nano/node/inactive_node.cpp b/nano/node/inactive_node.cpp new file mode 100644 index 0000000000..6ceb33ac18 --- /dev/null +++ b/nano/node/inactive_node.cpp @@ -0,0 +1,29 @@ +#include +#include +#include + +nano::inactive_node::inactive_node (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a) : + node_wrapper (path_a, config_path_a, node_flags_a), + node (node_wrapper.node) +{ + node_wrapper.node->active.stop (); +} + +nano::inactive_node::inactive_node (std::filesystem::path const & path_a, nano::node_flags const & node_flags_a) : + inactive_node (path_a, path_a, node_flags_a) +{ +} + +nano::node_flags const & nano::inactive_node_flag_defaults () +{ + static nano::node_flags node_flags; + node_flags.inactive_node = true; + node_flags.read_only = true; + node_flags.generate_cache.reps = false; + node_flags.generate_cache.cemented_count = false; + node_flags.generate_cache.unchecked_count = false; + node_flags.generate_cache.account_count = false; + node_flags.disable_bootstrap_listener = true; + node_flags.disable_tcp_realtime = true; + return node_flags; +} diff --git a/nano/node/inactive_node.hpp b/nano/node/inactive_node.hpp new file mode 100644 index 0000000000..50651d69e4 --- /dev/null +++ b/nano/node/inactive_node.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +#include + +namespace nano +{ + +class node; +class node_flags; + +class inactive_node final +{ +public: + inactive_node (std::filesystem::path const & path_a, nano::node_flags const & node_flags_a); + inactive_node (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a); + + nano::node_wrapper node_wrapper; + std::shared_ptr node; +}; + +std::unique_ptr default_inactive_node (std::filesystem::path const &, boost::program_options::variables_map const &); + +} // namespace nano \ No newline at end of file diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index ccec370e1c..7d226c4656 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1446,13 +1446,13 @@ void nano::json_handler::block_account () void nano::json_handler::block_count () { - response_l.put ("count", std::to_string (node.ledger.cache.block_count)); + response_l.put ("count", std::to_string (node.ledger.block_count ())); response_l.put ("unchecked", std::to_string (node.unchecked.count ())); - response_l.put ("cemented", std::to_string (node.ledger.cache.cemented_count)); + response_l.put ("cemented", std::to_string (node.ledger.cemented_count ())); if (node.flags.enable_pruning) { - response_l.put ("full", std::to_string (node.ledger.cache.block_count - node.ledger.cache.pruned_count)); - response_l.put ("pruned", std::to_string (node.ledger.cache.pruned_count)); + response_l.put ("full", std::to_string (node.ledger.block_count () - node.ledger.pruned_count ())); + response_l.put ("pruned", std::to_string (node.ledger.pruned_count ())); } response_errors (); } @@ -2002,6 +2002,44 @@ void nano::json_handler::confirmation_active () response_errors (); } +void nano::json_handler::election_statistics () +{ + auto active_elections = node.active.list_active (); + unsigned normal_count = 0; + unsigned hinted_count = 0; + unsigned optimistic_count = 0; + unsigned total_count = 0; + + for (auto const & election : active_elections) + { + total_count++; + switch (election->behavior ()) + { + case election_behavior::normal: + normal_count++; + break; + case election_behavior::hinted: + hinted_count++; + break; + case election_behavior::optimistic: + optimistic_count++; + break; + } + } + + auto utilization_percentage = (static_cast (total_count * 100) / node.config.active_elections_size); + std::stringstream stream; + stream << std::fixed << std::setprecision (2) << utilization_percentage; + + response_l.put ("normal", normal_count); + response_l.put ("hinted", hinted_count); + response_l.put ("optimistic", optimistic_count); + response_l.put ("total", total_count); + response_l.put ("aec_utilization_percentage", stream.str ()); + + response_errors (); +} + void nano::json_handler::confirmation_history () { boost::property_tree::ptree elections; @@ -2351,7 +2389,7 @@ void nano::json_handler::frontiers () void nano::json_handler::account_count () { - auto size (node.ledger.cache.account_count.load ()); + auto size (node.ledger.account_count ()); response_l.put ("count", std::to_string (size)); response_errors (); } @@ -5327,6 +5365,7 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map () no_arg_funcs.emplace ("delegators", &nano::json_handler::delegators); no_arg_funcs.emplace ("delegators_count", &nano::json_handler::delegators_count); no_arg_funcs.emplace ("deterministic_key", &nano::json_handler::deterministic_key); + no_arg_funcs.emplace ("election_statistics", &nano::json_handler::election_statistics); no_arg_funcs.emplace ("epoch_upgrade", &nano::json_handler::epoch_upgrade); no_arg_funcs.emplace ("frontiers", &nano::json_handler::frontiers); no_arg_funcs.emplace ("frontier_count", &nano::json_handler::account_count); diff --git a/nano/node/json_handler.hpp b/nano/node/json_handler.hpp index cc8d7b7a8e..33eca6c6c1 100644 --- a/nano/node/json_handler.hpp +++ b/nano/node/json_handler.hpp @@ -46,6 +46,7 @@ class json_handler : public std::enable_shared_from_this void accounts_pending (); void accounts_receivable (); void active_difficulty (); + void election_statistics (); void available_supply (); void block_info (); void block_confirm (); diff --git a/nano/node/make_store.cpp b/nano/node/make_store.cpp index 87c15700fe..9122a23d07 100644 --- a/nano/node/make_store.cpp +++ b/nano/node/make_store.cpp @@ -3,11 +3,11 @@ #include #include -std::unique_ptr nano::make_store (nano::logger & logger, std::filesystem::path const & path, nano::ledger_constants & constants, bool read_only, bool add_db_postfix, nano::rocksdb_config const & rocksdb_config, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, bool backup_before_upgrade) +std::unique_ptr nano::make_store (nano::logger & logger, std::filesystem::path const & path, nano::ledger_constants & constants, bool read_only, bool add_db_postfix, nano::rocksdb_config const & rocksdb_config, nano::txn_tracking_config const & txn_tracking_config_a, std::chrono::milliseconds block_processor_batch_max_time_a, nano::lmdb_config const & lmdb_config_a, bool backup_before_upgrade, bool force_use_write_queue) { if (rocksdb_config.enable) { - return std::make_unique (logger, add_db_postfix ? path / "rocksdb" : path, constants, rocksdb_config, read_only); + return std::make_unique (logger, add_db_postfix ? path / "rocksdb" : path, constants, rocksdb_config, read_only, force_use_write_queue); } return std::make_unique (logger, add_db_postfix ? path / "data.ldb" : path, constants, txn_tracking_config_a, block_processor_batch_max_time_a, lmdb_config_a, backup_before_upgrade); diff --git a/nano/node/make_store.hpp b/nano/node/make_store.hpp index 5f5b5f9372..d66db5cbfb 100644 --- a/nano/node/make_store.hpp +++ b/nano/node/make_store.hpp @@ -22,5 +22,5 @@ class component; namespace nano { -std::unique_ptr make_store (nano::logger &, std::filesystem::path const & path, nano::ledger_constants & constants, bool open_read_only = false, bool add_db_postfix = true, nano::rocksdb_config const & rocksdb_config = nano::rocksdb_config{}, nano::txn_tracking_config const & txn_tracking_config_a = nano::txn_tracking_config{}, std::chrono::milliseconds block_processor_batch_max_time_a = std::chrono::milliseconds (5000), nano::lmdb_config const & lmdb_config_a = nano::lmdb_config{}, bool backup_before_upgrade = false); +std::unique_ptr make_store (nano::logger &, std::filesystem::path const & path, nano::ledger_constants & constants, bool open_read_only = false, bool add_db_postfix = true, nano::rocksdb_config const & rocksdb_config = nano::rocksdb_config{}, nano::txn_tracking_config const & txn_tracking_config_a = nano::txn_tracking_config{}, std::chrono::milliseconds block_processor_batch_max_time_a = std::chrono::milliseconds (5000), nano::lmdb_config const & lmdb_config_a = nano::lmdb_config{}, bool backup_before_upgrade = false, bool force_use_write_queue = false); } diff --git a/nano/node/network.cpp b/nano/node/network.cpp index 95381bcad8..f7f373b10d 100644 --- a/nano/node/network.cpp +++ b/nano/node/network.cpp @@ -336,15 +336,12 @@ class network_message_visitor : public nano::message_visitor } } - void publish (nano::publish const & message_a) override + void publish (nano::publish const & message) override { - if (!node.block_processor.full ()) + bool added = node.block_processor.add (message.block, nano::block_source::live, channel); + if (!added) { - node.process_active (message_a.block); - } - else - { - node.network.publish_filter.clear (message_a.digest); + node.network.publish_filter.clear (message.digest); node.stats.inc (nano::stat::type::drop, nano::stat::detail::publish, nano::stat::dir::in); } } diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 11155e4984..3744f9dcba 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -135,7 +135,6 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy io_ctx_shared{ io_ctx_a }, io_ctx{ *io_ctx_shared }, node_id{ load_or_create_node_id (application_path_a) }, - write_database_queue (!flags_a.force_use_write_database_queue && (config_a.rocksdb_config.enable)), node_initialized_latch (1), config (config_a), network_params{ config.network_params }, @@ -146,7 +145,7 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy flags (flags_a), work (work_a), distributed_work (*this), - store_impl (nano::make_store (logger, application_path_a, network_params.ledger, flags.read_only, true, config_a.rocksdb_config, config_a.diagnostics_config.txn_tracking, config_a.block_processor_batch_max_time, config_a.lmdb_config, config_a.backup_before_upgrade)), + store_impl (nano::make_store (logger, application_path_a, network_params.ledger, flags.read_only, true, config_a.rocksdb_config, config_a.diagnostics_config.txn_tracking, config_a.block_processor_batch_max_time, config_a.lmdb_config, config_a.backup_before_upgrade, flags.force_use_write_queue)), store (*store_impl), unchecked{ config.max_unchecked_blocks, stats, flags.disable_block_processor_unchecked_deletion }, wallets_store_impl (std::make_unique (application_path_a / "wallets.ldb", config_a.lmdb_config)), @@ -172,8 +171,8 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy tcp_listener{ *tcp_listener_impl }, application_path (application_path_a), port_mapping (*this), - block_processor (*this, write_database_queue), - confirming_set_impl{ std::make_unique (ledger, write_database_queue, config.confirming_set_batch_time) }, + block_processor (*this), + confirming_set_impl{ std::make_unique (ledger, config.confirming_set_batch_time) }, confirming_set{ *confirming_set_impl }, active_impl{ std::make_unique (*this, confirming_set, block_processor) }, active{ *active_impl }, @@ -423,10 +422,10 @@ nano::node::node (std::shared_ptr io_ctx_a, std::filesy ledger.bootstrap_weight_max_blocks = bootstrap_weights.first; logger.info (nano::log::type::node, "Initial bootstrap height: {}", ledger.bootstrap_weight_max_blocks); - logger.info (nano::log::type::node, "Current ledger height: {}", ledger.cache.block_count.load ()); + logger.info (nano::log::type::node, "Current ledger height: {}", ledger.block_count ()); // Use bootstrap weights if initial bootstrap is not completed - const bool use_bootstrap_weight = ledger.cache.block_count < bootstrap_weights.first; + const bool use_bootstrap_weight = ledger.block_count () < bootstrap_weights.first; if (use_bootstrap_weight) { logger.info (nano::log::type::node, "Using predefined representative weights, since block count is less than bootstrap threshold"); @@ -555,7 +554,7 @@ std::unique_ptr nano::collect_container_info (no { auto composite = std::make_unique (name); composite->add_component (collect_container_info (node.work, "work")); - composite->add_component (collect_container_info (node.ledger, "ledger")); + composite->add_component (node.ledger.collect_container_info ("ledger")); composite->add_component (collect_container_info (node.active, "active")); composite->add_component (collect_container_info (node.bootstrap_initiator, "bootstrap_initiator")); composite->add_component (node.tcp_listener.collect_container_info ("tcp_listener")); @@ -828,7 +827,7 @@ void nano::node::ongoing_bootstrap () } // Differential bootstrap with max age (75% of all legacy attempts) uint32_t frontiers_age (std::numeric_limits::max ()); - auto bootstrap_weight_reached (ledger.cache.block_count >= ledger.bootstrap_weight_max_blocks); + auto bootstrap_weight_reached (ledger.block_count () >= ledger.bootstrap_weight_max_blocks); auto previous_bootstrap_count (stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate, nano::stat::dir::out) + stats.count (nano::stat::type::bootstrap, nano::stat::detail::initiate_legacy_age, nano::stat::dir::out)); /* - Maximum value for 25% of attempts or if block count is below preconfigured value (initial bootstrap not finished) @@ -1006,7 +1005,7 @@ void nano::node::ledger_pruning (uint64_t const batch_size_a, bool bootstrap_wei transaction_write_count = 0; if (!pruning_targets.empty () && !stopped) { - auto scoped_write_guard = write_database_queue.wait (nano::writer::pruning); + auto scoped_write_guard = store.write_queue.wait (nano::store::writer::pruning); auto write_transaction (store.tx_begin_write ({ tables::blocks, tables::pruned })); while (!pruning_targets.empty () && transaction_write_count < batch_size_a && !stopped) { @@ -1026,7 +1025,7 @@ void nano::node::ledger_pruning (uint64_t const batch_size_a, bool bootstrap_wei void nano::node::ongoing_ledger_pruning () { - auto bootstrap_weight_reached (ledger.cache.block_count >= ledger.bootstrap_weight_max_blocks); + auto bootstrap_weight_reached (ledger.block_count () >= ledger.bootstrap_weight_max_blocks); ledger_pruning (flags.block_processor_batch_size != 0 ? flags.block_processor_batch_size : 2 * 1024, bootstrap_weight_reached); auto const ledger_pruning_interval (bootstrap_weight_reached ? config.max_pruning_age : std::min (config.max_pruning_age, std::chrono::seconds (15 * 60))); auto this_l (shared ()); @@ -1353,15 +1352,15 @@ nano::telemetry_data nano::node::local_telemetry () const { nano::telemetry_data telemetry_data; telemetry_data.node_id = node_id.pub; - telemetry_data.block_count = ledger.cache.block_count; - telemetry_data.cemented_count = ledger.cache.cemented_count; + telemetry_data.block_count = ledger.block_count (); + telemetry_data.cemented_count = ledger.cemented_count (); telemetry_data.bandwidth_cap = config.bandwidth_limit; telemetry_data.protocol_version = network_params.network.protocol_version; telemetry_data.uptime = std::chrono::duration_cast (std::chrono::steady_clock::now () - startup_time).count (); telemetry_data.unchecked_count = unchecked.count (); telemetry_data.genesis_block = network_params.ledger.genesis->hash (); telemetry_data.peer_count = nano::narrow_cast (network.size ()); - telemetry_data.account_count = ledger.cache.account_count; + telemetry_data.account_count = ledger.account_count (); telemetry_data.major_version = nano::get_major_node_version (); telemetry_data.minor_version = nano::get_minor_node_version (); telemetry_data.patch_version = nano::get_patch_node_version (); @@ -1378,76 +1377,4 @@ std::string nano::node::make_logger_identifier (const nano::keypair & node_id) { // Node identifier consists of first 10 characters of node id return node_id.pub.to_node_id ().substr (0, 10); -} - -/* - * node_wrapper - */ - -nano::node_wrapper::node_wrapper (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a) : - network_params{ nano::network_constants::active_network }, - io_context (std::make_shared ()), - work{ network_params.network, 1 } -{ - /* - * @warning May throw a filesystem exception - */ - std::filesystem::create_directories (path_a); - - boost::system::error_code error_chmod; - nano::set_secure_perm_directory (path_a, error_chmod); - - nano::daemon_config daemon_config{ path_a, network_params }; - auto error = nano::read_node_config_toml (config_path_a, daemon_config, node_flags_a.config_overrides); - if (error) - { - std::cerr << "Error deserializing config file"; - if (!node_flags_a.config_overrides.empty ()) - { - std::cerr << " or --config option"; - } - std::cerr << "\n" - << error.get_message () << std::endl; - std::exit (1); - } - - auto & node_config = daemon_config.node; - node_config.peering_port = 24000; - - node = std::make_shared (io_context, path_a, node_config, work, node_flags_a); -} - -nano::node_wrapper::~node_wrapper () -{ - node->stop (); -} - -/* - * inactive_node - */ - -nano::inactive_node::inactive_node (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a) : - node_wrapper (path_a, config_path_a, node_flags_a), - node (node_wrapper.node) -{ - node_wrapper.node->active.stop (); -} - -nano::inactive_node::inactive_node (std::filesystem::path const & path_a, nano::node_flags const & node_flags_a) : - inactive_node (path_a, path_a, node_flags_a) -{ -} - -nano::node_flags const & nano::inactive_node_flag_defaults () -{ - static nano::node_flags node_flags; - node_flags.inactive_node = true; - node_flags.read_only = true; - node_flags.generate_cache.reps = false; - node_flags.generate_cache.cemented_count = false; - node_flags.generate_cache.unchecked_count = false; - node_flags.generate_cache.account_count = false; - node_flags.disable_bootstrap_listener = true; - node_flags.disable_tcp_realtime = true; - return node_flags; -} +} \ No newline at end of file diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 01d385af2e..5eeb08c1c5 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include @@ -138,7 +137,6 @@ class node final : public std::enable_shared_from_this public: const nano::keypair node_id; - nano::write_database_queue write_database_queue; std::shared_ptr io_ctx_shared; boost::asio::io_context & io_ctx; boost::latch node_initialized_latch; @@ -239,26 +237,4 @@ std::unique_ptr collect_container_info (node & node, s nano::node_flags const & inactive_node_flag_defaults (); -class node_wrapper final -{ -public: - node_wrapper (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a); - ~node_wrapper (); - - nano::network_params network_params; - std::shared_ptr io_context; - nano::work_pool work; - std::shared_ptr node; -}; - -class inactive_node final -{ -public: - inactive_node (std::filesystem::path const & path_a, nano::node_flags const & node_flags_a); - inactive_node (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a); - - nano::node_wrapper node_wrapper; - std::shared_ptr node; -}; -std::unique_ptr default_inactive_node (std::filesystem::path const &, boost::program_options::variables_map const &); -} +} \ No newline at end of file diff --git a/nano/node/node_wrapper.cpp b/nano/node/node_wrapper.cpp new file mode 100644 index 0000000000..5f4f1a17b9 --- /dev/null +++ b/nano/node/node_wrapper.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +nano::node_wrapper::node_wrapper (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a) : + network_params{ nano::network_constants::active_network }, + io_context (std::make_shared ()), + work{ network_params.network, 1 } +{ + /* + * @warning May throw a filesystem exception + */ + std::filesystem::create_directories (path_a); + + boost::system::error_code error_chmod; + nano::set_secure_perm_directory (path_a, error_chmod); + + nano::daemon_config daemon_config{ path_a, network_params }; + auto error = nano::read_node_config_toml (config_path_a, daemon_config, node_flags_a.config_overrides); + if (error) + { + std::cerr << "Error deserializing config file"; + if (!node_flags_a.config_overrides.empty ()) + { + std::cerr << " or --config option"; + } + std::cerr << "\n" + << error.get_message () << std::endl; + std::exit (1); + } + + auto & node_config = daemon_config.node; + node_config.peering_port = 24000; + + node = std::make_shared (io_context, path_a, node_config, work, node_flags_a); +} + +nano::node_wrapper::~node_wrapper () +{ + node->stop (); +} \ No newline at end of file diff --git a/nano/node/node_wrapper.hpp b/nano/node/node_wrapper.hpp new file mode 100644 index 0000000000..082c5a78e1 --- /dev/null +++ b/nano/node/node_wrapper.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +#include + +#include + +namespace nano +{ + +class node; +class node_flags; + +class node_wrapper final +{ +public: + node_wrapper (std::filesystem::path const & path_a, std::filesystem::path const & config_path_a, nano::node_flags const & node_flags_a); + ~node_wrapper (); + + nano::network_params network_params; + std::shared_ptr io_context; + nano::work_pool work; + std::shared_ptr node; +}; + +} \ No newline at end of file diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index ad597389b8..cfa5fcbf66 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -33,7 +33,8 @@ nano::node_config::node_config (const std::optional & peering_port_a, websocket_config{ network_params.network }, ipc_config{ network_params.network }, external_address{ boost::asio::ip::address_v6{}.to_string () }, - rep_crawler{ network_params.network } + rep_crawler{ network_params.network }, + block_processor{ network_params.network } { if (peering_port == 0) { @@ -212,6 +213,10 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const rep_crawler.serialize (rep_crawler_l); toml.put_child ("rep_crawler", rep_crawler_l); + nano::tomlconfig block_processor_l; + block_processor.serialize (block_processor_l); + toml.put_child ("block_processor", block_processor_l); + return toml.get_error (); } @@ -287,6 +292,12 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml) rep_crawler.deserialize (config_l); } + if (toml.has_key ("block_processor")) + { + auto config_l = toml.get_required_child ("block_processor"); + block_processor.deserialize (config_l); + } + if (toml.has_key ("work_peers")) { work_peers.clear (); diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index 2b8f453577..db09480e35 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,7 @@ class node_config unsigned backlog_scan_frequency{ 10 }; nano::vote_cache_config vote_cache; nano::rep_crawler_config rep_crawler; + nano::block_processor_config block_processor; public: std::string serialize_frontiers_confirmation (nano::frontiers_confirmation_mode) const; @@ -169,7 +171,7 @@ class node_flags final bool allow_bootstrap_peers_duplicates{ false }; bool disable_max_peers_per_ip{ false }; // For testing only bool disable_max_peers_per_subnetwork{ false }; // For testing only - bool force_use_write_database_queue{ false }; // For testing only. RocksDB does not use the database queue, but some tests rely on it being used. + bool force_use_write_queue{ false }; // For testing only. RocksDB does not use the database queue, but some tests rely on it being used. bool disable_search_pending{ false }; // For testing only bool enable_pruning{ false }; bool fast_bootstrap{ false }; diff --git a/nano/node/transport/socket.cpp b/nano/node/transport/socket.cpp index 0fae87d4fe..b76f653044 100644 --- a/nano/node/transport/socket.cpp +++ b/nano/node/transport/socket.cpp @@ -26,7 +26,7 @@ nano::transport::socket::socket (nano::node & node_a, nano::transport::socket_en nano::transport::socket::socket (nano::node & node_a, boost::asio::ip::tcp::socket boost_socket_a, boost::asio::ip::tcp::endpoint remote_endpoint_a, boost::asio::ip::tcp::endpoint local_endpoint_a, nano::transport::socket_endpoint endpoint_type_a, std::size_t max_queue_size_a) : send_queue{ max_queue_size_a }, - node{ node_a }, + node_w{ node_a.shared () }, strand{ node_a.io_ctx.get_executor () }, tcp_socket{ std::move (boost_socket_a) }, remote{ remote_endpoint_a }, @@ -62,10 +62,18 @@ void nano::transport::socket::async_connect (nano::tcp_endpoint const & endpoint tcp_socket.async_connect (endpoint_a, boost::asio::bind_executor (strand, [this_l = shared_from_this (), callback = std::move (callback_a), endpoint_a] (boost::system::error_code const & ec) { + debug_assert (this_l->strand.running_in_this_thread ()); + + auto node_l = this_l->node_w.lock (); + if (!node_l) + { + return; + } + this_l->remote = endpoint_a; if (ec) { - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_connect_error, nano::stat::dir::in); + node_l->stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_connect_error, nano::stat::dir::in); this_l->close (); } else @@ -76,7 +84,7 @@ void nano::transport::socket::async_connect (nano::tcp_endpoint const & endpoint boost::system::error_code ec; this_l->local = this_l->tcp_socket.local_endpoint (ec); } - this_l->node.observers.socket_connected.notify (*this_l); + node_l->observers.socket_connected.notify (*this_l); } callback (ec); })); @@ -97,14 +105,20 @@ void nano::transport::socket::async_read (std::shared_ptr> [this_l, buffer_a, cbk = std::move (callback)] (boost::system::error_code const & ec, std::size_t size_a) { debug_assert (this_l->strand.running_in_this_thread ()); + auto node_l = this_l->node_w.lock (); + if (!node_l) + { + return; + } + if (ec) { - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_read_error, nano::stat::dir::in); + node_l->stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_read_error, nano::stat::dir::in); this_l->close (); } else { - this_l->node.stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::in, size_a); + node_l->stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::in, size_a); this_l->set_last_completion (); this_l->set_last_receive_time (); } @@ -123,11 +137,17 @@ void nano::transport::socket::async_read (std::shared_ptr> void nano::transport::socket::async_write (nano::shared_const_buffer const & buffer_a, std::function callback_a, nano::transport::traffic_type traffic_type) { + auto node_l = node_w.lock (); + if (!node_l) + { + return; + } + if (closed) { if (callback_a) { - node.background ([callback = std::move (callback_a)] () { + node_l->background ([callback = std::move (callback_a)] () { callback (boost::system::errc::make_error_code (boost::system::errc::not_supported), 0); }); } @@ -139,7 +159,7 @@ void nano::transport::socket::async_write (nano::shared_const_buffer const & buf { if (callback_a) { - node.background ([callback = std::move (callback_a)] () { + node_l->background ([callback = std::move (callback_a)] () { callback (boost::system::errc::make_error_code (boost::system::errc::not_supported), 0); }); } @@ -177,15 +197,21 @@ void nano::transport::socket::write_queued_messages () boost::asio::bind_executor (strand, [this_l = shared_from_this (), next /* `next` object keeps buffer in scope */] (boost::system::error_code ec, std::size_t size) { debug_assert (this_l->strand.running_in_this_thread ()); + auto node_l = this_l->node_w.lock (); + if (!node_l) + { + return; + } + this_l->write_in_progress = false; if (ec) { - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_write_error, nano::stat::dir::in); + node_l->stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_write_error, nano::stat::dir::in); this_l->close (); } else { - this_l->node.stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::out, size); + node_l->stats.add (nano::stat::type::traffic_tcp, nano::stat::dir::out, size); this_l->set_last_completion (); } @@ -240,56 +266,76 @@ void nano::transport::socket::set_last_receive_time () void nano::transport::socket::ongoing_checkup () { - std::weak_ptr this_w (shared_from_this ()); - node.workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (node.network_params.network.is_dev_network () ? 1 : 5), [this_w] () { - if (auto this_l = this_w.lock ()) + auto node_l = node_w.lock (); + if (!node_l) + { + return; + } + + node_l->workers.add_timed_task (std::chrono::steady_clock::now () + std::chrono::seconds (node_l->network_params.network.is_dev_network () ? 1 : 5), [this_w = weak_from_this ()] () { + auto this_l = this_w.lock (); + if (!this_l) { - // If the socket is already dead, close just in case, and stop doing checkups - if (!this_l->alive ()) - { - this_l->close (); - return; - } + return; + } - nano::seconds_t now = nano::seconds_since_epoch (); - auto condition_to_disconnect{ false }; + auto node_l = this_l->node_w.lock (); + if (!node_l) + { + return; + } - // if this is a server socket, and no data is received for silent_connection_tolerance_time seconds then disconnect - if (this_l->endpoint_type () == socket_endpoint::server && (now - this_l->last_receive_time_or_init) > static_cast (this_l->silent_connection_tolerance_time.count ())) - { - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_silent_connection_drop, nano::stat::dir::in); + // If the socket is already dead, close just in case, and stop doing checkups + if (!this_l->alive ()) + { + this_l->close (); + return; + } - condition_to_disconnect = true; - } + nano::seconds_t now = nano::seconds_since_epoch (); + auto condition_to_disconnect{ false }; - // if there is no activity for timeout seconds then disconnect - if ((now - this_l->last_completion_time_or_init) > this_l->timeout) - { - this_l->node.stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_io_timeout_drop, this_l->endpoint_type () == socket_endpoint::server ? nano::stat::dir::in : nano::stat::dir::out); + // if this is a server socket, and no data is received for silent_connection_tolerance_time seconds then disconnect + if (this_l->endpoint_type () == socket_endpoint::server && (now - this_l->last_receive_time_or_init) > static_cast (this_l->silent_connection_tolerance_time.count ())) + { + node_l->stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_silent_connection_drop, nano::stat::dir::in); - condition_to_disconnect = true; - } + condition_to_disconnect = true; + } - if (condition_to_disconnect) - { - this_l->node.logger.debug (nano::log::type::tcp_server, "Closing socket due to timeout ({})", nano::util::to_str (this_l->remote)); + // if there is no activity for timeout seconds then disconnect + if ((now - this_l->last_completion_time_or_init) > this_l->timeout) + { + node_l->stats.inc (nano::stat::type::tcp, nano::stat::detail::tcp_io_timeout_drop, this_l->endpoint_type () == socket_endpoint::server ? nano::stat::dir::in : nano::stat::dir::out); - this_l->timed_out = true; - this_l->close (); - } - else if (!this_l->closed) - { - this_l->ongoing_checkup (); - } + condition_to_disconnect = true; + } + + if (condition_to_disconnect) + { + node_l->logger.debug (nano::log::type::tcp_server, "Closing socket due to timeout ({})", nano::util::to_str (this_l->remote)); + + this_l->timed_out = true; + this_l->close (); + } + else if (!this_l->closed) + { + this_l->ongoing_checkup (); } }); } void nano::transport::socket::read_impl (std::shared_ptr> const & data_a, std::size_t size_a, std::function callback_a) { + auto node_l = node_w.lock (); + if (!node_l) + { + return; + } + // Increase timeout to receive TCP header (idle server socket) auto const prev_timeout = get_default_timeout_value (); - set_default_timeout_value (node.network_params.network.idle_timeout); + set_default_timeout_value (node_l->network_params.network.idle_timeout); async_read (data_a, size_a, [callback_l = std::move (callback_a), prev_timeout, this_l = shared_from_this ()] (boost::system::error_code const & ec_a, std::size_t size_a) { this_l->set_default_timeout_value (prev_timeout); callback_l (ec_a, size_a); @@ -321,6 +367,12 @@ void nano::transport::socket::close () // This must be called from a strand or the destructor void nano::transport::socket::close_internal () { + auto node_l = node_w.lock (); + if (!node_l) + { + return; + } + if (closed.exchange (true)) { return; @@ -337,8 +389,8 @@ void nano::transport::socket::close_internal () if (ec) { - node.stats.inc (nano::stat::type::socket, nano::stat::detail::error_socket_close); - node.logger.error (nano::log::type::socket, "Failed to close socket gracefully: {} ({})", ec.message (), nano::util::to_str (remote)); + node_l->stats.inc (nano::stat::type::socket, nano::stat::detail::error_socket_close); + node_l->logger.error (nano::log::type::socket, "Failed to close socket gracefully: {} ({})", ec.message (), nano::util::to_str (remote)); } } diff --git a/nano/node/transport/socket.hpp b/nano/node/transport/socket.hpp index 5c558dea83..35f6f85cf7 100644 --- a/nano/node/transport/socket.hpp +++ b/nano/node/transport/socket.hpp @@ -172,7 +172,7 @@ class socket final : public std::enable_shared_from_this write_queue send_queue; protected: - nano::node & node; + std::weak_ptr node_w; boost::asio::strand strand; boost::asio::ip::tcp::socket tcp_socket; diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index d33db65ccc..3742c308e4 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -939,9 +939,9 @@ std::string nano_qt::status::text () size_t cemented (0); std::string count_string; { - auto size (wallet.wallet_m->wallets.node.ledger.cache.block_count.load ()); + auto size (wallet.wallet_m->wallets.node.ledger.block_count ()); unchecked = wallet.wallet_m->wallets.node.unchecked.count (); - cemented = wallet.wallet_m->wallets.node.ledger.cache.cemented_count.load (); + cemented = wallet.wallet_m->wallets.node.ledger.cemented_count (); count_string = std::to_string (size); } @@ -979,8 +979,8 @@ std::string nano_qt::status::text () if (wallet.node.flags.enable_pruning) { - count_string += ", Full: " + std::to_string (wallet.wallet_m->wallets.node.ledger.cache.block_count - wallet.wallet_m->wallets.node.ledger.cache.pruned_count); - count_string += ", Pruned: " + std::to_string (wallet.wallet_m->wallets.node.ledger.cache.pruned_count); + count_string += ", Full: " + std::to_string (wallet.wallet_m->wallets.node.ledger.block_count () - wallet.wallet_m->wallets.node.ledger.pruned_count ()); + count_string += ", Pruned: " + std::to_string (wallet.wallet_m->wallets.node.ledger.pruned_count ()); } result += count_string.c_str (); diff --git a/nano/qt_test/qt.cpp b/nano/qt_test/qt.cpp index d6758ff883..16f7306cbb 100644 --- a/nano/qt_test/qt.cpp +++ b/nano/qt_test/qt.cpp @@ -581,6 +581,7 @@ TEST (history, pruned_source) ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive)); auto open = std::make_shared (send2->hash (), key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, open)); + ledger.confirm (transaction, send1->hash ()); ASSERT_EQ (1, ledger.pruning_action (transaction, send1->hash (), 2)); next_pruning = send2->hash (); } @@ -593,6 +594,7 @@ TEST (history, pruned_source) // Additional legacy test { auto transaction (store->tx_begin_write ()); + ledger.confirm (transaction, next_pruning); ASSERT_EQ (1, ledger.pruning_action (transaction, next_pruning, 2)); } history1.refresh (); @@ -608,7 +610,9 @@ TEST (history, pruned_source) auto latest_key (ledger.latest (transaction, key.pub)); auto receive = std::make_shared (key.pub, latest_key, key.pub, 200, send->hash (), key.prv, key.pub, *system.work.generate (latest_key)); ASSERT_EQ (nano::block_status::progress, ledger.process (transaction, receive)); + ledger.confirm (transaction, latest); ASSERT_EQ (1, ledger.pruning_action (transaction, latest, 2)); + ledger.confirm (transaction, latest_key); ASSERT_EQ (1, ledger.pruning_action (transaction, latest_key, 2)); } history1.refresh (); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index f846ac9d4c..3b9168a000 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -843,7 +843,6 @@ TEST (rpc, frontier) nano::block_hash hash; nano::random_pool::generate_block (hash.bytes.data (), hash.bytes.size ()); source[key.pub] = hash; - node->store.confirmation_height.put (transaction, key.pub, { 0, nano::block_hash (0) }); node->store.account.put (transaction, key.pub, nano::account_info (hash, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } @@ -881,7 +880,6 @@ TEST (rpc, frontier_limited) nano::block_hash hash; nano::random_pool::generate_block (hash.bytes.data (), hash.bytes.size ()); source[key.pub] = hash; - node->store.confirmation_height.put (transaction, key.pub, { 0, nano::block_hash (0) }); node->store.account.put (transaction, key.pub, nano::account_info (hash, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } @@ -909,7 +907,6 @@ TEST (rpc, frontier_startpoint) nano::block_hash hash; nano::random_pool::generate_block (hash.bytes.data (), hash.bytes.size ()); source[key.pub] = hash; - node->store.confirmation_height.put (transaction, key.pub, { 0, nano::block_hash (0) }); node->store.account.put (transaction, key.pub, nano::account_info (hash, 0, 0, 0, 0, 0, nano::epoch::epoch_0)); } } @@ -3823,10 +3820,6 @@ TEST (rpc, account_info) .build (); ASSERT_EQ (nano::block_status::progress, node1->process (send)); auto time = nano::seconds_since_epoch (); - { - auto transaction = node1->store.tx_begin_write (); - node1->store.confirmation_height.put (transaction, nano::dev::genesis_key.pub, { 1, nano::dev::genesis->hash () }); - } request.put ("account", nano::dev::genesis_key.pub.to_account ()); { @@ -6599,7 +6592,7 @@ TEST (rpc, receive_pruned) auto transaction (node2->store.tx_begin_write ()); ASSERT_EQ (2, node2->ledger.pruning_action (transaction, send2->hash (), 1)); } - ASSERT_EQ (2, node2->ledger.cache.pruned_count); + ASSERT_EQ (2, node2->ledger.pruned_count ()); ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send1->hash ())); ASSERT_FALSE (node2->ledger.block_exists (node2->store.tx_begin_read (), send1->hash ())); ASSERT_TRUE (node2->ledger.block_or_pruned_exists (send2->hash ())); @@ -6873,3 +6866,18 @@ TEST (rpc, confirmation_info) ASSERT_EQ (0, response.get ("total_tally")); } } + +TEST (rpc, election_statistics) +{ + nano::test::system system; + auto node1 = add_ipc_enabled_node (system); + auto const rpc_ctx = add_rpc (system, node1); + boost::property_tree::ptree request1; + request1.put ("action", "election_statistics"); + auto response1 (wait_response (system, rpc_ctx, request1)); + ASSERT_EQ ("0", response1.get ("normal")); + ASSERT_EQ ("0", response1.get ("hinted")); + ASSERT_EQ ("0", response1.get ("optimistic")); + ASSERT_EQ ("0", response1.get ("total")); + ASSERT_EQ ("0.00", response1.get ("aec_utilization_percentage")); +} diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 1f2f373a02..85313fa625 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -570,13 +570,6 @@ void ledger_processor::receive_block (nano::receive_block & block_a) if (result == nano::block_status::progress) { auto new_balance (info->balance.number () + pending.value ().amount.number ()); -#ifdef NDEBUG - if (ledger.store.block.exists (transaction, block_a.hashables.source)) - { - auto info = ledger.account_info (transaction, pending.value ().source); - debug_assert (info); - } -#endif ledger.store.pending.del (transaction, key); block_a.sideband_set (nano::block_sideband (account, 0, new_balance, info->block_count + 1, nano::seconds_since_epoch (), block_details, nano::epoch::epoch_0 /* unused */)); ledger.store.block.put (transaction, hash, block_a); @@ -629,14 +622,6 @@ void ledger_processor::open_block (nano::open_block & block_a) result = ledger.constants.work.difficulty (block_a) >= ledger.constants.work.threshold (block_a.work_version (), block_details) ? nano::block_status::progress : nano::block_status::insufficient_work; // Does this block have sufficient work? (Malformed) if (result == nano::block_status::progress) { -#ifdef NDEBUG - if (ledger.store.block.exists (transaction, block_a.hashables.source)) - { - nano::account_info source_info; - [[maybe_unused]] auto error (ledger.store.account.get (transaction, pending.value ().source, source_info)); - debug_assert (!error); - } -#endif ledger.store.pending.del (transaction, key); block_a.sideband_set (nano::block_sideband (block_a.hashables.account, 0, pending.value ().amount, 1, nano::seconds_since_epoch (), block_details, nano::epoch::epoch_0 /* unused */)); ledger.store.block.put (transaction, hash, block_a); @@ -981,7 +966,7 @@ std::pair nano::ledger::hash_root_random (st } // Vote weight of an account -nano::uint128_t nano::ledger::weight (nano::account const & account_a) +nano::uint128_t nano::ledger::weight (nano::account const & account_a) const { if (check_bootstrap_weights.load ()) { @@ -1001,7 +986,7 @@ nano::uint128_t nano::ledger::weight (nano::account const & account_a) return cache.rep_weights.representation_get (account_a); } -nano::uint128_t nano::ledger::weight_exact (store::transaction const & txn_a, nano::account const & representative_a) +nano::uint128_t nano::ledger::weight_exact (store::transaction const & txn_a, nano::account const & representative_a) const { return store.rep_weight.get (txn_a, representative_a); } @@ -1346,6 +1331,7 @@ uint64_t nano::ledger::pruning_action (store::write_transaction & transaction_a, auto block_l = block (transaction_a, hash); if (block_l != nullptr) { + release_assert (block_confirmed (transaction_a, hash)); store.block.del (transaction_a, hash); store.pruned.put (transaction_a, hash); hash = block_l->previous (); @@ -1370,37 +1356,6 @@ uint64_t nano::ledger::pruning_action (store::write_transaction & transaction_a, return pruned_count; } -std::multimap> nano::ledger::unconfirmed_frontiers () const -{ - nano::locked>> result; - using result_t = decltype (result)::value_type; - - store.account.for_each_par ([this, &result] (store::read_transaction const & transaction_a, store::iterator i, store::iterator n) { - result_t unconfirmed_frontiers_l; - for (; i != n; ++i) - { - auto const & account (i->first); - auto const & account_info (i->second); - - nano::confirmation_height_info conf_height_info; - this->store.confirmation_height.get (transaction_a, account, conf_height_info); - - if (account_info.block_count != conf_height_info.height) - { - // Always output as no confirmation height has been set on the account yet - auto height_delta = account_info.block_count - conf_height_info.height; - auto const & frontier = account_info.head; - auto const & cemented_frontier = conf_height_info.frontier; - unconfirmed_frontiers_l.emplace (std::piecewise_construct, std::forward_as_tuple (height_delta), std::forward_as_tuple (cemented_frontier, frontier, i->first)); - } - } - // Merge results - auto result_locked = result.lock (); - result_locked->insert (unconfirmed_frontiers_l.begin (), unconfirmed_frontiers_l.end ()); - }); - return result; -} - // A precondition is that the store is an LMDB store bool nano::ledger::migrate_lmdb_to_rocksdb (std::filesystem::path const & data_path_a) const { @@ -1600,17 +1555,32 @@ nano::receivable_iterator nano::ledger::receivable_upper_bound (store::transacti return nano::receivable_iterator{ *this, tx, result }; } -nano::uncemented_info::uncemented_info (nano::block_hash const & cemented_frontier, nano::block_hash const & frontier, nano::account const & account) : - cemented_frontier (cemented_frontier), frontier (frontier), account (account) +uint64_t nano::ledger::cemented_count () const +{ + return cache.cemented_count; +} + +uint64_t nano::ledger::block_count () const +{ + return cache.block_count; +} + +uint64_t nano::ledger::account_count () const +{ + return cache.account_count; +} + +uint64_t nano::ledger::pruned_count () const { + return cache.pruned_count; } -std::unique_ptr nano::collect_container_info (ledger & ledger, std::string const & name) +std::unique_ptr nano::ledger::collect_container_info (std::string const & name) const { - auto count = ledger.bootstrap_weights.size (); - auto sizeof_element = sizeof (decltype (ledger.bootstrap_weights)::value_type); + auto count = bootstrap_weights.size (); + auto sizeof_element = sizeof (decltype (bootstrap_weights)::value_type); auto composite = std::make_unique (name); composite->add_component (std::make_unique (container_info{ "bootstrap_weights", count, sizeof_element })); - composite->add_component (ledger.cache.rep_weights.collect_container_info ("rep_weights")); + composite->add_component (cache.rep_weights.collect_container_info ("rep_weights")); return composite; } diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 1d25da1338..a3e7f127a3 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -27,15 +27,6 @@ class pending_info; class pending_key; class stats; -class uncemented_info -{ -public: - uncemented_info (nano::block_hash const & cemented_frontier, nano::block_hash const & frontier, nano::account const & account); - nano::block_hash cemented_frontier; - nano::block_hash frontier; - nano::account account; -}; - class ledger final { friend class receivable_iterator; @@ -59,11 +50,11 @@ class ledger final * If the weight is below the cache limit it returns 0. * During bootstrap it returns the preconfigured bootstrap weights. */ - nano::uint128_t weight (nano::account const &); + nano::uint128_t weight (nano::account const &) const; std::optional successor (store::transaction const &, nano::qualified_root const &) const noexcept; std::optional successor (store::transaction const & transaction, nano::block_hash const & hash) const noexcept; /* Returns the exact vote weight for the given representative by doing a database lookup */ - nano::uint128_t weight_exact (store::transaction const &, nano::account const &); + nano::uint128_t weight_exact (store::transaction const &, nano::account const &) const; std::shared_ptr forked_block (store::transaction const &, nano::block const &); std::shared_ptr head_block (store::transaction const &, nano::account const &); bool block_confirmed (store::transaction const &, nano::block_hash const &) const; @@ -90,7 +81,6 @@ class ledger final std::shared_ptr find_receive_block_by_send_hash (store::transaction const & transaction, nano::account const & destination, nano::block_hash const & send_block_hash); nano::account const & epoch_signer (nano::link const &) const; nano::link const & epoch_link (nano::epoch) const; - std::multimap> unconfirmed_frontiers () const; bool migrate_lmdb_to_rocksdb (std::filesystem::path const &) const; bool bootstrap_weight_reached () const; static nano::epoch version (nano::block const & block); @@ -103,6 +93,11 @@ class ledger final nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account) const; // Returns the next receivable entry for the account 'account' with hash greater than 'hash' nano::receivable_iterator receivable_upper_bound (store::transaction const & tx, nano::account const & account, nano::block_hash const & hash) const; + std::unique_ptr collect_container_info (std::string const & name) const; + uint64_t cemented_count () const; + uint64_t block_count () const; + uint64_t account_count () const; + uint64_t pruned_count () const; static nano::uint128_t const unit; nano::ledger_constants & constants; nano::store::component & store; @@ -110,7 +105,7 @@ class ledger final nano::stats & stats; std::unordered_map bootstrap_weights; uint64_t bootstrap_weight_max_blocks{ 1 }; - std::atomic check_bootstrap_weights; + mutable std::atomic check_bootstrap_weights; bool pruning{ false }; private: @@ -119,6 +114,4 @@ class ledger final void initialize (nano::generate_cache_flags const &); void confirm (nano::store::write_transaction const & transaction, nano::block const & block); }; - -std::unique_ptr collect_container_info (ledger & ledger, std::string const & name); } diff --git a/nano/secure/ledger_cache.hpp b/nano/secure/ledger_cache.hpp index 1d8026efe1..81503b6df5 100644 --- a/nano/secure/ledger_cache.hpp +++ b/nano/secure/ledger_cache.hpp @@ -6,14 +6,28 @@ #include +namespace nano +{ +class ledger; +} +namespace nano::store +{ +class component; +} + namespace nano { /* Holds an in-memory cache of various counts */ class ledger_cache { + friend class store::component; + friend class ledger; + public: explicit ledger_cache (nano::store::rep_weight & rep_weight_store_a, nano::uint128_t min_rep_weight_a = 0); nano::rep_weights rep_weights; + +private: std::atomic cemented_count{ 0 }; std::atomic block_count{ 0 }; std::atomic pruned_count{ 0 }; diff --git a/nano/secure/rep_weights.cpp b/nano/secure/rep_weights.cpp index 632ace73a2..7b9622ae00 100644 --- a/nano/secure/rep_weights.cpp +++ b/nano/secure/rep_weights.cpp @@ -126,7 +126,7 @@ std::size_t nano::rep_weights::size () const return rep_amounts.size (); } -std::unique_ptr nano::rep_weights::collect_container_info (std::string const & name) +std::unique_ptr nano::rep_weights::collect_container_info (std::string const & name) const { size_t rep_amounts_count; diff --git a/nano/secure/rep_weights.hpp b/nano/secure/rep_weights.hpp index 214e22b8e6..f90bf19b7b 100644 --- a/nano/secure/rep_weights.hpp +++ b/nano/secure/rep_weights.hpp @@ -29,7 +29,7 @@ class rep_weights /* Only use this method when loading rep weights from the database table */ void copy_from (rep_weights & other_a); size_t size () const; - std::unique_ptr collect_container_info (std::string const &); + std::unique_ptr collect_container_info (std::string const &) const; private: mutable nano::mutex mutex; diff --git a/nano/slow_test/bootstrap.cpp b/nano/slow_test/bootstrap.cpp index 95662612ad..d6b9d4d946 100644 --- a/nano/slow_test/bootstrap.cpp +++ b/nano/slow_test/bootstrap.cpp @@ -169,10 +169,10 @@ TEST (bootstrap_ascending, profile) } });*/ - std::cout << "server count: " << server->ledger.cache.block_count << std::endl; + std::cout << "server count: " << server->ledger.block_count () << std::endl; nano::test::rate_observer rate; - rate.observe ("count", [&] () { return client->ledger.cache.block_count.load (); }); + rate.observe ("count", [&] () { return client->ledger.block_count (); }); rate.observe ("unchecked", [&] () { return client->unchecked.count (); }); rate.observe ("block_processor", [&] () { return client->block_processor.size (); }); rate.observe ("priority", [&] () { return client->ascendboot.priority_size (); }); diff --git a/nano/slow_test/node.cpp b/nano/slow_test/node.cpp index f9f3d57cde..a1a1230831 100644 --- a/nano/slow_test/node.cpp +++ b/nano/slow_test/node.cpp @@ -234,7 +234,6 @@ TEST (store, load) { nano::account account; nano::random_pool::generate_block (account.bytes.data (), account.bytes.size ()); - system.nodes[0]->store.confirmation_height.put (transaction, account, { 0, nano::block_hash (0) }); system.nodes[0]->store.account.put (transaction, account, nano::account_info ()); } } @@ -709,12 +708,12 @@ TEST (confirmation_height, many_accounts_single_confirmation) cemented_count += i->second.height; } - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in), num_accounts * 2 - 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in), num_accounts * 2 - 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in), 0); - ASSERT_TIMELY_EQ (40s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (40s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); } @@ -777,7 +776,7 @@ TEST (confirmation_height, many_accounts_many_confirmations) ASSERT_GE (num_confirmed_bounded, nano::confirmation_height::unbounded_cutoff); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in), num_blocks_to_confirm - num_confirmed_bounded); - ASSERT_TIMELY_EQ (60s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (60s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); auto transaction = node->store.tx_begin_read (); size_t cemented_count = 0; @@ -787,9 +786,9 @@ TEST (confirmation_height, many_accounts_many_confirmations) } ASSERT_EQ (num_blocks_to_confirm + 1, cemented_count); - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); - ASSERT_TIMELY_EQ (20s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (20s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); } @@ -931,12 +930,12 @@ TEST (confirmation_height, long_chains) cemented_count += i->second.height; } - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in), num_blocks * 2 + 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in), num_blocks * 2 + 2); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_unbounded, nano::stat::dir::in), 0); - ASSERT_TIMELY_EQ (40s, (node->ledger.cache.cemented_count - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); + ASSERT_TIMELY_EQ (40s, (node->ledger.cemented_count () - 1), node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)); ASSERT_TIMELY_EQ (10s, node->active.election_winner_details_size (), 0); } @@ -976,11 +975,11 @@ TEST (confirmation_height, dynamic_algorithm) } node->confirming_set.add (state_blocks.front ()->hash ()); - ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, 2); + ASSERT_TIMELY_EQ (20s, node->ledger.cemented_count (), 2); node->confirming_set.add (latest_genesis->hash ()); - ASSERT_TIMELY_EQ (20s, node->ledger.cache.cemented_count, num_blocks + 1); + ASSERT_TIMELY_EQ (20s, node->ledger.cemented_count (), num_blocks + 1); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed, nano::stat::dir::in), num_blocks); ASSERT_EQ (node->ledger.stats.count (nano::stat::type::confirmation_height, nano::stat::detail::blocks_confirmed_bounded, nano::stat::dir::in), 1); @@ -1094,7 +1093,7 @@ TEST (confirmation_height, many_accounts_send_receive_self) } system.deadline_set (200s); - while ((node->ledger.cache.cemented_count - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) + while ((node->ledger.cemented_count () - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) { ASSERT_NO_ERROR (system.poll ()); } @@ -1107,10 +1106,10 @@ TEST (confirmation_height, many_accounts_send_receive_self) } ASSERT_EQ (num_blocks_to_confirm + 1, cemented_count); - ASSERT_EQ (cemented_count, node->ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, node->ledger.cemented_count ()); system.deadline_set (60s); - while ((node->ledger.cache.cemented_count - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) + while ((node->ledger.cemented_count () - 1) != node->stats.count (nano::stat::type::confirmation_observer, nano::stat::detail::all, nano::stat::dir::out)) { ASSERT_NO_ERROR (system.poll ()); } @@ -1137,14 +1136,14 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) ASSERT_TRUE (!store->init_error ()); nano::stats stats; nano::ledger ledger (*store, stats, nano::dev::constants); - nano::write_database_queue write_database_queue (false); + nano::store::write_queue write_database_queue (false); nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; std::atomic stopped{ false }; boost::latch initialized_latch{ 0 }; nano::block_hash block_hash_being_processed{ 0 }; - nano::write_database_queue write_queue{ false }; - nano::confirming_set confirming_set{ ledger, write_queue }; + nano::store::write_queue write_queue{ false }; + nano::confirming_set confirming_set{ ledger }; auto const num_accounts = 100000; @@ -1264,7 +1263,7 @@ TEST (confirmation_height, many_accounts_send_receive_self_no_elections) } ASSERT_EQ (num_blocks_to_confirm + 1, cemented_count); - ASSERT_EQ (cemented_count, ledger.cache.cemented_count); + ASSERT_EQ (cemented_count, ledger.cemented_count ()); } } @@ -1471,7 +1470,7 @@ TEST (telemetry, under_load) std::thread thread1 (thread_func, nano::dev::genesis_key, latest_genesis, nano::dev::constants.genesis_amount - num_blocks); std::thread thread2 (thread_func, key, latest_key, num_blocks); - ASSERT_TIMELY_EQ (200s, node1->ledger.cache.block_count, num_blocks * 2 + 3); + ASSERT_TIMELY_EQ (200s, node1->ledger.block_count (), num_blocks * 2 + 3); thread1.join (); thread2.join (); @@ -1702,8 +1701,8 @@ TEST (node, mass_epoch_upgrader) info.pending_hash = block->hash (); } } - ASSERT_EQ (1 + total_accounts, node.ledger.cache.block_count); - ASSERT_EQ (1, node.ledger.cache.account_count); + ASSERT_EQ (1 + total_accounts, node.ledger.block_count ()); + ASSERT_EQ (1, node.ledger.account_count ()); // Receive for half of accounts for (auto const & info : opened) @@ -1723,32 +1722,32 @@ TEST (node, mass_epoch_upgrader) ASSERT_NE (nullptr, block); ASSERT_EQ (nano::block_status::progress, node.process (block)); } - ASSERT_EQ (1 + total_accounts + opened.size (), node.ledger.cache.block_count); - ASSERT_EQ (1 + opened.size (), node.ledger.cache.account_count); + ASSERT_EQ (1 + total_accounts + opened.size (), node.ledger.block_count ()); + ASSERT_EQ (1 + opened.size (), node.ledger.account_count ()); nano::keypair epoch_signer (nano::dev::genesis_key); - auto const block_count_before = node.ledger.cache.block_count.load (); + auto const block_count_before = node.ledger.block_count (); auto const total_to_upgrade = 1 + total_accounts; std::cout << "Mass upgrading " << total_to_upgrade << " accounts" << std::endl; - while (node.ledger.cache.block_count != block_count_before + total_to_upgrade) + while (node.ledger.block_count () != block_count_before + total_to_upgrade) { - auto const pre_upgrade = node.ledger.cache.block_count.load (); + auto const pre_upgrade = node.ledger.block_count (); auto upgrade_count = std::min (batch_size, block_count_before + total_to_upgrade - pre_upgrade); ASSERT_FALSE (node.epoch_upgrader.start (epoch_signer.prv, nano::epoch::epoch_1, upgrade_count, threads)); // Already ongoing - should fail ASSERT_TRUE (node.epoch_upgrader.start (epoch_signer.prv, nano::epoch::epoch_1, upgrade_count, threads)); system.deadline_set (60s); - while (node.ledger.cache.block_count != pre_upgrade + upgrade_count) + while (node.ledger.block_count () != pre_upgrade + upgrade_count) { ASSERT_NO_ERROR (system.poll ()); std::this_thread::sleep_for (200ms); - std::cout << node.ledger.cache.block_count - block_count_before << " / " << total_to_upgrade << std::endl; + std::cout << node.ledger.block_count () - block_count_before << " / " << total_to_upgrade << std::endl; } std::this_thread::sleep_for (50ms); } auto expected_blocks = block_count_before + total_accounts + 1; - ASSERT_EQ (expected_blocks, node.ledger.cache.block_count); + ASSERT_EQ (expected_blocks, node.ledger.block_count ()); // Check upgrade { auto transaction (node.store.tx_begin_read ()); @@ -1794,7 +1793,7 @@ TEST (node, mass_block_new) { node.process_active (block); } - ASSERT_TIMELY_EQ (200s, node.ledger.cache.block_count, next_block_count); + ASSERT_TIMELY_EQ (200s, node.ledger.block_count (), next_block_count); next_block_count += num_blocks; while (node.block_processor.size () > 0) { @@ -1971,7 +1970,7 @@ TEST (node, aggressive_flooding) ASSERT_TIMELY (!nano::slow_instrumentation () ? 10s : 40s, all_received ()); - ASSERT_TIMELY_EQ (!nano::slow_instrumentation () ? 10s : 40s, node1.ledger.cache.block_count, 1 + 2 * nodes_wallets.size ()); + ASSERT_TIMELY_EQ (!nano::slow_instrumentation () ? 10s : 40s, node1.ledger.block_count (), 1 + 2 * nodes_wallets.size ()); // Wait until the main node sees all representatives ASSERT_TIMELY_EQ (!nano::slow_instrumentation () ? 10s : 40s, node1.rep_crawler.principal_representatives ().size (), nodes_wallets.size ()); @@ -2008,7 +2007,7 @@ TEST (node, aggressive_flooding) // All blocks: genesis + (send+open) for each representative + 2 local blocks // The main node only sees all blocks if other nodes are flooding their PR's open block to all other PRs - ASSERT_EQ (1 + 2 * nodes_wallets.size () + 2, node1.ledger.cache.block_count); + ASSERT_EQ (1 + 2 * nodes_wallets.size () + 2, node1.ledger.block_count ()); } TEST (node, send_single_many_peers) @@ -2166,7 +2165,7 @@ TEST (system, block_sequence) std::string message; for (auto i : system.nodes) { - message += boost::str (boost::format ("N:%1% b:%2% c:%3% a:%4% s:%5% p:%6%\n") % std::to_string (i->network.port) % std::to_string (i->ledger.cache.block_count) % std::to_string (i->ledger.cache.cemented_count) % std::to_string (i->active.size ()) % std::to_string (i->scheduler.priority.size ()) % std::to_string (i->network.size ())); + message += boost::str (boost::format ("N:%1% b:%2% c:%3% a:%4% s:%5% p:%6%\n") % std::to_string (i->network.port) % std::to_string (i->ledger.block_count ()) % std::to_string (i->ledger.cemented_count ()) % std::to_string (i->active.size ()) % std::to_string (i->scheduler.priority.size ()) % std::to_string (i->network.size ())); nano::lock_guard lock{ i->active.mutex }; for (auto const & j : i->active.roots) { diff --git a/nano/store/CMakeLists.txt b/nano/store/CMakeLists.txt index 794f6c0535..68cde103a1 100644 --- a/nano/store/CMakeLists.txt +++ b/nano/store/CMakeLists.txt @@ -92,7 +92,9 @@ add_library( rocksdb/version.cpp transaction.cpp version.cpp - versioning.cpp) + versioning.cpp + write_queue.hpp + write_queue.cpp) target_link_libraries( nano_store diff --git a/nano/store/component.cpp b/nano/store/component.cpp index 5d8c8bec20..815f3e5950 100644 --- a/nano/store/component.cpp +++ b/nano/store/component.cpp @@ -7,7 +7,7 @@ #include #include -nano::store::component::component (nano::store::block & block_store_a, nano::store::account & account_store_a, nano::store::pending & pending_store_a, nano::store::online_weight & online_weight_store_a, nano::store::pruned & pruned_store_a, nano::store::peer & peer_store_a, nano::store::confirmation_height & confirmation_height_store_a, nano::store::final_vote & final_vote_store_a, nano::store::version & version_store_a, nano::store::rep_weight & rep_weight_a) : +nano::store::component::component (nano::store::block & block_store_a, nano::store::account & account_store_a, nano::store::pending & pending_store_a, nano::store::online_weight & online_weight_store_a, nano::store::pruned & pruned_store_a, nano::store::peer & peer_store_a, nano::store::confirmation_height & confirmation_height_store_a, nano::store::final_vote & final_vote_store_a, nano::store::version & version_store_a, nano::store::rep_weight & rep_weight_a, bool use_noops_a) : block (block_store_a), account (account_store_a), pending (pending_store_a), @@ -17,6 +17,7 @@ nano::store::component::component (nano::store::block & block_store_a, nano::sto confirmation_height (confirmation_height_store_a), final_vote (final_vote_store_a), version (version_store_a), + write_queue (use_noops_a), rep_weight (rep_weight_a) { } diff --git a/nano/store/component.hpp b/nano/store/component.hpp index 9ba94029af..7cdd7d1fff 100644 --- a/nano/store/component.hpp +++ b/nano/store/component.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -52,7 +53,8 @@ namespace store nano::store::confirmation_height &, nano::store::final_vote &, nano::store::version &, - nano::store::rep_weight & + nano::store::rep_weight &, + bool use_noops_a ); // clang-format on virtual ~component () = default; @@ -79,6 +81,8 @@ namespace store store::final_vote & final_vote; store::version & version; + store::write_queue write_queue; + virtual unsigned max_block_write_batch_num () const = 0; virtual bool copy_db (std::filesystem::path const & destination) = 0; diff --git a/nano/store/lmdb/lmdb.cpp b/nano/store/lmdb/lmdb.cpp index 122bf55a98..e5d2f448bc 100644 --- a/nano/store/lmdb/lmdb.cpp +++ b/nano/store/lmdb/lmdb.cpp @@ -26,7 +26,8 @@ nano::store::lmdb::component::component (nano::logger & logger_a, std::filesyste confirmation_height_store, final_vote_store, version_store, - rep_weight_store + rep_weight_store, + false // write_queue use_noops }, // clang-format on block_store{ *this }, diff --git a/nano/store/rocksdb/rocksdb.cpp b/nano/store/rocksdb/rocksdb.cpp index d9fb4e3164..92fafe6934 100644 --- a/nano/store/rocksdb/rocksdb.cpp +++ b/nano/store/rocksdb/rocksdb.cpp @@ -35,7 +35,7 @@ class event_listener : public rocksdb::EventListener }; } -nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesystem::path const & path_a, nano::ledger_constants & constants, nano::rocksdb_config const & rocksdb_config_a, bool open_read_only_a) : +nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesystem::path const & path_a, nano::ledger_constants & constants, nano::rocksdb_config const & rocksdb_config_a, bool open_read_only_a, bool force_use_write_queue) : // clang-format off nano::store::component{ block_store, @@ -47,7 +47,8 @@ nano::store::rocksdb::component::component (nano::logger & logger_a, std::filesy confirmation_height_store, final_vote_store, version_store, - rep_weight_store + rep_weight_store, + !force_use_write_queue // write_queue use_noops }, // clang-format on block_store{ *this }, diff --git a/nano/store/rocksdb/rocksdb.hpp b/nano/store/rocksdb/rocksdb.hpp index 8a71cf2b12..d526099697 100644 --- a/nano/store/rocksdb/rocksdb.hpp +++ b/nano/store/rocksdb/rocksdb.hpp @@ -64,7 +64,7 @@ class component : public nano::store::component friend class nano::store::rocksdb::version; friend class nano::store::rocksdb::rep_weight; - explicit component (nano::logger &, std::filesystem::path const &, nano::ledger_constants & constants, nano::rocksdb_config const & = nano::rocksdb_config{}, bool open_read_only = false); + explicit component (nano::logger &, std::filesystem::path const &, nano::ledger_constants & constants, nano::rocksdb_config const & = nano::rocksdb_config{}, bool open_read_only = false, bool force_use_write_queue = false); store::write_transaction tx_begin_write (std::vector const & tables_requiring_lock = {}, std::vector const & tables_no_lock = {}) override; store::read_transaction tx_begin_read () const override; diff --git a/nano/node/write_database_queue.cpp b/nano/store/write_queue.cpp similarity index 72% rename from nano/node/write_database_queue.cpp rename to nano/store/write_queue.cpp index 8bb42fe322..cdc7f41ff8 100644 --- a/nano/node/write_database_queue.cpp +++ b/nano/store/write_queue.cpp @@ -1,15 +1,15 @@ #include #include -#include +#include #include -nano::write_guard::write_guard (std::function guard_finish_callback_a) : +nano::store::write_guard::write_guard (std::function guard_finish_callback_a) : guard_finish_callback (guard_finish_callback_a) { } -nano::write_guard::write_guard (nano::write_guard && write_guard_a) noexcept : +nano::store::write_guard::write_guard (write_guard && write_guard_a) noexcept : guard_finish_callback (std::move (write_guard_a.guard_finish_callback)), owns (write_guard_a.owns) { @@ -17,7 +17,7 @@ nano::write_guard::write_guard (nano::write_guard && write_guard_a) noexcept : write_guard_a.guard_finish_callback = nullptr; } -nano::write_guard & nano::write_guard::operator= (nano::write_guard && write_guard_a) noexcept +nano::store::write_guard & nano::store::write_guard::operator= (write_guard && write_guard_a) noexcept { owns = write_guard_a.owns; guard_finish_callback = std::move (write_guard_a.guard_finish_callback); @@ -27,7 +27,7 @@ nano::write_guard & nano::write_guard::operator= (nano::write_guard && write_gua return *this; } -nano::write_guard::~write_guard () +nano::store::write_guard::~write_guard () { if (owns) { @@ -35,12 +35,12 @@ nano::write_guard::~write_guard () } } -bool nano::write_guard::is_owned () const +bool nano::store::write_guard::is_owned () const { return owns; } -void nano::write_guard::release () +void nano::store::write_guard::release () { debug_assert (owns); if (owns) @@ -50,7 +50,7 @@ void nano::write_guard::release () owns = false; } -nano::write_database_queue::write_database_queue (bool use_noops_a) : +nano::store::write_queue::write_queue (bool use_noops_a) : guard_finish_callback ([use_noops_a, &queue = queue, &mutex = mutex, &cv = cv] () { if (!use_noops_a) { @@ -65,7 +65,7 @@ nano::write_database_queue::write_database_queue (bool use_noops_a) : { } -nano::write_guard nano::write_database_queue::wait (nano::writer writer) +nano::store::write_guard nano::store::write_queue::wait (writer writer) { if (use_noops) { @@ -88,14 +88,14 @@ nano::write_guard nano::write_database_queue::wait (nano::writer writer) return write_guard (guard_finish_callback); } -bool nano::write_database_queue::contains (nano::writer writer) +bool nano::store::write_queue::contains (writer writer) { debug_assert (!use_noops); nano::lock_guard guard (mutex); return std::find (queue.cbegin (), queue.cend (), writer) != queue.cend (); } -bool nano::write_database_queue::process (nano::writer writer) +bool nano::store::write_queue::process (writer writer) { if (use_noops) { @@ -123,7 +123,7 @@ bool nano::write_database_queue::process (nano::writer writer) return result; } -nano::write_guard nano::write_database_queue::pop () +nano::store::write_guard nano::store::write_queue::pop () { return write_guard (guard_finish_callback); } diff --git a/nano/node/write_database_queue.hpp b/nano/store/write_queue.hpp similarity index 83% rename from nano/node/write_database_queue.hpp rename to nano/store/write_queue.hpp index d6c6883ea6..674a7742bc 100644 --- a/nano/node/write_database_queue.hpp +++ b/nano/store/write_queue.hpp @@ -6,7 +6,7 @@ #include #include -namespace nano +namespace nano::store { /** Distinct areas write locking is done, order is irrelevant */ enum class writer @@ -20,7 +20,7 @@ enum class writer class write_guard final { public: - write_guard (std::function guard_finish_callback_a); + explicit write_guard (std::function guard_finish_callback_a); void release (); ~write_guard (); write_guard (write_guard const &) = delete; @@ -38,27 +38,27 @@ class write_guard final * Allocates database write access in a fair maner rather than directly waiting for mutex aquisition * Users should wait() for access to database write transaction and hold the write_guard until complete */ -class write_database_queue final +class write_queue final { public: - write_database_queue (bool use_noops_a); + explicit write_queue (bool use_noops_a); /** Blocks until we are at the head of the queue and blocks other waiters until write_guard goes out of scope */ - [[nodiscard ("write_guard blocks other waiters")]] write_guard wait (nano::writer writer); + [[nodiscard ("write_guard blocks other waiters")]] write_guard wait (writer writer); /** Returns true if this writer is now at the front of the queue */ - bool process (nano::writer writer); + bool process (writer writer); /** Returns true if this writer is anywhere in the queue. Currently only used in tests */ - bool contains (nano::writer writer); + bool contains (writer writer); /** Doesn't actually pop anything until the returned write_guard is out of scope */ write_guard pop (); private: - std::deque queue; + std::deque queue; nano::mutex mutex; nano::condition_variable cv; std::function guard_finish_callback; bool use_noops; }; -} +} // namespace nano::store diff --git a/nano/test_common/system.cpp b/nano/test_common/system.cpp index 4af8f1b749..a736a6fa96 100644 --- a/nano/test_common/system.cpp +++ b/nano/test_common/system.cpp @@ -610,7 +610,7 @@ void nano::test::system::generate_mass_activity (uint32_t count_a, nano::node & { auto now (std::chrono::steady_clock::now ()); auto us (std::chrono::duration_cast (now - previous).count ()); - auto count = node_a.ledger.cache.block_count.load (); + auto count = node_a.ledger.block_count (); std::cerr << boost::str (boost::format ("Mass activity iteration %1% us %2% us/t %3% block count: %4%\n") % i % us % (us / 256) % count); previous = now; } diff --git a/nano/test_common/testutil.cpp b/nano/test_common/testutil.cpp index e3a7e24cc0..0ae32d3c6f 100644 --- a/nano/test_common/testutil.cpp +++ b/nano/test_common/testutil.cpp @@ -214,7 +214,7 @@ std::vector nano::test::blocks_to_hashes (std::vector nano::test::fake_channel (nano::node & node, nano::account node_id) +std::shared_ptr nano::test::fake_channel (nano::node & node, nano::account node_id) { auto channel = std::make_shared (node); if (!node_id.is_zero ()) diff --git a/nano/test_common/testutil.hpp b/nano/test_common/testutil.hpp index 36ec9a3143..01bc60d042 100644 --- a/nano/test_common/testutil.hpp +++ b/nano/test_common/testutil.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -382,7 +383,7 @@ namespace test /* * Creates a new fake channel associated with `node` */ - std::shared_ptr fake_channel (nano::node & node, nano::account node_id = { 0 }); + std::shared_ptr fake_channel (nano::node & node, nano::account node_id = { 0 }); /* * Start an election on system system_a, node node_a and hash hash_a by reading the block * out of the ledger and adding it to the manual election scheduler queue. diff --git a/sanitize_ignorelist_ubsan b/sanitize_ignorelist_ubsan index e69de29bb2..a79cca2ac1 100644 --- a/sanitize_ignorelist_ubsan +++ b/sanitize_ignorelist_ubsan @@ -0,0 +1,12 @@ +# This class has a member variable that is marked alignas(16) which means the object should be aligned an a 16-byte boundary which it isn’t. +# Looking at where it’s instantiated it’s a static local function variable that is also thread_local. +# This could be some sort of bug in the thread_local alignment, maybe in combination with being a static local function variable. +# This issue is only reproducible on the GitHub MacOS UBSAN runners. +# /Users/runner/work/nano-node/nano-node/submodules/boost/libs/beast/include/boost/beast/core/detail/chacha.hpp:101:5: runtime error: constructor call on misaligned address 0x0001408c3e08 for type 'boost::beast::detail::chacha<20> *', which requires 16 byte alignment +# 0x0001408c3e08: note: pointer points here +# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +# ^ +# #0 0x1059df490 in boost::beast::detail::chacha<20ul>::chacha(unsigned int const*, unsigned long long) chacha.hpp +# #1 0x1059df21c in boost::beast::websocket::detail::secure_generate() prng.ipp:123 +src:*/beast/core/detail/chacha.hpp +src:*/beast/websocket/detail/prng.ipp