-
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
8 changed files
with
524 additions
and
23 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,225 @@ | ||
#include <nano/node/block_checker.hpp> | ||
#include <nano/node/block_checker_rules.hpp> | ||
#include <nano/secure/ledger.hpp> | ||
#include <nano/store/block.hpp> | ||
#include <nano/store/component.hpp> | ||
#include <nano/store/pending.hpp> | ||
|
||
nano::block_checker::context::context (nano::ledger & ledger, std::shared_ptr<nano::block> block) : | ||
block{ block }, | ||
epochs{ ledger.constants.epochs } | ||
{ | ||
auto transaction = ledger.store.tx_begin_read (); | ||
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 ()); | ||
} | ||
if (ledger.store.block.exists (transaction, block->hash ())) | ||
{ | ||
this->block = nullptr; // Signal this block already exists by nulling out block | ||
} | ||
//nano::account const & account_a, nano::block_hash const & successor_a, nano::amount const & balance_a, uint64_t const height_a, nano::seconds_t const timestamp_a, nano::block_details const & details_a, nano::epoch const source_epoch_a | ||
//block->sideband_set (nano::block_sideband (account (), 0, pending.amount, 1, nano::seconds_since_epoch (), block_details, nano::epoch::epoch_0 /* unused */)); | ||
} | ||
/** | ||
This class filters blocks in four directions based on how the link field should be interpreted | ||
For state blocks the link field is interpreted as: | ||
If the balance has decreased, a destination account | ||
If the balance has not decreased | ||
If the link field is 0, a noop | ||
If the link field is an epoch link, an epoch sentinel | ||
Otherwise, a block hash of an block ready to be received | ||
For legacy blocks, the link field interpretation is applied to source field for receive and open blocks or the destination field for send blocks */ | ||
nano::block_checker::block_op nano::block_checker::context::block_op () const | ||
{ | ||
debug_assert (state.has_value ()); | ||
switch (block->type ()) | ||
{ | ||
case nano::block_type::state: | ||
if (block->balance () < state->balance) | ||
{ | ||
return nano::block_checker::block_op::send; | ||
} | ||
if (block->link ().is_zero ()) | ||
{ | ||
return nano::block_checker::block_op::noop; | ||
} | ||
if (epochs.is_epoch_link (block->link ())) | ||
{ | ||
return nano::block_checker::block_op::epoch; | ||
} | ||
return nano::block_checker::block_op::receive; | ||
case nano::block_type::send: | ||
return nano::block_checker::block_op::send; | ||
case nano::block_type::open: | ||
case nano::block_type::receive: | ||
return nano::block_checker::block_op::receive; | ||
case nano::block_type::change: | ||
return nano::block_checker::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_checker::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_checker::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_checker::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_checker::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_checker::context::gap_previous () const | ||
{ | ||
return !block->previous ().is_zero () && previous == nullptr; | ||
} | ||
|
||
nano::block_checker::block_checker () | ||
{ | ||
} | ||
|
||
nano::process_result nano::block_checker::check (context & context) | ||
{ | ||
if (!context.block) | ||
{ | ||
return nano::process_result::old; | ||
} | ||
nano::process_result result; | ||
if (result = nano::rule_reserved_account (context), result != nano::process_result::progress) | ||
{ | ||
return result; | ||
} | ||
if (result = nano::rule_previous_frontier (context), result != nano::process_result::progress) | ||
{ | ||
return result; | ||
} | ||
if (result = nano::rule_block_position (context), result != nano::process_result::progress) | ||
{ | ||
return result; | ||
} | ||
if (result = nano::rule_block_signed (context), result != nano::process_result::progress) | ||
{ | ||
return result; | ||
} | ||
if (result = nano::rule_metastable (context), result != nano::process_result::progress) | ||
{ | ||
return result; | ||
} | ||
switch (context.block_op ()) | ||
{ | ||
case block_op::receive: | ||
return nano::rule_receivable (context); | ||
case block_op::send: | ||
return nano::rule_send_restrictions (context); | ||
case block_op::noop: | ||
return nano::process_result::progress; | ||
case block_op::epoch: | ||
return nano::rule_epoch_restrictions (context); | ||
} | ||
} |
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,52 @@ | ||
#pragma once | ||
|
||
#include <nano/lib/epoch.hpp> | ||
#include <nano/lib/numbers.hpp> | ||
#include <nano/secure/common.hpp> | ||
|
||
#include <memory> | ||
#include <optional> | ||
|
||
namespace nano | ||
{ | ||
class block; | ||
class ledger; | ||
} | ||
|
||
namespace nano | ||
{ | ||
class block_checker | ||
{ | ||
public: | ||
enum class block_op | ||
{ | ||
receive, | ||
send, | ||
noop, | ||
epoch | ||
}; | ||
// Context that is passed between pipeline stages | ||
class context | ||
{ | ||
public: | ||
context (nano::ledger & ledger, std::shared_ptr<nano::block> block); | ||
bool is_send () const; | ||
nano::account account () const; | ||
nano::block_hash source () const; | ||
nano::account signer (nano::epochs const & epochs) const; | ||
bool gap_previous () const; | ||
block_op block_op () const; | ||
std::shared_ptr<nano::block> block; | ||
std::shared_ptr<nano::block> previous; | ||
std::optional<nano::account_info> state; | ||
std::optional<nano::pending_info> pending; | ||
bool any_pending{ false }; | ||
bool source_exists{ false }; | ||
nano::epochs & epochs; | ||
}; | ||
|
||
public: | ||
block_checker (); | ||
nano::process_result check (context & context); | ||
}; | ||
} // namespace nano |
Oops, something went wrong.