dncurrency/nano/secure/ledger.cpp
cryptocode bab4474274
Network selector (#1729)
* Network selector

* Make sure network option is checked before working path is called (migration). Also remove bool assignment from error.

* Formatting

* Fix merge error

* Use network_params for RPC port config (rebase)

* Formatting

* Rebase

* Rebase (debug_opencl, merge fix)

* Rebase fix

* post-rebase update
2019-03-11 16:10:33 +01:00

1073 lines
46 KiB
C++

#include <nano/node/common.hpp>
#include <nano/node/stats.hpp>
#include <nano/secure/blockstore.hpp>
#include <nano/secure/ledger.hpp>
namespace
{
/**
* Roll back the visited block
*/
class rollback_visitor : public nano::block_visitor
{
public:
rollback_visitor (nano::transaction const & transaction_a, nano::ledger & ledger_a, std::vector<nano::block_hash> & list_a) :
transaction (transaction_a),
ledger (ledger_a),
list (list_a)
{
}
virtual ~rollback_visitor () = default;
void send_block (nano::send_block const & block_a) override
{
auto hash (block_a.hash ());
nano::pending_info pending;
nano::pending_key key (block_a.hashables.destination, hash);
while (!error && ledger.store.pending_get (transaction, key, pending))
{
error = ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.destination), list);
}
if (!error)
{
nano::account_info info;
auto error (ledger.store.account_get (transaction, pending.source, info));
assert (!error);
ledger.store.pending_del (transaction, key);
ledger.store.representation_add (transaction, ledger.representative (transaction, hash), pending.amount.number ());
ledger.change_latest (transaction, pending.source, block_a.hashables.previous, info.rep_block, ledger.balance (transaction, block_a.hashables.previous), info.block_count - 1);
ledger.store.block_del (transaction, hash);
ledger.store.frontier_del (transaction, hash);
ledger.store.frontier_put (transaction, block_a.hashables.previous, pending.source);
ledger.store.block_successor_clear (transaction, block_a.hashables.previous);
ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::send);
}
}
void receive_block (nano::receive_block const & block_a) override
{
auto hash (block_a.hash ());
auto representative (ledger.representative (transaction, block_a.hashables.previous));
auto amount (ledger.amount (transaction, block_a.hashables.source));
auto destination_account (ledger.account (transaction, hash));
auto source_account (ledger.account (transaction, block_a.hashables.source));
nano::account_info info;
auto error (ledger.store.account_get (transaction, destination_account, info));
assert (!error);
ledger.store.representation_add (transaction, ledger.representative (transaction, hash), 0 - amount);
ledger.change_latest (transaction, destination_account, block_a.hashables.previous, representative, ledger.balance (transaction, block_a.hashables.previous), info.block_count - 1);
ledger.store.block_del (transaction, hash);
ledger.store.pending_put (transaction, nano::pending_key (destination_account, block_a.hashables.source), { source_account, amount, nano::epoch::epoch_0 });
ledger.store.frontier_del (transaction, hash);
ledger.store.frontier_put (transaction, block_a.hashables.previous, destination_account);
ledger.store.block_successor_clear (transaction, block_a.hashables.previous);
ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::receive);
}
void open_block (nano::open_block const & block_a) override
{
auto hash (block_a.hash ());
auto amount (ledger.amount (transaction, block_a.hashables.source));
auto destination_account (ledger.account (transaction, hash));
auto source_account (ledger.account (transaction, block_a.hashables.source));
ledger.store.representation_add (transaction, ledger.representative (transaction, hash), 0 - amount);
ledger.change_latest (transaction, destination_account, 0, 0, 0, 0);
ledger.store.block_del (transaction, hash);
ledger.store.pending_put (transaction, nano::pending_key (destination_account, block_a.hashables.source), { source_account, amount, nano::epoch::epoch_0 });
ledger.store.frontier_del (transaction, hash);
ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::open);
}
void change_block (nano::change_block const & block_a) override
{
auto hash (block_a.hash ());
auto representative (ledger.representative (transaction, block_a.hashables.previous));
auto account (ledger.account (transaction, block_a.hashables.previous));
nano::account_info info;
auto error (ledger.store.account_get (transaction, account, info));
assert (!error);
auto balance (ledger.balance (transaction, block_a.hashables.previous));
ledger.store.representation_add (transaction, representative, balance);
ledger.store.representation_add (transaction, hash, 0 - balance);
ledger.store.block_del (transaction, hash);
ledger.change_latest (transaction, account, block_a.hashables.previous, representative, info.balance, info.block_count - 1);
ledger.store.frontier_del (transaction, hash);
ledger.store.frontier_put (transaction, block_a.hashables.previous, account);
ledger.store.block_successor_clear (transaction, block_a.hashables.previous);
ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::change);
}
void state_block (nano::state_block const & block_a) override
{
auto hash (block_a.hash ());
nano::block_hash representative (0);
if (!block_a.hashables.previous.is_zero ())
{
representative = ledger.representative (transaction, block_a.hashables.previous);
}
auto balance (ledger.balance (transaction, block_a.hashables.previous));
auto is_send (block_a.hashables.balance < balance);
// Add in amount delta
ledger.store.representation_add (transaction, hash, 0 - block_a.hashables.balance.number ());
if (!representative.is_zero ())
{
// Move existing representation
ledger.store.representation_add (transaction, representative, balance);
}
nano::account_info info;
auto error (ledger.store.account_get (transaction, block_a.hashables.account, info));
if (is_send)
{
nano::pending_key key (block_a.hashables.link, hash);
while (!error && !ledger.store.pending_exists (transaction, key))
{
error = ledger.rollback (transaction, ledger.latest (transaction, block_a.hashables.link), list);
}
ledger.store.pending_del (transaction, key);
ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::send);
}
else if (!block_a.hashables.link.is_zero () && !ledger.is_epoch_link (block_a.hashables.link))
{
auto source_version (ledger.store.block_version (transaction, block_a.hashables.link));
nano::pending_info pending_info (ledger.account (transaction, block_a.hashables.link), block_a.hashables.balance.number () - balance, source_version);
ledger.store.pending_put (transaction, nano::pending_key (block_a.hashables.account, block_a.hashables.link), pending_info);
ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::receive);
}
assert (!error);
auto previous_version (ledger.store.block_version (transaction, block_a.hashables.previous));
ledger.change_latest (transaction, block_a.hashables.account, block_a.hashables.previous, representative, balance, info.block_count - 1, false, previous_version);
auto previous (ledger.store.block_get (transaction, block_a.hashables.previous));
if (previous != nullptr)
{
ledger.store.block_successor_clear (transaction, block_a.hashables.previous);
if (previous->type () < nano::block_type::state)
{
ledger.store.frontier_put (transaction, block_a.hashables.previous, block_a.hashables.account);
}
}
else
{
ledger.stats.inc (nano::stat::type::rollback, nano::stat::detail::open);
}
ledger.store.block_del (transaction, hash);
}
nano::transaction const & transaction;
nano::ledger & ledger;
std::vector<nano::block_hash> & list;
bool error{ false };
};
class ledger_processor : public nano::block_visitor
{
public:
ledger_processor (nano::ledger &, nano::transaction const &, nano::signature_verification = nano::signature_verification::unknown);
virtual ~ledger_processor () = default;
void send_block (nano::send_block const &) override;
void receive_block (nano::receive_block const &) override;
void open_block (nano::open_block const &) override;
void change_block (nano::change_block const &) override;
void state_block (nano::state_block const &) override;
void state_block_impl (nano::state_block const &);
void epoch_block_impl (nano::state_block const &);
nano::ledger & ledger;
nano::transaction const & transaction;
nano::signature_verification verification;
nano::process_return result;
};
void ledger_processor::state_block (nano::state_block const & block_a)
{
result.code = nano::process_result::progress;
auto is_epoch_block (false);
// Check if this is an epoch block
if (!ledger.epoch_link.is_zero () && ledger.is_epoch_link (block_a.hashables.link))
{
nano::amount prev_balance (0);
if (!block_a.hashables.previous.is_zero ())
{
result.code = ledger.store.block_exists (transaction, block_a.hashables.previous) ? nano::process_result::progress : nano::process_result::gap_previous;
if (result.code == nano::process_result::progress)
{
prev_balance = ledger.balance (transaction, block_a.hashables.previous);
}
else if (result.verified == nano::signature_verification::unknown)
{
// Check for possible regular state blocks with epoch link (send subtype)
if (validate_message (block_a.hashables.account, block_a.hash (), block_a.signature))
{
// Is epoch block signed correctly
if (validate_message (ledger.epoch_signer, block_a.hash (), block_a.signature))
{
result.verified = nano::signature_verification::invalid;
result.code = nano::process_result::bad_signature;
}
else
{
result.verified = nano::signature_verification::valid_epoch;
}
}
else
{
result.verified = nano::signature_verification::valid;
}
}
}
if (block_a.hashables.balance == prev_balance)
{
is_epoch_block = true;
}
}
if (result.code == nano::process_result::progress)
{
if (is_epoch_block)
{
epoch_block_impl (block_a);
}
else
{
state_block_impl (block_a);
}
}
}
void ledger_processor::state_block_impl (nano::state_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, block_a.type (), hash));
result.code = existing ? nano::process_result::old : nano::process_result::progress; // Have we seen this block before? (Unambiguous)
if (result.code == nano::process_result::progress)
{
// Validate block if not verified outside of ledger
if (result.verified != nano::signature_verification::valid)
{
result.code = validate_message (block_a.hashables.account, hash, block_a.signature) ? nano::process_result::bad_signature : nano::process_result::progress; // Is this block signed correctly (Unambiguous)
}
if (result.code == nano::process_result::progress)
{
assert (!validate_message (block_a.hashables.account, hash, block_a.signature));
result.verified = nano::signature_verification::valid;
result.code = block_a.hashables.account.is_zero () ? nano::process_result::opened_burn_account : nano::process_result::progress; // Is this for the burn account? (Unambiguous)
if (result.code == nano::process_result::progress)
{
nano::epoch epoch (nano::epoch::epoch_0);
nano::account_info info;
result.amount = block_a.hashables.balance;
auto is_send (false);
auto account_error (ledger.store.account_get (transaction, block_a.hashables.account, info));
if (!account_error)
{
epoch = info.epoch;
// Account already exists
result.code = block_a.hashables.previous.is_zero () ? nano::process_result::fork : nano::process_result::progress; // Has this account already been opened? (Ambigious)
if (result.code == nano::process_result::progress)
{
result.code = ledger.store.block_exists (transaction, block_a.hashables.previous) ? nano::process_result::progress : nano::process_result::gap_previous; // Does the previous block exist in the ledger? (Unambigious)
if (result.code == nano::process_result::progress)
{
is_send = block_a.hashables.balance < info.balance;
result.amount = is_send ? (info.balance.number () - result.amount.number ()) : (result.amount.number () - info.balance.number ());
result.code = block_a.hashables.previous == info.head ? nano::process_result::progress : nano::process_result::fork; // Is the previous block the account's head block? (Ambigious)
}
}
}
else
{
// Account does not yet exists
result.code = block_a.previous ().is_zero () ? nano::process_result::progress : nano::process_result::gap_previous; // Does the first block in an account yield 0 for previous() ? (Unambigious)
if (result.code == nano::process_result::progress)
{
result.code = !block_a.hashables.link.is_zero () ? nano::process_result::progress : nano::process_result::gap_source; // Is the first block receiving from a send ? (Unambigious)
}
}
if (result.code == nano::process_result::progress)
{
if (!is_send)
{
if (!block_a.hashables.link.is_zero ())
{
result.code = ledger.store.source_exists (transaction, block_a.hashables.link) ? nano::process_result::progress : nano::process_result::gap_source; // Have we seen the source block already? (Harmless)
if (result.code == nano::process_result::progress)
{
nano::pending_key key (block_a.hashables.account, block_a.hashables.link);
nano::pending_info pending;
result.code = ledger.store.pending_get (transaction, key, pending) ? nano::process_result::unreceivable : nano::process_result::progress; // Has this source already been received (Malformed)
if (result.code == nano::process_result::progress)
{
result.code = result.amount == pending.amount ? nano::process_result::progress : nano::process_result::balance_mismatch;
epoch = std::max (epoch, pending.epoch);
}
}
}
else
{
// If there's no link, the balance must remain the same, only the representative can change
result.code = result.amount.is_zero () ? nano::process_result::progress : nano::process_result::balance_mismatch;
}
}
}
if (result.code == nano::process_result::progress)
{
ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::state_block);
result.state_is_send = is_send;
nano::block_sideband sideband (nano::block_type::state, block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch ());
ledger.store.block_put (transaction, hash, block_a, sideband, epoch);
if (!info.rep_block.is_zero ())
{
// Move existing representation
ledger.store.representation_add (transaction, info.rep_block, 0 - info.balance.number ());
}
// Add in amount delta
ledger.store.representation_add (transaction, hash, block_a.hashables.balance.number ());
if (is_send)
{
nano::pending_key key (block_a.hashables.link, hash);
nano::pending_info info (block_a.hashables.account, result.amount.number (), epoch);
ledger.store.pending_put (transaction, key, info);
}
else if (!block_a.hashables.link.is_zero ())
{
ledger.store.pending_del (transaction, nano::pending_key (block_a.hashables.account, block_a.hashables.link));
}
ledger.change_latest (transaction, block_a.hashables.account, hash, hash, block_a.hashables.balance, info.block_count + 1, true, epoch);
if (!ledger.store.frontier_get (transaction, info.head).is_zero ())
{
ledger.store.frontier_del (transaction, info.head);
}
// Frontier table is unnecessary for state blocks and this also prevents old blocks from being inserted on top of state blocks
result.account = block_a.hashables.account;
}
}
}
}
}
void ledger_processor::epoch_block_impl (nano::state_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, block_a.type (), hash));
result.code = existing ? nano::process_result::old : nano::process_result::progress; // Have we seen this block before? (Unambiguous)
if (result.code == nano::process_result::progress)
{
// Validate block if not verified outside of ledger
if (result.verified != nano::signature_verification::valid_epoch)
{
result.code = validate_message (ledger.epoch_signer, hash, block_a.signature) ? nano::process_result::bad_signature : nano::process_result::progress; // Is this block signed correctly (Unambiguous)
}
if (result.code == nano::process_result::progress)
{
assert (!validate_message (ledger.epoch_signer, hash, block_a.signature));
result.verified = nano::signature_verification::valid_epoch;
result.code = block_a.hashables.account.is_zero () ? nano::process_result::opened_burn_account : nano::process_result::progress; // Is this for the burn account? (Unambiguous)
if (result.code == nano::process_result::progress)
{
nano::account_info info;
auto account_error (ledger.store.account_get (transaction, block_a.hashables.account, info));
if (!account_error)
{
// Account already exists
result.code = block_a.hashables.previous.is_zero () ? nano::process_result::fork : nano::process_result::progress; // Has this account already been opened? (Ambigious)
if (result.code == nano::process_result::progress)
{
result.code = block_a.hashables.previous == info.head ? nano::process_result::progress : nano::process_result::fork; // Is the previous block the account's head block? (Ambigious)
if (result.code == nano::process_result::progress)
{
auto last_rep_block (ledger.store.block_get (transaction, info.rep_block));
assert (last_rep_block != nullptr);
result.code = block_a.hashables.representative == last_rep_block->representative () ? nano::process_result::progress : nano::process_result::representative_mismatch;
}
}
}
else
{
result.code = block_a.hashables.representative.is_zero () ? nano::process_result::progress : nano::process_result::representative_mismatch;
}
if (result.code == nano::process_result::progress)
{
result.code = info.epoch == nano::epoch::epoch_0 ? nano::process_result::progress : nano::process_result::block_position;
if (result.code == nano::process_result::progress)
{
result.code = block_a.hashables.balance == info.balance ? nano::process_result::progress : nano::process_result::balance_mismatch;
if (result.code == nano::process_result::progress)
{
ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::epoch_block);
result.account = block_a.hashables.account;
result.amount = 0;
nano::block_sideband sideband (nano::block_type::state, block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch ());
ledger.store.block_put (transaction, hash, block_a, sideband, nano::epoch::epoch_1);
ledger.change_latest (transaction, block_a.hashables.account, hash, hash, info.balance, info.block_count + 1, true, nano::epoch::epoch_1);
if (!ledger.store.frontier_get (transaction, info.head).is_zero ())
{
ledger.store.frontier_del (transaction, info.head);
}
}
}
}
}
}
}
}
void ledger_processor::change_block (nano::change_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, block_a.type (), hash));
result.code = existing ? nano::process_result::old : nano::process_result::progress; // Have we seen this block before? (Harmless)
if (result.code == nano::process_result::progress)
{
auto previous (ledger.store.block_get (transaction, block_a.hashables.previous));
result.code = previous != nullptr ? nano::process_result::progress : nano::process_result::gap_previous; // Have we seen the previous block already? (Harmless)
if (result.code == nano::process_result::progress)
{
result.code = block_a.valid_predecessor (*previous) ? nano::process_result::progress : nano::process_result::block_position;
if (result.code == nano::process_result::progress)
{
auto account (ledger.store.frontier_get (transaction, block_a.hashables.previous));
result.code = account.is_zero () ? nano::process_result::fork : nano::process_result::progress;
if (result.code == nano::process_result::progress)
{
nano::account_info info;
auto latest_error (ledger.store.account_get (transaction, account, info));
assert (!latest_error);
assert (info.head == block_a.hashables.previous);
// Validate block if not verified outside of ledger
if (result.verified != nano::signature_verification::valid)
{
result.code = validate_message (account, hash, block_a.signature) ? nano::process_result::bad_signature : nano::process_result::progress; // Is this block signed correctly (Malformed)
}
if (result.code == nano::process_result::progress)
{
assert (!validate_message (account, hash, block_a.signature));
result.verified = nano::signature_verification::valid;
nano::block_sideband sideband (nano::block_type::change, account, 0, info.balance, info.block_count + 1, nano::seconds_since_epoch ());
ledger.store.block_put (transaction, hash, block_a, sideband);
auto balance (ledger.balance (transaction, block_a.hashables.previous));
ledger.store.representation_add (transaction, hash, balance);
ledger.store.representation_add (transaction, info.rep_block, 0 - balance);
ledger.change_latest (transaction, account, hash, hash, info.balance, info.block_count + 1);
ledger.store.frontier_del (transaction, block_a.hashables.previous);
ledger.store.frontier_put (transaction, hash, account);
result.account = account;
result.amount = 0;
ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::change);
}
}
}
}
}
}
void ledger_processor::send_block (nano::send_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, block_a.type (), hash));
result.code = existing ? nano::process_result::old : nano::process_result::progress; // Have we seen this block before? (Harmless)
if (result.code == nano::process_result::progress)
{
auto previous (ledger.store.block_get (transaction, block_a.hashables.previous));
result.code = previous != nullptr ? nano::process_result::progress : nano::process_result::gap_previous; // Have we seen the previous block already? (Harmless)
if (result.code == nano::process_result::progress)
{
result.code = block_a.valid_predecessor (*previous) ? nano::process_result::progress : nano::process_result::block_position;
if (result.code == nano::process_result::progress)
{
auto account (ledger.store.frontier_get (transaction, block_a.hashables.previous));
result.code = account.is_zero () ? nano::process_result::fork : nano::process_result::progress;
if (result.code == nano::process_result::progress)
{
// Validate block if not verified outside of ledger
if (result.verified != nano::signature_verification::valid)
{
result.code = validate_message (account, hash, block_a.signature) ? nano::process_result::bad_signature : nano::process_result::progress; // Is this block signed correctly (Malformed)
}
if (result.code == nano::process_result::progress)
{
assert (!validate_message (account, hash, block_a.signature));
result.verified = nano::signature_verification::valid;
nano::account_info info;
auto latest_error (ledger.store.account_get (transaction, account, info));
assert (!latest_error);
assert (info.head == block_a.hashables.previous);
result.code = info.balance.number () >= block_a.hashables.balance.number () ? nano::process_result::progress : nano::process_result::negative_spend; // Is this trying to spend a negative amount (Malicious)
if (result.code == nano::process_result::progress)
{
auto amount (info.balance.number () - block_a.hashables.balance.number ());
ledger.store.representation_add (transaction, info.rep_block, 0 - amount);
nano::block_sideband sideband (nano::block_type::send, account, 0, block_a.hashables.balance /* unused */, info.block_count + 1, nano::seconds_since_epoch ());
ledger.store.block_put (transaction, hash, block_a, sideband);
ledger.change_latest (transaction, account, hash, info.rep_block, block_a.hashables.balance, info.block_count + 1);
ledger.store.pending_put (transaction, nano::pending_key (block_a.hashables.destination, hash), { account, amount, nano::epoch::epoch_0 });
ledger.store.frontier_del (transaction, block_a.hashables.previous);
ledger.store.frontier_put (transaction, hash, account);
result.account = account;
result.amount = amount;
result.pending_account = block_a.hashables.destination;
ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::send);
}
}
}
}
}
}
}
void ledger_processor::receive_block (nano::receive_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, block_a.type (), hash));
result.code = existing ? nano::process_result::old : nano::process_result::progress; // Have we seen this block already? (Harmless)
if (result.code == nano::process_result::progress)
{
auto previous (ledger.store.block_get (transaction, block_a.hashables.previous));
result.code = previous != nullptr ? nano::process_result::progress : nano::process_result::gap_previous;
if (result.code == nano::process_result::progress)
{
result.code = block_a.valid_predecessor (*previous) ? nano::process_result::progress : nano::process_result::block_position;
if (result.code == nano::process_result::progress)
{
auto account (ledger.store.frontier_get (transaction, block_a.hashables.previous));
result.code = account.is_zero () ? nano::process_result::gap_previous : nano::process_result::progress; //Have we seen the previous block? No entries for account at all (Harmless)
if (result.code == nano::process_result::progress)
{
// Validate block if not verified outside of ledger
if (result.verified != nano::signature_verification::valid)
{
result.code = validate_message (account, hash, block_a.signature) ? nano::process_result::bad_signature : nano::process_result::progress; // Is the signature valid (Malformed)
}
if (result.code == nano::process_result::progress)
{
assert (!validate_message (account, hash, block_a.signature));
result.verified = nano::signature_verification::valid;
result.code = ledger.store.source_exists (transaction, block_a.hashables.source) ? nano::process_result::progress : nano::process_result::gap_source; // Have we seen the source block already? (Harmless)
if (result.code == nano::process_result::progress)
{
nano::account_info info;
ledger.store.account_get (transaction, account, info);
result.code = info.head == block_a.hashables.previous ? nano::process_result::progress : nano::process_result::gap_previous; // Block doesn't immediately follow latest block (Harmless)
if (result.code == nano::process_result::progress)
{
nano::pending_key key (account, block_a.hashables.source);
nano::pending_info pending;
result.code = ledger.store.pending_get (transaction, key, pending) ? nano::process_result::unreceivable : nano::process_result::progress; // Has this source already been received (Malformed)
if (result.code == nano::process_result::progress)
{
result.code = pending.epoch == nano::epoch::epoch_0 ? nano::process_result::progress : nano::process_result::unreceivable; // Are we receiving a state-only send? (Malformed)
if (result.code == nano::process_result::progress)
{
auto new_balance (info.balance.number () + pending.amount.number ());
nano::account_info source_info;
auto error (ledger.store.account_get (transaction, pending.source, source_info));
assert (!error);
ledger.store.pending_del (transaction, key);
nano::block_sideband sideband (nano::block_type::receive, account, 0, new_balance, info.block_count + 1, nano::seconds_since_epoch ());
ledger.store.block_put (transaction, hash, block_a, sideband);
ledger.change_latest (transaction, account, hash, info.rep_block, new_balance, info.block_count + 1);
ledger.store.representation_add (transaction, info.rep_block, pending.amount.number ());
ledger.store.frontier_del (transaction, block_a.hashables.previous);
ledger.store.frontier_put (transaction, hash, account);
result.account = account;
result.amount = pending.amount;
ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::receive);
}
}
}
}
}
}
else
{
result.code = ledger.store.block_exists (transaction, block_a.hashables.previous) ? nano::process_result::fork : nano::process_result::gap_previous; // If we have the block but it's not the latest we have a signed fork (Malicious)
}
}
}
}
}
void ledger_processor::open_block (nano::open_block const & block_a)
{
auto hash (block_a.hash ());
auto existing (ledger.store.block_exists (transaction, block_a.type (), hash));
result.code = existing ? nano::process_result::old : nano::process_result::progress; // Have we seen this block already? (Harmless)
if (result.code == nano::process_result::progress)
{
// Validate block if not verified outside of ledger
if (result.verified != nano::signature_verification::valid)
{
result.code = validate_message (block_a.hashables.account, hash, block_a.signature) ? nano::process_result::bad_signature : nano::process_result::progress; // Is the signature valid (Malformed)
}
if (result.code == nano::process_result::progress)
{
assert (!validate_message (block_a.hashables.account, hash, block_a.signature));
result.verified = nano::signature_verification::valid;
result.code = ledger.store.source_exists (transaction, block_a.hashables.source) ? nano::process_result::progress : nano::process_result::gap_source; // Have we seen the source block? (Harmless)
if (result.code == nano::process_result::progress)
{
nano::account_info info;
result.code = ledger.store.account_get (transaction, block_a.hashables.account, info) ? nano::process_result::progress : nano::process_result::fork; // Has this account already been opened? (Malicious)
if (result.code == nano::process_result::progress)
{
nano::pending_key key (block_a.hashables.account, block_a.hashables.source);
nano::pending_info pending;
result.code = ledger.store.pending_get (transaction, key, pending) ? nano::process_result::unreceivable : nano::process_result::progress; // Has this source already been received (Malformed)
if (result.code == nano::process_result::progress)
{
result.code = block_a.hashables.account == ledger.network_params.ledger.burn_account ? nano::process_result::opened_burn_account : nano::process_result::progress; // Is it burning 0 account? (Malicious)
if (result.code == nano::process_result::progress)
{
result.code = pending.epoch == nano::epoch::epoch_0 ? nano::process_result::progress : nano::process_result::unreceivable; // Are we receiving a state-only send? (Malformed)
if (result.code == nano::process_result::progress)
{
nano::account_info source_info;
auto error (ledger.store.account_get (transaction, pending.source, source_info));
assert (!error);
ledger.store.pending_del (transaction, key);
nano::block_sideband sideband (nano::block_type::open, block_a.hashables.account, 0, pending.amount, 1, nano::seconds_since_epoch ());
ledger.store.block_put (transaction, hash, block_a, sideband);
ledger.change_latest (transaction, block_a.hashables.account, hash, hash, pending.amount.number (), 1);
ledger.store.representation_add (transaction, hash, pending.amount.number ());
ledger.store.frontier_put (transaction, hash, block_a.hashables.account);
result.account = block_a.hashables.account;
result.amount = pending.amount;
ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::open);
}
}
}
}
}
}
}
}
ledger_processor::ledger_processor (nano::ledger & ledger_a, nano::transaction const & transaction_a, nano::signature_verification verification_a) :
ledger (ledger_a),
transaction (transaction_a),
verification (verification_a)
{
result.verified = verification;
}
} // namespace
size_t nano::shared_ptr_block_hash::operator() (std::shared_ptr<nano::block> const & block_a) const
{
auto hash (block_a->hash ());
auto result (static_cast<size_t> (hash.qwords[0]));
return result;
}
bool nano::shared_ptr_block_hash::operator() (std::shared_ptr<nano::block> const & lhs, std::shared_ptr<nano::block> const & rhs) const
{
return lhs->hash () == rhs->hash ();
}
nano::ledger::ledger (nano::block_store & store_a, nano::stat & stat_a, nano::uint256_union const & epoch_link_a, nano::account const & epoch_signer_a) :
store (store_a),
stats (stat_a),
check_bootstrap_weights (true),
epoch_link (epoch_link_a),
epoch_signer (epoch_signer_a)
{
}
// Balance for account containing hash
nano::uint128_t nano::ledger::balance (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
{
return hash_a.is_zero () ? 0 : store.block_balance (transaction_a, hash_a);
}
// Balance for an account by account number
nano::uint128_t nano::ledger::account_balance (nano::transaction const & transaction_a, nano::account const & account_a)
{
nano::uint128_t result (0);
nano::account_info info;
auto none (store.account_get (transaction_a, account_a, info));
if (!none)
{
result = info.balance.number ();
}
return result;
}
nano::uint128_t nano::ledger::account_pending (nano::transaction const & transaction_a, nano::account const & account_a)
{
nano::uint128_t result (0);
nano::account end (account_a.number () + 1);
for (auto i (store.pending_v0_begin (transaction_a, nano::pending_key (account_a, 0))), n (store.pending_v0_begin (transaction_a, nano::pending_key (end, 0))); i != n; ++i)
{
nano::pending_info info (i->second);
result += info.amount.number ();
}
for (auto i (store.pending_v1_begin (transaction_a, nano::pending_key (account_a, 0))), n (store.pending_v1_begin (transaction_a, nano::pending_key (end, 0))); i != n; ++i)
{
nano::pending_info info (i->second);
result += info.amount.number ();
}
return result;
}
nano::process_return nano::ledger::process (nano::transaction const & transaction_a, nano::block const & block_a, nano::signature_verification verification)
{
ledger_processor processor (*this, transaction_a, verification);
block_a.visit (processor);
return processor.result;
}
nano::block_hash nano::ledger::representative (nano::transaction const & transaction_a, nano::block_hash const & hash_a)
{
auto result (representative_calculated (transaction_a, hash_a));
assert (result.is_zero () || store.block_exists (transaction_a, result));
return result;
}
nano::block_hash nano::ledger::representative_calculated (nano::transaction const & transaction_a, nano::block_hash const & hash_a)
{
representative_visitor visitor (transaction_a, store);
visitor.compute (hash_a);
return visitor.result;
}
bool nano::ledger::block_exists (nano::block_hash const & hash_a)
{
auto transaction (store.tx_begin_read ());
auto result (store.block_exists (transaction, hash_a));
return result;
}
bool nano::ledger::block_exists (nano::block_type type, nano::block_hash const & hash_a)
{
auto transaction (store.tx_begin_read ());
auto result (store.block_exists (transaction, type, hash_a));
return result;
}
std::string nano::ledger::block_text (char const * hash_a)
{
return block_text (nano::block_hash (hash_a));
}
std::string nano::ledger::block_text (nano::block_hash const & hash_a)
{
std::string result;
auto transaction (store.tx_begin_read ());
auto block (store.block_get (transaction, hash_a));
if (block != nullptr)
{
block->serialize_json (result);
}
return result;
}
bool nano::ledger::is_send (nano::transaction const & transaction_a, nano::state_block const & block_a) const
{
bool result (false);
nano::block_hash previous (block_a.hashables.previous);
if (!previous.is_zero ())
{
if (block_a.hashables.balance < balance (transaction_a, previous))
{
result = true;
}
}
return result;
}
nano::block_hash nano::ledger::block_destination (nano::transaction const & transaction_a, nano::block const & block_a)
{
nano::block_hash result (0);
nano::send_block const * send_block (dynamic_cast<nano::send_block const *> (&block_a));
nano::state_block const * state_block (dynamic_cast<nano::state_block const *> (&block_a));
if (send_block != nullptr)
{
result = send_block->hashables.destination;
}
else if (state_block != nullptr && is_send (transaction_a, *state_block))
{
result = state_block->hashables.link;
}
return result;
}
nano::block_hash nano::ledger::block_source (nano::transaction const & transaction_a, nano::block const & block_a)
{
/*
* block_source() requires that the previous block of the block
* passed in exist in the database. This is because it will try
* to check account balances to determine if it is a send block.
*/
assert (block_a.previous ().is_zero () || store.block_exists (transaction_a, block_a.previous ()));
// If block_a.source () is nonzero, then we have our source.
// However, universal blocks will always return zero.
nano::block_hash result (block_a.source ());
nano::state_block const * state_block (dynamic_cast<nano::state_block const *> (&block_a));
if (state_block != nullptr && !is_send (transaction_a, *state_block))
{
result = state_block->hashables.link;
}
return result;
}
// Vote weight of an account
nano::uint128_t nano::ledger::weight (nano::transaction const & transaction_a, nano::account const & account_a)
{
if (check_bootstrap_weights.load ())
{
auto blocks = store.block_count (transaction_a);
if (blocks.sum () < bootstrap_weight_max_blocks)
{
auto weight = bootstrap_weights.find (account_a);
if (weight != bootstrap_weights.end ())
{
return weight->second;
}
}
else
{
check_bootstrap_weights = false;
}
}
return store.representation_get (transaction_a, account_a);
}
// Rollback blocks until `block_a' doesn't exist or it tries to penetrate the confirmation height
bool nano::ledger::rollback (nano::transaction const & transaction_a, nano::block_hash const & block_a, std::vector<nano::block_hash> & list_a)
{
assert (store.block_exists (transaction_a, block_a));
auto account_l (account (transaction_a, block_a));
auto block_account_height (store.block_account_height (transaction_a, block_a));
rollback_visitor rollback (transaction_a, *this, list_a);
nano::account_info account_info;
auto error (false);
while (!error && store.block_exists (transaction_a, block_a))
{
auto latest_error (store.account_get (transaction_a, account_l, account_info));
assert (!latest_error);
if (block_account_height > account_info.confirmation_height)
{
auto block (store.block_get (transaction_a, account_info.head));
list_a.push_back (account_info.head);
block->visit (rollback);
error = rollback.error;
}
else
{
error = true;
}
}
return error;
}
bool nano::ledger::rollback (nano::transaction const & transaction_a, nano::block_hash const & block_a)
{
std::vector<nano::block_hash> rollback_list;
return rollback (transaction_a, block_a, rollback_list);
}
// Return account containing hash
nano::account nano::ledger::account (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
{
return store.block_account (transaction_a, hash_a);
}
// Return amount decrease or increase for block
nano::uint128_t nano::ledger::amount (nano::transaction const & transaction_a, nano::block_hash const & hash_a)
{
nano::uint128_t result;
if (hash_a != network_params.ledger.genesis_account)
{
auto block (store.block_get (transaction_a, hash_a));
auto block_balance (balance (transaction_a, hash_a));
auto previous_balance (balance (transaction_a, block->previous ()));
result = block_balance > previous_balance ? block_balance - previous_balance : previous_balance - block_balance;
}
else
{
result = network_params.ledger.genesis_amount;
}
return result;
}
// Return latest block for account
nano::block_hash nano::ledger::latest (nano::transaction const & transaction_a, nano::account const & account_a)
{
nano::account_info info;
auto latest_error (store.account_get (transaction_a, account_a, info));
return latest_error ? 0 : info.head;
}
// Return latest root for account, account number of there are no blocks for this account.
nano::block_hash nano::ledger::latest_root (nano::transaction const & transaction_a, nano::account const & account_a)
{
nano::account_info info;
auto latest_error (store.account_get (transaction_a, account_a, info));
nano::block_hash result;
if (latest_error)
{
result = account_a;
}
else
{
result = info.head;
}
return result;
}
void nano::ledger::dump_account_chain (nano::account const & account_a)
{
auto transaction (store.tx_begin_read ());
auto hash (latest (transaction, account_a));
while (!hash.is_zero ())
{
auto block (store.block_get (transaction, hash));
assert (block != nullptr);
std::cerr << hash.to_string () << std::endl;
hash = block->previous ();
}
}
class block_fit_visitor : public nano::block_visitor
{
public:
block_fit_visitor (nano::ledger & ledger_a, nano::transaction const & transaction_a) :
ledger (ledger_a),
transaction (transaction_a),
result (false)
{
}
void send_block (nano::send_block const & block_a) override
{
result = ledger.store.block_exists (transaction, block_a.previous ());
}
void receive_block (nano::receive_block const & block_a) override
{
result = ledger.store.block_exists (transaction, block_a.previous ());
result &= ledger.store.block_exists (transaction, block_a.source ());
}
void open_block (nano::open_block const & block_a) override
{
result = ledger.store.block_exists (transaction, block_a.source ());
}
void change_block (nano::change_block const & block_a) override
{
result = ledger.store.block_exists (transaction, block_a.previous ());
}
void state_block (nano::state_block const & block_a) override
{
result = block_a.previous ().is_zero () || ledger.store.block_exists (transaction, block_a.previous ());
if (result && !ledger.is_send (transaction, block_a))
{
result &= ledger.store.block_exists (transaction, block_a.hashables.link) || block_a.hashables.link.is_zero () || ledger.is_epoch_link (block_a.hashables.link);
}
}
nano::ledger & ledger;
nano::transaction const & transaction;
bool result;
};
bool nano::ledger::could_fit (nano::transaction const & transaction_a, nano::block const & block_a)
{
block_fit_visitor visitor (*this, transaction_a);
block_a.visit (visitor);
return visitor.result;
}
bool nano::ledger::is_epoch_link (nano::uint256_union const & link_a)
{
return link_a == epoch_link;
}
void nano::ledger::change_latest (nano::transaction const & transaction_a, nano::account const & account_a, nano::block_hash const & hash_a, nano::block_hash const & rep_block_a, nano::amount const & balance_a, uint64_t block_count_a, bool is_state, nano::epoch epoch_a)
{
nano::account_info info;
auto exists (!store.account_get (transaction_a, account_a, info));
if (!exists)
{
assert (store.block_get (transaction_a, hash_a)->previous ().is_zero ());
info.open_block = hash_a;
}
if (!hash_a.is_zero ())
{
info.head = hash_a;
info.rep_block = rep_block_a;
info.balance = balance_a;
info.modified = nano::seconds_since_epoch ();
info.block_count = block_count_a;
if (exists && info.epoch != epoch_a)
{
// otherwise we'd end up with a duplicate
store.account_del (transaction_a, account_a);
}
info.epoch = epoch_a;
store.account_put (transaction_a, account_a, info);
}
else
{
store.account_del (transaction_a, account_a);
}
}
std::shared_ptr<nano::block> nano::ledger::successor (nano::transaction const & transaction_a, nano::uint512_union const & root_a)
{
nano::block_hash successor (0);
if (root_a.uint256s[0].is_zero () && store.account_exists (transaction_a, root_a.uint256s[1]))
{
nano::account_info info;
auto error (store.account_get (transaction_a, root_a.uint256s[1], info));
assert (!error);
successor = info.open_block;
}
else
{
successor = store.block_successor (transaction_a, root_a.uint256s[0]);
}
std::shared_ptr<nano::block> result;
if (!successor.is_zero ())
{
result = store.block_get (transaction_a, successor);
}
assert (successor.is_zero () || result != nullptr);
return result;
}
std::shared_ptr<nano::block> nano::ledger::forked_block (nano::transaction const & transaction_a, nano::block const & block_a)
{
assert (!store.block_exists (transaction_a, block_a.type (), block_a.hash ()));
auto root (block_a.root ());
assert (store.block_exists (transaction_a, root) || store.account_exists (transaction_a, root));
auto result (store.block_get (transaction_a, store.block_successor (transaction_a, root)));
if (result == nullptr)
{
nano::account_info info;
auto error (store.account_get (transaction_a, root, info));
assert (!error);
result = store.block_get (transaction_a, info.open_block);
assert (result != nullptr);
}
return result;
}
bool nano::ledger::block_confirmed (nano::transaction const & transaction_a, nano::block_hash const & hash_a) const
{
auto confirmed (false);
auto block_height (store.block_account_height (transaction_a, hash_a));
if (block_height) // 0 indicates that the block doesn't exist
{
auto account_l (account (transaction_a, hash_a));
nano::account_info account_info;
release_assert (!store.account_get (transaction_a, account_l, account_info));
confirmed = (account_info.confirmation_height >= block_height);
}
return confirmed;
}
namespace nano
{
std::unique_ptr<seq_con_info_component> collect_seq_con_info (ledger & ledger, const std::string & name)
{
auto composite = std::make_unique<seq_con_info_composite> (name);
auto count = ledger.bootstrap_weights.size ();
auto sizeof_element = sizeof (decltype (ledger.bootstrap_weights)::value_type);
composite->add_component (std::make_unique<seq_con_info_leaf> (seq_con_info{ "bootstrap_weights", count, sizeof_element }));
return composite;
}
}