Skip to content

Commit

Permalink
Simplifying block checker context
Browse files Browse the repository at this point in the history
  • Loading branch information
clemahieu committed Jan 23, 2024
1 parent 767d922 commit 0e6e5fc
Show file tree
Hide file tree
Showing 11 changed files with 454 additions and 471 deletions.
2 changes: 1 addition & 1 deletion nano/lib/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace program_options
}
}

void assert_internal (char const * check_expr, char const * func, char const * file, unsigned int line, bool is_release_assert, std::string_view error = "");
[[noreturn]] void assert_internal (char const * check_expr, char const * func, char const * file, unsigned int line, bool is_release_assert, std::string_view error = "");

#define release_assert_1(check) check ? (void)0 : assert_internal (#check, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, true)
#define release_assert_2(check, error_msg) check ? (void)0 : assert_internal (#check, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__, true, error_msg)
Expand Down
6 changes: 2 additions & 4 deletions nano/node/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,8 @@ add_library(
block_arrival.cpp
block_broadcast.cpp
block_broadcast.hpp
block_checker.cpp
block_checker.hpp
block_checker_rules.cpp
block_checker_rules.hpp
block_check_context.cpp
block_check_context.hpp
block_publisher.cpp
block_publisher.hpp
gap_tracker.cpp
Expand Down
357 changes: 357 additions & 0 deletions nano/node/block_check_context.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
#include <nano/node/block_check_context.hpp>
#include <nano/secure/ledger.hpp>
#include <nano/store/block.hpp>
#include <nano/store/component.hpp>
#include <nano/store/pending.hpp>

nano::block_check_context::block_check_context (nano::ledger & ledger, std::shared_ptr<nano::block> const & block) :
block{ block },
ledger{ ledger }
{
auto transaction = ledger.store.tx_begin_read ();
if (ledger.store.block.exists (transaction, block->hash ()))
{
this->block = nullptr; // Signal this block already exists by nulling out block
return;
}
previous = ledger.store.block.get (transaction, block->previous ());
if (!gap_previous ())
{
state = ledger.account_info (transaction, account ());
if (!state)
{
state = nano::account_info{};
}
source_exists = ledger.block_or_pruned_exists (transaction, source ());
pending = ledger.pending_info (transaction, { account (), source () });
any_pending = ledger.store.pending.any (transaction, account ());
}
}

auto nano::block_check_context::op () const -> block_op
{
debug_assert (state.has_value ());
switch (block->type ())
{
case nano::block_type::state:
if (block->balance () < state->balance)
{
return block_op::send;
}
if (block->link ().is_zero ())
{
return block_op::noop;
}
if (ledger.constants.epochs.is_epoch_link (block->link ()))
{
return block_op::epoch;
}
return block_op::receive;
case nano::block_type::send:
return block_op::send;
case nano::block_type::open:
case nano::block_type::receive:
return block_op::receive;
case nano::block_type::change:
return block_op::noop;
case nano::block_type::not_a_block:
case nano::block_type::invalid:
release_assert (false);
break;
}
release_assert (false);
}

bool nano::block_check_context::old () const
{
return block == nullptr;
}

bool nano::block_check_context::is_send () const
{
debug_assert (state.has_value ());
auto legacy_send = block->type () == nano::block_type::send;
auto type = block->type () == nano::block_type::state;
auto decreased = block->balance () < state->balance;
return legacy_send || (type && decreased);
}

nano::account nano::block_check_context::account () const
{
switch (block->type ())
{
case nano::block_type::change:
case nano::block_type::receive:
case nano::block_type::send:
debug_assert (previous != nullptr);
switch (previous->type ())
{
case nano::block_type::state:
case nano::block_type::open:
return previous->account ();
case nano::block_type::change:
case nano::block_type::receive:
case nano::block_type::send:
return previous->sideband ().account;
case nano::block_type::not_a_block:
case nano::block_type::invalid:
debug_assert (false);
break;
}
break;
case nano::block_type::state:
case nano::block_type::open:
return block->account ();
case nano::block_type::not_a_block:
case nano::block_type::invalid:
debug_assert (false);
break;
}
// std::unreachable (); c++23
return 1; // Return an account that cannot be signed for.
}

nano::block_hash nano::block_check_context::source () const
{
switch (block->type ())
{
case nano::block_type::send:
case nano::block_type::change:
// 0 is returned for source on send/change blocks
case nano::block_type::receive:
case nano::block_type::open:
return block->source ();
case nano::block_type::state:
return block->link ().as_block_hash ();
case nano::block_type::not_a_block:
case nano::block_type::invalid:
return 0;
}
debug_assert (false);
return 0;
}

nano::account nano::block_check_context::signer (nano::epochs const & epochs) const
{
debug_assert (block != nullptr);
switch (block->type ())
{
case nano::block_type::send:
case nano::block_type::receive:
case nano::block_type::change:
debug_assert (previous != nullptr); // Previous block must be passed in for non-open blocks
switch (previous->type ())
{
case nano::block_type::state:
debug_assert (false && "Legacy blocks can't follow state blocks");
break;
case nano::block_type::open:
// Open blocks have the account written in the block.
return previous->account ();
default:
// Other legacy block types have the account stored in sideband.
return previous->sideband ().account;
}
break;
case nano::block_type::state:
{
debug_assert (dynamic_cast<nano::state_block *> (block.get ()));
// If the block is a send, while the link field may contain an epoch link value, it is actually a malformed destination address.
return (!epochs.is_epoch_link (block->link ()) || is_send ()) ? block->account () : epochs.signer (epochs.epoch (block->link ()));
}
case nano::block_type::open: // Open block signer is determined statelessly as it's written in the block
return block->account ();
case nano::block_type::invalid:
case nano::block_type::not_a_block:
debug_assert (false);
break;
}
// std::unreachable (); c++23
return 1; // Return an account that cannot be signed for.
}

bool nano::block_check_context::gap_previous () const
{
return !block->previous ().is_zero () && previous == nullptr;
}

bool nano::block_check_context::failed (nano::process_result const & code) const
{
return code != nano::process_result::progress;
}

nano::process_result nano::block_check_context::rule_reserved_account () const
{
switch (block->type ())
{
case nano::block_type::open:
case nano::block_type::state:
if (!block->account ().is_zero ())
{
return nano::process_result::progress;
}
else
{
return nano::process_result::opened_burn_account;
}
break;
case nano::block_type::change:
case nano::block_type::receive:
case nano::block_type::send:
return nano::process_result::progress;
case nano::block_type::invalid:
case nano::block_type::not_a_block:
release_assert (false);
break;
}
release_assert (false);
}

nano::process_result nano::block_check_context::rule_previous_frontier () const
{
debug_assert (block != nullptr); //
if (gap_previous ())
{
return nano::process_result::gap_previous;
}
else
{
return nano::process_result::progress;
}
}

nano::process_result nano::block_check_context::rule_block_position () const
{
if (previous == nullptr)
{
return nano::process_result::progress;
}
switch (block->type ())
{
case nano::block_type::send:
case nano::block_type::receive:
case nano::block_type::change:
{
switch (previous->type ())
{
case nano::block_type::state:
return nano::process_result::block_position;
default:
return nano::process_result::progress;
}
}
default:
return nano::process_result::progress;
}
}

nano::process_result nano::block_check_context::rule_block_signed () const
{
if (!nano::validate_message (signer (ledger.constants.epochs), block->hash (), block->block_signature ()))
{
return nano::process_result::progress;
}
return nano::process_result::bad_signature;
}

nano::process_result nano::block_check_context::rule_metastable () const
{
debug_assert (state.has_value ());
if (block->previous () == state->head)
{
return nano::process_result::progress;
}
else
{
return nano::process_result::fork;
}
}

nano::process_result nano::block_check_context::check_receive_rules () const
{
if (!source_exists)
{
return nano::process_result::gap_source;
}
if (!pending.has_value ())
{
return nano::process_result::unreceivable;
}
if (block->type () == nano::block_type::state)
{
auto next_balance = state->balance.number () + pending->amount.number ();
if (next_balance != block->balance ().number ())
{
return nano::process_result::balance_mismatch;
}
}
return nano::process_result::progress;
}

nano::process_result nano::block_check_context::check_epoch_rules () const
{
debug_assert (state.has_value ());
if (state->balance != block->balance ())
{
return nano::process_result::balance_mismatch;
}
if (state->representative != block->representative ())
{
return nano::process_result::representative_mismatch;
}
if (block->previous ().is_zero () && !any_pending)
{
return nano::process_result::gap_epoch_open_pending;
}
return nano::process_result::progress;
}

nano::process_result nano::block_check_context::check_send_rules () const
{
debug_assert (block->type () == nano::block_type::send || block->type () == nano::block_type::state);
if (state->balance < block->balance ())
{
return nano::process_result::negative_spend;
}
return nano::process_result::progress;
}

nano::process_result nano::block_check_context::check ()
{
if (old ())
{
return nano::process_result::old;
}
nano::process_result result;
if (failed (result = rule_reserved_account ()))
{
return result;
}
if (failed (result = rule_previous_frontier ()))
{
return result;
}
if (failed (result = rule_block_position ()))
{
return result;
}
if (failed (result = rule_block_signed ()))
{
return result;
}
if (failed (result = rule_metastable ()))
{
return result;
}
switch (block_op ())
{
case block_op::receive:
return check_receive_rules ();
case block_op::send:
return check_send_rules ();
case block_op::noop:
return nano::process_result::progress;
case block_op::epoch:
return check_epoch_rules ();
}
}
Loading

0 comments on commit 0e6e5fc

Please sign in to comment.