From 8df03622e98e49f4f5f98f3cb9af66679371faa2 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Mon, 23 Mar 2020 19:05:17 +0000 Subject: [PATCH] Validate work difficulty during ledger processing (#2667) Block work is now validated during ledger processing according to their epoch version and some block details. The following article details the reasoning for this change: [Development Update: V21 PoW Difficulty Increases](https://medium.com/nanocurrency/development-update-v21-pow-difficulty-increases-362b5d052c8e). When an account is upgraded with epoch 2, blocks must meet the new thresholds or they are discarded during ledger processing. Before ledger processing, an initial work validation is performed on node entry. --- nano/core_test/ledger.cpp | 87 +++++++++++++ nano/lib/blocks.cpp | 10 ++ nano/lib/blocks.hpp | 1 + nano/node/blockprocessor.cpp | 27 ++-- nano/secure/common.hpp | 3 +- nano/secure/ledger.cpp | 234 ++++++++++++++++++++--------------- 6 files changed, 248 insertions(+), 114 deletions(-) diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 3b8eb22c..6e234696 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -2969,3 +2969,90 @@ TEST (ledger, zero_rep) ASSERT_EQ (nano::genesis_amount, node1.ledger.cache.rep_weights.representation_get (nano::test_genesis_key.pub)); ASSERT_EQ (0, node1.ledger.cache.rep_weights.representation_get (0)); } + +TEST (ledger, work_validation) +{ + nano::logger_mt logger; + auto store = nano::make_store (logger, nano::unique_path ()); + ASSERT_TRUE (!store->init_error ()); + nano::stat stats; + nano::ledger ledger (*store, stats); + nano::genesis genesis; + store->initialize (store->tx_begin_write (), genesis, ledger.cache); + nano::work_pool pool (std::numeric_limits::max ()); + nano::block_builder builder; + auto gen = nano::test_genesis_key; + nano::keypair key; + + // With random work the block doesn't pass, then modifies the block with sufficient work and ensures a correct result + auto process_block = [&store, &ledger, &pool](nano::block & block_a, nano::block_details const details_a) { + EXPECT_EQ (nano::process_result::insufficient_work, ledger.process (store->tx_begin_write (), block_a).code); + block_a.block_work_set (*pool.generate (block_a.root (), nano::work_threshold (block_a.work_version (), details_a))); + EXPECT_EQ (nano::process_result::progress, ledger.process (store->tx_begin_write (), block_a).code); + }; + + std::error_code ec; + + auto send = *builder.send () + .previous (nano::genesis_hash) + .destination (gen.pub) + .balance (nano::genesis_amount - 1) + .sign (gen.prv, gen.pub) + .work (0) + .build (ec); + ASSERT_FALSE (ec); + + auto receive = *builder.receive () + .previous (send.hash ()) + .source (send.hash ()) + .sign (gen.prv, gen.pub) + .work (0) + .build (ec); + ASSERT_FALSE (ec); + + auto change = *builder.change () + .previous (receive.hash ()) + .representative (key.pub) + .sign (gen.prv, gen.pub) + .work (0) + .build (ec); + ASSERT_FALSE (ec); + + auto state = *builder.state () + .account (gen.pub) + .previous (change.hash ()) + .representative (gen.pub) + .balance (nano::genesis_amount - 1) + .link (key.pub) + .sign (gen.prv, gen.pub) + .work (0) + .build (ec); + ASSERT_FALSE (ec); + + auto open = *builder.open () + .account (key.pub) + .source (state.hash ()) + .representative (key.pub) + .sign (key.prv, key.pub) + .work (0) + .build (ec); + ASSERT_FALSE (ec); + + auto epoch = *builder.state () + .account (key.pub) + .previous (open.hash ()) + .balance (1) + .representative (key.pub) + .link (ledger.epoch_link (nano::epoch::epoch_1)) + .sign (gen.prv, gen.pub) + .work (0) + .build (ec); + ASSERT_FALSE (ec); + + process_block (send, {}); + process_block (receive, {}); + process_block (change, {}); + process_block (state, nano::block_details (nano::epoch::epoch_0, true, false, false)); + process_block (open, {}); + process_block (epoch, nano::block_details (nano::epoch::epoch_1, false, false, true)); +} diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index 7c0f2361..396bbb63 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -1707,6 +1707,16 @@ bool nano::block_details::deserialize (nano::stream & stream_a) return result; } +nano::block_sideband::block_sideband (nano::account const & account_a, nano::block_hash const & successor_a, nano::amount const & balance_a, uint64_t height_a, uint64_t timestamp_a, nano::block_details const & details_a) : +successor (successor_a), +account (account_a), +balance (balance_a), +height (height_a), +timestamp (timestamp_a), +details (details_a) +{ +} + nano::block_sideband::block_sideband (nano::account const & account_a, nano::block_hash const & successor_a, nano::amount const & balance_a, uint64_t height_a, uint64_t timestamp_a, nano::epoch epoch_a, bool is_send, bool is_receive, bool is_epoch) : successor (successor_a), account (account_a), diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index d0ee95c9..dab4c032 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -53,6 +53,7 @@ class block_sideband final { public: block_sideband () = default; + block_sideband (nano::account const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t, nano::block_details const &); block_sideband (nano::account const &, nano::block_hash const &, nano::amount const &, uint64_t, uint64_t, nano::epoch, bool is_send, bool is_receive, bool is_epoch); void serialize (nano::stream &, nano::block_type) const; bool deserialize (nano::stream &, nano::block_type); diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index ccc7d0da..18124dc0 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -86,20 +86,17 @@ void nano::block_processor::add (std::shared_ptr block_a, uint64_t void nano::block_processor::add (nano::unchecked_info const & info_a) { debug_assert (!nano::work_validate (*info_a.block)); - if (info_a.block->difficulty () >= nano::work_threshold (info_a.block->work_version ())) + if (info_a.verified == nano::signature_verification::unknown && (info_a.block->type () == nano::block_type::state || info_a.block->type () == nano::block_type::open || !info_a.account.is_zero ())) + { + state_block_signature_verification.add (info_a); + } + else { - if (info_a.verified == nano::signature_verification::unknown && (info_a.block->type () == nano::block_type::state || info_a.block->type () == nano::block_type::open || !info_a.account.is_zero ())) { - state_block_signature_verification.add (info_a); - } - else - { - { - nano::lock_guard guard (mutex); - blocks.push_back (info_a); - } - condition.notify_all (); + nano::lock_guard guard (mutex); + blocks.push_back (info_a); } + condition.notify_all (); } } @@ -448,6 +445,14 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction } break; } + case nano::process_result::insufficient_work: + { + if (node.config.logging.ledger_logging ()) + { + node.logger.try_log (boost::str (boost::format ("Insufficient work for %1% : %2 (difficulty %3)") % hash.to_string () % info_a.block->block_work () % info_a.block->difficulty ())); + } + break; + } } return result; } diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 1781ba20..2f2403b7 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -318,7 +318,8 @@ enum class process_result opened_burn_account, // The impossible happened, someone found the private key associated with the public key '0'. balance_mismatch, // Balance and amount delta don't match representative_mismatch, // Representative is changed when it is not allowed - block_position // This block cannot follow the previous block + block_position, // This block cannot follow the previous block + insufficient_work // Insufficient work for this block, even though it passed the minimal validation }; class process_return final { diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 1e552b5a..2539a7a8 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -333,37 +333,42 @@ void ledger_processor::state_block_impl (nano::state_block & block_a) } if (result.code == nano::process_result::progress) { - ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::state_block); - block_a.sideband_set (nano::block_sideband (block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), epoch, is_send, is_receive, false)); - ledger.store.block_put (transaction, hash, block_a); + nano::block_details block_details (epoch, is_send, is_receive, false); + result.code = block_a.difficulty () >= nano::work_threshold (block_a.work_version (), block_details) ? nano::process_result::progress : nano::process_result::insufficient_work; // Does this block have sufficient work? (Malformed) + if (result.code == nano::process_result::progress) + { + ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::state_block); + block_a.sideband_set (nano::block_sideband (block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), block_details)); + ledger.store.block_put (transaction, hash, block_a); - if (!info.head.is_zero ()) - { - // Move existing representation - ledger.cache.rep_weights.representation_add (info.representative, 0 - info.balance.number ()); - } - // Add in amount delta - ledger.cache.rep_weights.representation_add (block_a.representative (), block_a.hashables.balance.number ()); + if (!info.head.is_zero ()) + { + // Move existing representation + ledger.cache.rep_weights.representation_add (info.representative, 0 - info.balance.number ()); + } + // Add in amount delta + ledger.cache.rep_weights.representation_add (block_a.representative (), 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)); - } + 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)); + } - nano::account_info new_info (hash, block_a.representative (), info.open_block.is_zero () ? hash : info.open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info.block_count + 1, epoch); - ledger.change_latest (transaction, block_a.hashables.account, info, new_info); - if (!ledger.store.frontier_get (transaction, info.head).is_zero ()) - { - ledger.store.frontier_del (transaction, info.head); + nano::account_info new_info (hash, block_a.representative (), info.open_block.is_zero () ? hash : info.open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info.block_count + 1, epoch); + ledger.change_latest (transaction, block_a.hashables.account, info, new_info); + 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; } - // 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; } } } @@ -419,16 +424,21 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a) 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; - block_a.sideband_set (nano::block_sideband (block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), epoch, false, false, true)); - ledger.store.block_put (transaction, hash, block_a); - nano::account_info new_info (hash, block_a.representative (), info.open_block.is_zero () ? hash : info.open_block, info.balance, nano::seconds_since_epoch (), info.block_count + 1, epoch); - ledger.change_latest (transaction, block_a.hashables.account, info, new_info); - if (!ledger.store.frontier_get (transaction, info.head).is_zero ()) + nano::block_details block_details (epoch, false, false, true); + result.code = block_a.difficulty () >= nano::work_threshold (block_a.work_version (), block_details) ? nano::process_result::progress : nano::process_result::insufficient_work; // Does this block have sufficient work? (Malformed) + if (result.code == nano::process_result::progress) { - ledger.store.frontier_del (transaction, info.head); + ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::epoch_block); + result.account = block_a.hashables.account; + result.amount = 0; + block_a.sideband_set (nano::block_sideband (block_a.hashables.account /* unused */, 0, 0 /* unused */, info.block_count + 1, nano::seconds_since_epoch (), block_details)); + ledger.store.block_put (transaction, hash, block_a); + nano::account_info new_info (hash, block_a.representative (), info.open_block.is_zero () ? hash : info.open_block, info.balance, nano::seconds_since_epoch (), info.block_count + 1, epoch); + ledger.change_latest (transaction, block_a.hashables.account, info, new_info); + if (!ledger.store.frontier_get (transaction, info.head).is_zero ()) + { + ledger.store.frontier_del (transaction, info.head); + } } } } @@ -468,20 +478,25 @@ void ledger_processor::change_block (nano::change_block & block_a) } if (result.code == nano::process_result::progress) { - debug_assert (!validate_message (account, hash, block_a.signature)); - result.verified = nano::signature_verification::valid; - block_a.sideband_set (nano::block_sideband (account, 0, info.balance, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); - ledger.store.block_put (transaction, hash, block_a); - auto balance (ledger.balance (transaction, block_a.hashables.previous)); - ledger.cache.rep_weights.representation_add (block_a.representative (), balance); - ledger.cache.rep_weights.representation_add (info.representative, 0 - balance); - nano::account_info new_info (hash, block_a.representative (), info.open_block, info.balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); - ledger.change_latest (transaction, account, info, new_info); - 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); + nano::block_details block_details (nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); + result.code = block_a.difficulty () >= nano::work_threshold (block_a.work_version (), block_details) ? nano::process_result::progress : nano::process_result::insufficient_work; // Does this block have sufficient work? (Malformed) + if (result.code == nano::process_result::progress) + { + debug_assert (!validate_message (account, hash, block_a.signature)); + result.verified = nano::signature_verification::valid; + block_a.sideband_set (nano::block_sideband (account, 0, info.balance, info.block_count + 1, nano::seconds_since_epoch (), block_details)); + ledger.store.block_put (transaction, hash, block_a); + auto balance (ledger.balance (transaction, block_a.hashables.previous)); + ledger.cache.rep_weights.representation_add (block_a.representative (), balance); + ledger.cache.rep_weights.representation_add (info.representative, 0 - balance); + nano::account_info new_info (hash, block_a.representative (), info.open_block, info.balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); + ledger.change_latest (transaction, account, info, new_info); + 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); + } } } } @@ -514,29 +529,34 @@ void ledger_processor::send_block (nano::send_block & block_a) } if (result.code == nano::process_result::progress) { - debug_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)); - (void)latest_error; - debug_assert (!latest_error); - debug_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) + nano::block_details block_details (nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); + result.code = block_a.difficulty () >= nano::work_threshold (block_a.work_version (), block_details) ? nano::process_result::progress : nano::process_result::insufficient_work; // Does this block have sufficient work? (Malformed) if (result.code == nano::process_result::progress) { - auto amount (info.balance.number () - block_a.hashables.balance.number ()); - ledger.cache.rep_weights.representation_add (info.representative, 0 - amount); - block_a.sideband_set (nano::block_sideband (account, 0, block_a.hashables.balance /* unused */, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); - ledger.store.block_put (transaction, hash, block_a); - nano::account_info new_info (hash, info.representative, info.open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); - ledger.change_latest (transaction, account, info, new_info); - 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); + debug_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)); + (void)latest_error; + debug_assert (!latest_error); + debug_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.cache.rep_weights.representation_add (info.representative, 0 - amount); + block_a.sideband_set (nano::block_sideband (account, 0, block_a.hashables.balance /* unused */, info.block_count + 1, nano::seconds_since_epoch (), block_details)); + ledger.store.block_put (transaction, hash, block_a); + nano::account_info new_info (hash, info.representative, info.open_block, block_a.hashables.balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); + ledger.change_latest (transaction, account, info, new_info); + 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); + } } } } @@ -588,22 +608,27 @@ void ledger_processor::receive_block (nano::receive_block & block_a) 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)); - (void)error; - debug_assert (!error); - ledger.store.pending_del (transaction, key); - block_a.sideband_set (nano::block_sideband (account, 0, new_balance, info.block_count + 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); - ledger.store.block_put (transaction, hash, block_a); - nano::account_info new_info (hash, info.representative, info.open_block, new_balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); - ledger.change_latest (transaction, account, info, new_info); - ledger.cache.rep_weights.representation_add (info.representative, 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); + nano::block_details block_details (nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); + result.code = block_a.difficulty () >= nano::work_threshold (block_a.work_version (), block_details) ? nano::process_result::progress : nano::process_result::insufficient_work; // Does this block have sufficient work? (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)); + (void)error; + debug_assert (!error); + ledger.store.pending_del (transaction, key); + block_a.sideband_set (nano::block_sideband (account, 0, new_balance, info.block_count + 1, nano::seconds_since_epoch (), block_details)); + ledger.store.block_put (transaction, hash, block_a); + nano::account_info new_info (hash, info.representative, info.open_block, new_balance, nano::seconds_since_epoch (), info.block_count + 1, nano::epoch::epoch_0); + ledger.change_latest (transaction, account, info, new_info); + ledger.cache.rep_weights.representation_add (info.representative, 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); + } } } } @@ -653,20 +678,25 @@ void ledger_processor::open_block (nano::open_block & block_a) 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)); - (void)error; - debug_assert (!error); - ledger.store.pending_del (transaction, key); - block_a.sideband_set (nano::block_sideband (block_a.hashables.account, 0, pending.amount, 1, nano::seconds_since_epoch (), nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */)); - ledger.store.block_put (transaction, hash, block_a); - nano::account_info new_info (hash, block_a.representative (), hash, pending.amount.number (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0); - ledger.change_latest (transaction, block_a.hashables.account, info, new_info); - ledger.cache.rep_weights.representation_add (block_a.representative (), 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); + nano::block_details block_details (nano::epoch::epoch_0, false /* unused */, false /* unused */, false /* unused */); + result.code = block_a.difficulty () >= nano::work_threshold (block_a.work_version (), block_details) ? nano::process_result::progress : nano::process_result::insufficient_work; // Does this block have sufficient work? (Malformed) + if (result.code == nano::process_result::progress) + { + nano::account_info source_info; + auto error (ledger.store.account_get (transaction, pending.source, source_info)); + (void)error; + debug_assert (!error); + ledger.store.pending_del (transaction, key); + block_a.sideband_set (nano::block_sideband (block_a.hashables.account, 0, pending.amount, 1, nano::seconds_since_epoch (), block_details)); + ledger.store.block_put (transaction, hash, block_a); + nano::account_info new_info (hash, block_a.representative (), hash, pending.amount.number (), nano::seconds_since_epoch (), 1, nano::epoch::epoch_0); + ledger.change_latest (transaction, block_a.hashables.account, info, new_info); + ledger.cache.rep_weights.representation_add (block_a.representative (), 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); + } } } } @@ -753,7 +783,7 @@ nano::uint128_t nano::ledger::account_pending (nano::transaction const & transac nano::process_return nano::ledger::process (nano::write_transaction const & transaction_a, nano::block & block_a, nano::signature_verification verification) { - debug_assert (!nano::work_validate (block_a)); + debug_assert (network_params.network.is_test_network () || !nano::work_validate (block_a)); ledger_processor processor (*this, transaction_a, verification); block_a.visit (processor); if (processor.result.code == nano::process_result::progress)