From d55445bedadd6b7f87d726942d714cb9ea1a9387 Mon Sep 17 00:00:00 2001 From: Colin LeMahieu Date: Sat, 17 Feb 2024 12:18:40 +0000 Subject: [PATCH] Initial work on separate unconfirmed and confirmed sets. Adding initial confirmation test. # Conflicts: # nano/secure/ledger.hpp Add tracking of unconfirmed blocks. --- nano/core_test/ledger.cpp | 20 +++++ nano/node/blockprocessor.cpp | 1 + nano/node/node.hpp | 2 + nano/secure/CMakeLists.txt | 2 + nano/secure/common.hpp | 15 +++- nano/secure/ledger.cpp | 138 ++++++++++++++++++++++++++++++-- nano/secure/ledger.hpp | 17 +++- nano/secure/unconfirmed_set.cpp | 2 + nano/secure/unconfirmed_set.hpp | 26 ++++++ nano/store/component.cpp | 4 +- nano/store/component.hpp | 2 +- 11 files changed, 216 insertions(+), 13 deletions(-) create mode 100644 nano/secure/unconfirmed_set.cpp create mode 100644 nano/secure/unconfirmed_set.hpp diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 7070817c1d..9ca4ae3862 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -5707,3 +5707,23 @@ TEST (ledger, head_block) auto tx = store.tx_begin_read (); ASSERT_EQ (*nano::dev::genesis, *ledger.head_block (tx, nano::dev::genesis->account ())); } + +TEST (ledger, confirm_one) +{ + nano::work_pool pool{ nano::dev::network_params.network, std::numeric_limits::max () }; + auto ctx = nano::test::context::ledger_empty (); + auto & ledger = ctx.ledger (); + nano::state_block_builder builder; + auto send = builder.make_block () + .account (nano::dev::genesis->account ()) + .previous (nano::dev::genesis->hash ()) + .representative (nano::dev::genesis->account ()) + .balance (nano::dev::constants.genesis_amount - 100) + .link (nano::dev::genesis->account ()) + .sign (nano::dev::genesis_key.prv, nano::dev::genesis_key.pub) + .work (*pool.generate (nano::dev::genesis->hash ())) + .build (); + ASSERT_EQ (nano::ledger_code::progress, ledger.process (ctx.store ().tx_begin_write (), send)); + ledger.confirm (ctx.store ().tx_begin_write (), send->hash ()); + ASSERT_TRUE (ctx.store ().block.exists (ctx.store ().tx_begin_read (), send->hash ())); +} diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 0eedc026ef..9da4477f73 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -331,6 +331,7 @@ nano::ledger_code nano::block_processor::process_one (store::write_transaction c { case nano::ledger_code::progress: { + node.unconfirmed.block.emplace (hash, block); queue_unchecked (transaction_a, hash); /* For send blocks check epoch open unchecked (gap pending). For state blocks check only send subtype and only if block epoch is not last epoch. diff --git a/nano/node/node.hpp b/nano/node/node.hpp index ccafe0d188..9c9079571e 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -175,6 +176,7 @@ class node final : public std::enable_shared_from_this nano::vote_generator generator; nano::vote_generator final_generator; nano::active_transactions active; + nano::unconfirmed_set unconfirmed; private: // Placed here to maintain initialization order std::unique_ptr scheduler_impl; diff --git a/nano/secure/CMakeLists.txt b/nano/secure/CMakeLists.txt index ea2830e3b3..56eef5048b 100644 --- a/nano/secure/CMakeLists.txt +++ b/nano/secure/CMakeLists.txt @@ -47,6 +47,8 @@ add_library( ledger.cpp network_filter.hpp network_filter.cpp + unconfirmed_set.cpp + unconfirmed_set.hpp utility.hpp utility.cpp vote.hpp diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index b250f304ac..1f20e8c8fb 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -19,6 +19,7 @@ #include #include +#include #include namespace boost @@ -106,7 +107,7 @@ class account_info final { public: account_info () = default; - account_info (nano::block_hash const &, nano::account const &, nano::block_hash const &, nano::amount const &, nano::seconds_t modified, uint64_t, epoch); + account_info (nano::block_hash const & head, nano::account const & representative, nano::block_hash const & open, nano::amount const & balance, nano::seconds_t modified, uint64_t, epoch); bool deserialize (nano::stream &); bool operator== (nano::account_info const &) const; bool operator!= (nano::account_info const &) const; @@ -459,3 +460,15 @@ class election_status final nano::wallet_id random_wallet_id (); } + +namespace std +{ +template <> +struct hash<::nano::pending_key> +{ + size_t operator() (::nano::pending_key const & data_a) const + { + return hash<::nano::uint512_union>{}({ ::nano::uint256_union{ data_a.account.number () }, data_a.hash }); + } +}; +} diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index acb2a2bb05..d61e5c04ae 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include @@ -828,12 +827,7 @@ nano::ledger_code nano::ledger::process (store::write_transaction const & transa auto code = ctx.check (); if (code == nano::ledger_code::progress) { - debug_assert (block_a->has_sideband ()); - ledger_processor processor (*this, transaction_a); - block_a->visit (processor); - debug_assert (processor.result == nano::ledger_code::progress); - ++cache.block_count; - return processor.result; + track (transaction_a, block_a); } return code; } @@ -1577,6 +1571,136 @@ uint64_t nano::ledger::height (store::transaction const & transaction, nano::blo return block->sideband ().height; } +void nano::ledger::confirm (nano::store::write_transaction const & transaction, nano::block_hash const & hash) +{ + debug_assert (unconfirmed.block.count (hash) == 1); + std::deque queue; + queue.push_front (hash); + while (!queue.empty ()) + { + auto hash = queue.front (); + debug_assert (unconfirmed.block.count (hash) == 1); + auto block = unconfirmed.block[hash]; + auto dependents = dependent_blocks (transaction, *block); + for (auto const & dependent: dependents) + { + if (unconfirmed.block.count (dependent) != 0) + { + queue.push_front (dependent); + } + else + { + debug_assert (dependent.is_zero () || store.block.exists (transaction, dependent)); + } + } + if (queue.front () == hash) + { + confirm (transaction, *block); + queue.pop_front (); + } + else + { + // unconfirmed dependencies were added + } + } +} + +void nano::ledger::track (nano::store::transaction const & transaction, std::shared_ptr block) +{ + debug_assert (block); + debug_assert (block->has_sideband ()); + debug_assert (unconfirmed.block.count (block->hash ()) == 0); + auto hash = block->hash (); + unconfirmed.block.emplace (hash, block); + auto account_l = account (*block); + std::optional info = unconfirmed.account.count (account_l) == 1 ? unconfirmed.account[account_l] : store.account.get (transaction, account_l); + auto representative = [&info, &block] () { + switch (block->type ()) + { + case nano::block_type::state: + case nano::block_type::open: + case nano::block_type::change: + return block->representative (); + case nano::block_type::send: + case nano::block_type::receive: + debug_assert (info.has_value ()); + return info->representative; + default: + release_assert (false); + } + }; + auto open = [&info, &hash] () { + return info.has_value () ? info->open_block : hash; + }; + unconfirmed.account[account_l] = nano::account_info{ hash, representative (), open (), balance (*block), nano::seconds_since_epoch (), block->sideband ().height, block->sideband ().details.epoch }; + if (block->sideband ().details.is_send) + { + auto destination_l = destination (*block); + debug_assert (destination_l.has_value ()); + nano::pending_key key{ *destination_l, hash }; + nano::pending_info info{ account_l, block->sideband ().amount, block->sideband ().details.epoch }; + debug_assert (unconfirmed.receivable.count (key) == 0); + unconfirmed.receivable.emplace (key, info); + } + else if (block->sideband ().details.is_receive) + { + nano::pending_key key{ account_l, hash }; + debug_assert (unconfirmed.received.count (key) == 0); + unconfirmed.received.emplace (key); + } +} + +void nano::ledger::confirm (nano::store::write_transaction const & transaction, nano::block const & block) +{ + auto account_l = account (block); + auto hash = block.hash (); + store.block.put (transaction, hash, block); + [[maybe_unused]] auto erased = unconfirmed.block.erase (hash); + debug_assert (erased == 1); + auto info = store.account.get (transaction, account_l); + auto representative = [&info, &block] () { + switch (block.type ()) + { + case nano::block_type::state: + case nano::block_type::open: + case nano::block_type::change: + return block.representative (); + case nano::block_type::send: + case nano::block_type::receive: + debug_assert (info.has_value ()); + return info->representative; + default: + release_assert (false); + } + }; + auto open = [&info, &hash] () { + return info.has_value () ? info->open_block : hash; + }; + store.account.put (transaction, account_l, account_info (transaction, block)); + if (unconfirmed.account[account_l].head == hash) + { + [[maybe_unused]] auto erased = unconfirmed.account.erase (account_l); + debug_assert (erased == 1); + } + if (block.sideband ().details.is_send) + { + auto destination_l = destination (block); + debug_assert (destination_l.has_value ()); + nano::pending_key key{ *destination_l, hash }; + nano::pending_info info{ account_l, block.sideband ().amount, block.sideband ().details.epoch }; + store.pending.put (transaction, key, info); + [[maybe_unused]] auto erased = unconfirmed.receivable.erase (key); + debug_assert (erased); + } + else if (block.sideband ().details.is_receive) + { + nano::pending_key key{ account_l, hash }; + store.pending.del (transaction, key); + [[maybe_unused]] auto erased = unconfirmed.received.erase (key); + debug_assert (erased); + } +} + 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) { diff --git a/nano/secure/ledger.hpp b/nano/secure/ledger.hpp index 9eefe749d9..424224f661 100644 --- a/nano/secure/ledger.hpp +++ b/nano/secure/ledger.hpp @@ -2,10 +2,18 @@ #include #include +#include #include +#include #include +namespace nano +{ +class confirmed_set; +class stats; +} + namespace nano::store { class component; @@ -15,8 +23,6 @@ class write_transaction; namespace nano { -class stats; - class uncemented_info { public: @@ -28,6 +34,8 @@ class uncemented_info class ledger final { + friend class confirmed_set; + public: ledger (nano::store::component &, nano::stats &, nano::ledger_constants & constants, nano::generate_cache const & = nano::generate_cache ()); /** @@ -86,6 +94,7 @@ class ledger final static nano::epoch version (nano::block const & block); nano::epoch version (store::transaction const & transaction, nano::block_hash const & hash) const; uint64_t height (store::transaction const & transaction, nano::block_hash const & hash) const; + void confirm (nano::store::write_transaction const & transaction, nano::block_hash const & hash); // Generate account_info for a given block nano::account_info account_info (nano::store::transaction const & transaction, nano::block const & block); static nano::uint128_t const unit; @@ -99,7 +108,11 @@ class ledger final bool pruning{ false }; private: + void track (nano::store::transaction const & transaction, std::shared_ptr block); + void confirm (nano::store::write_transaction const & transaction, nano::block const & block); void initialize (nano::generate_cache const &); + nano::unconfirmed_set unconfirmed; + std::mutex mutex; }; std::unique_ptr collect_container_info (ledger & ledger, std::string const & name); diff --git a/nano/secure/unconfirmed_set.cpp b/nano/secure/unconfirmed_set.cpp new file mode 100644 index 0000000000..445b224dcf --- /dev/null +++ b/nano/secure/unconfirmed_set.cpp @@ -0,0 +1,2 @@ +#include +#include diff --git a/nano/secure/unconfirmed_set.hpp b/nano/secure/unconfirmed_set.hpp new file mode 100644 index 0000000000..760b9b71e4 --- /dev/null +++ b/nano/secure/unconfirmed_set.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace nano +{ +class block; +} + +namespace nano +{ +class unconfirmed_set +{ +public: + std::unordered_map> block; + std::unordered_map account; + std::unordered_map receivable; + std::unordered_set received; +}; +} diff --git a/nano/store/component.cpp b/nano/store/component.cpp index e3ecdb280e..af4af70376 100644 --- a/nano/store/component.cpp +++ b/nano/store/component.cpp @@ -8,13 +8,13 @@ nano::store::component::component (nano::store::block & block_store_a, nano::sto block (block_store_a), account (account_store_a), pending (pending_store_a), + successor{ successor }, online_weight (online_weight_store_a), pruned (pruned_store_a), peer (peer_store_a), confirmation_height (confirmation_height_store_a), final_vote (final_vote_store_a), - version (version_store_a), - successor{ successor } + version (version_store_a) { } diff --git a/nano/store/component.hpp b/nano/store/component.hpp index e6e1271fbe..f3ef35c59e 100644 --- a/nano/store/component.hpp +++ b/nano/store/component.hpp @@ -67,6 +67,7 @@ namespace store store::block & block; store::account & account; store::pending & pending; + store::successor & successor; static int constexpr version_minimum{ 21 }; static int constexpr version_current{ 23 }; @@ -77,7 +78,6 @@ namespace store store::confirmation_height & confirmation_height; store::final_vote & final_vote; store::version & version; - store::successor & successor; virtual unsigned max_block_write_batch_num () const = 0;