Skip to content

Commit

Permalink
Initial work on separate unconfirmed and confirmed sets.
Browse files Browse the repository at this point in the history
Adding initial confirmation test.

# Conflicts:
#	nano/secure/ledger.hpp

Add tracking of unconfirmed blocks.
  • Loading branch information
clemahieu committed Feb 20, 2024
1 parent 52e4c13 commit d55445b
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 13 deletions.
20 changes: 20 additions & 0 deletions nano/core_test/ledger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<unsigned>::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 ()));
}
1 change: 1 addition & 0 deletions nano/node/blockprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions nano/node/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include <nano/node/websocket.hpp>
#include <nano/node/write_database_queue.hpp>
#include <nano/secure/ledger.hpp>
#include <nano/secure/unconfirmed_set.hpp>
#include <nano/secure/utility.hpp>

#include <boost/program_options.hpp>
Expand Down Expand Up @@ -175,6 +176,7 @@ class node final : public std::enable_shared_from_this<nano::node>
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<nano::scheduler::component> scheduler_impl;
Expand Down
2 changes: 2 additions & 0 deletions nano/secure/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion nano/secure/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <boost/variant/variant.hpp>

#include <array>
#include <functional>
#include <unordered_map>

namespace boost
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 });
}
};
}
138 changes: 131 additions & 7 deletions nano/secure/ledger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <nano/lib/utility.hpp>
#include <nano/lib/work.hpp>
#include <nano/node/make_store.hpp>
#include <nano/secure/block_check_context.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/ledger.hpp>
#include <nano/store/account.hpp>
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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<nano::block_hash> 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<nano::block> 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<nano::account_info> 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)
{
Expand Down
17 changes: 15 additions & 2 deletions nano/secure/ledger.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

#include <nano/lib/rep_weights.hpp>
#include <nano/lib/timer.hpp>
#include <nano/secure/block_check_context.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/unconfirmed_set.hpp>

#include <map>

namespace nano
{
class confirmed_set;
class stats;
}

namespace nano::store
{
class component;
Expand All @@ -15,8 +23,6 @@ class write_transaction;

namespace nano
{
class stats;

class uncemented_info
{
public:
Expand All @@ -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 ());
/**
Expand Down Expand Up @@ -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;
Expand All @@ -99,7 +108,11 @@ class ledger final
bool pruning{ false };

private:
void track (nano::store::transaction const & transaction, std::shared_ptr<nano::block> 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<container_info_component> collect_container_info (ledger & ledger, std::string const & name);
Expand Down
2 changes: 2 additions & 0 deletions nano/secure/unconfirmed_set.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#include <nano/lib/blocks.hpp>
#include <nano/secure/unconfirmed_set.hpp>
26 changes: 26 additions & 0 deletions nano/secure/unconfirmed_set.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <nano/lib/numbers.hpp>
#include <nano/secure/common.hpp>

#include <deque>
#include <memory>
#include <unordered_map>
#include <unordered_set>

namespace nano
{
class block;
}

namespace nano
{
class unconfirmed_set
{
public:
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> block;
std::unordered_map<nano::account, nano::account_info> account;
std::unordered_map<nano::pending_key, nano::pending_info> receivable;
std::unordered_set<nano::pending_key> received;
};
}
4 changes: 2 additions & 2 deletions nano/store/component.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
}

Expand Down
2 changes: 1 addition & 1 deletion nano/store/component.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand All @@ -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;

Expand Down

0 comments on commit d55445b

Please sign in to comment.