-
Notifications
You must be signed in to change notification settings - Fork 783
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
454 additions
and
471 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 (); | ||
} | ||
} |
Oops, something went wrong.