From df47579c0de6f9bdc758684dd86f1ef827759891 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Fri, 10 Apr 2020 19:31:24 +0100 Subject: [PATCH] Difficulty updates for elections with multiple blocks (#2710) * Difficulty updates for elections with multiple blocks `active_transactions::publish` now performs a difficulty update for a new block, ensuring elections with forks have equivalent priority accross the network as long as they see the same blocks. `election->publish` is no longer called from `update_difficulty(_impl)` as it has no effect. Difficulty normalization (https://github.com/nanocurrency/nano-node/pull/2691) made this more difficult as forks don't have a loaded sideband and we wish to avoid extra disk reads. https://github.com/clemahieu suggested storing the previous block's epoch and balance in order to infer we can infer block details, implemented in this PR. In elections for live blocks, the only case where it is not possible to correctly infer the threshold is during an epoch upgrade, for blocks performing an upgrade (epoch will mismatch from root, and the block itself might be an epoch block). This only affects prioritization and the window is short. In the future, this can be disambiguated by including some flags in the block itself. New tests added ensuring correct difficulty updates for old blocks and forks. * Check block previous zero first to avoid tx_begin_read (Serg review) * Assign previous_balance for all blocks (Serg review) * Use const ref (Wes review) --- nano/core_test/active_transactions.cpp | 95 ++++++++++++++++++++++++++ nano/core_test/conflicts.cpp | 1 + nano/core_test/ledger.cpp | 64 +++++++++-------- nano/node/active_transactions.cpp | 69 ++++++++++++------- nano/node/active_transactions.hpp | 52 +++++++------- nano/node/blockprocessor.cpp | 8 +-- nano/node/blockprocessor.hpp | 2 +- nano/node/node.cpp | 2 +- nano/node/wallet.cpp | 2 +- nano/secure/common.hpp | 1 + nano/secure/ledger.cpp | 10 ++- 11 files changed, 222 insertions(+), 84 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 177bde557f..d15e215659 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -768,6 +768,10 @@ TEST (active_transactions, insertion_prioritization) // Sort by difficulty, descending std::vector> blocks{ send1, send2, send3, send4, send5, send6, send7 }; + for (auto const & block : blocks) + { + ASSERT_EQ (nano::process_result::progress, node.process (*block).code); + } std::sort (blocks.begin (), blocks.end (), [](auto const & blockl, auto const & blockr) { return blockl->difficulty () > blockr->difficulty (); }); auto update_active_multiplier = [&node] { @@ -904,3 +908,94 @@ TEST (active_transactions, vote_generator_session) } } } + +TEST (active_transactions, election_difficulty_update_old) +{ + nano::system system; + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node = *system.add_node (node_flags); + nano::genesis genesis; + nano::keypair key; + auto send1 (std::make_shared (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - 10 * nano::xrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + auto send1_copy (std::make_shared (*send1)); + node.process_active (send1); + node.block_processor.flush (); + auto root (send1->qualified_root ()); + ASSERT_EQ (1, node.active.size ()); + auto multiplier = node.active.roots.begin ()->multiplier; + { + nano::lock_guard guard (node.active.mutex); + ASSERT_EQ (node.active.normalized_multiplier (*send1), multiplier); + } + // Should not update with a lower difficulty + send1_copy->block_work_set (0); + ASSERT_EQ (nano::process_result::old, node.process (*send1_copy).code); + ASSERT_FALSE (send1_copy->has_sideband ()); + node.process_active (send1); + node.block_processor.flush (); + ASSERT_EQ (1, node.active.size ()); + ASSERT_EQ (node.active.roots.begin ()->multiplier, multiplier); + // Update work, even without a sideband it should find the block in the election and update the election multiplier + ASSERT_TRUE (node.work_generate_blocking (*send1_copy, send1->difficulty () + 1).is_initialized ()); + node.process_active (send1_copy); + node.block_processor.flush (); + ASSERT_EQ (1, node.active.size ()); + ASSERT_GT (node.active.roots.begin ()->multiplier, multiplier); +} + +TEST (active_transactions, election_difficulty_update_fork) +{ + nano::system system; + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node = *system.add_node (node_flags); + + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_1)); + auto epoch2 = system.upgrade_genesis_epoch (node, nano::epoch::epoch_2); + ASSERT_NE (nullptr, epoch2); + nano::keypair key; + auto send1 (std::make_shared (nano::test_genesis_key.pub, epoch2->hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (epoch2->hash ()))); + auto open1 (std::make_shared (key.pub, 0, key.pub, nano::Gxrb_ratio, send1->hash (), key.prv, key.pub, *system.work.generate (key.pub))); + auto send2 (std::make_shared (nano::test_genesis_key.pub, send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 2 * nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ()))); + ASSERT_EQ (nano::process_result::progress, node.process (*send1).code); + ASSERT_EQ (nano::process_result::progress, node.process (*open1).code); + ASSERT_EQ (nano::process_result::progress, node.process (*send2).code); + + // Verify an election with multiple blocks is correctly updated on arrival of another block + // Each subsequent block has difficulty at least higher than the previous one + auto fork_change (std::make_shared (key.pub, open1->hash (), nano::test_genesis_key.pub, nano::Gxrb_ratio, 0, key.prv, key.pub, *system.work.generate (open1->hash ()))); + auto fork_send (std::make_shared (key.pub, open1->hash (), key.pub, 0, key.pub, key.prv, key.pub, *system.work.generate (open1->hash (), fork_change->difficulty ()))); + auto fork_receive (std::make_shared (key.pub, open1->hash (), key.pub, 2 * nano::Gxrb_ratio, send2->hash (), key.prv, key.pub, *system.work.generate (open1->hash (), fork_send->difficulty ()))); + ASSERT_GT (fork_send->difficulty (), fork_change->difficulty ()); + ASSERT_GT (fork_receive->difficulty (), fork_send->difficulty ()); + + node.process_active (fork_change); + node.block_processor.flush (); + ASSERT_EQ (1, node.active.size ()); + auto multiplier_change = node.active.roots.begin ()->multiplier; + node.process_active (fork_send); + node.block_processor.flush (); + ASSERT_EQ (1, node.active.size ()); + auto multiplier_send = node.active.roots.begin ()->multiplier; + node.process_active (fork_receive); + node.block_processor.flush (); + ASSERT_EQ (1, node.active.size ()); + auto multiplier_receive = node.active.roots.begin ()->multiplier; + + ASSERT_GT (multiplier_send, multiplier_change); + ASSERT_GT (multiplier_receive, multiplier_send); + + EXPECT_FALSE (fork_receive->has_sideband ()); + auto threshold = nano::work_threshold (fork_receive->work_version (), nano::block_details (nano::epoch::epoch_2, false, true, false)); + auto denormalized = nano::denormalized_multiplier (multiplier_receive, threshold); + ASSERT_NEAR (nano::difficulty::to_multiplier (fork_receive->difficulty (), threshold), denormalized, 1e-10); + + // Ensure a fork with updated difficulty will also update the election difficulty + fork_receive->block_work_set (*system.work.generate (fork_receive->root (), fork_receive->difficulty () + 1)); + node.process_active (fork_receive); + node.block_processor.flush (); + ASSERT_EQ (1, node.active.size ()); + auto multiplier_receive_updated = node.active.roots.begin ()->multiplier; + ASSERT_GT (multiplier_receive_updated, multiplier_receive); +} diff --git a/nano/core_test/conflicts.cpp b/nano/core_test/conflicts.cpp index a55b010974..79fdfc4292 100644 --- a/nano/core_test/conflicts.cpp +++ b/nano/core_test/conflicts.cpp @@ -39,6 +39,7 @@ TEST (conflicts, add_existing) node1.active.insert (send1); nano::keypair key2; auto send2 (std::make_shared (genesis.hash (), key2.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); + send2->sideband_set ({}); auto election1 = node1.active.insert (send2); ASSERT_EQ (1, node1.active.size ()); auto vote1 (std::make_shared (key2.pub, key2.prv, 0, send2)); diff --git a/nano/core_test/ledger.cpp b/nano/core_test/ledger.cpp index 5aadcfaca3..e57aa97a7d 100644 --- a/nano/core_test/ledger.cpp +++ b/nano/core_test/ledger.cpp @@ -928,32 +928,33 @@ TEST (votes, add_old_different_account) node1.work_generate_blocking (*send1); auto send2 (std::make_shared (send1->hash (), key1.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); node1.work_generate_blocking (*send2); - auto transaction (node1.store.tx_begin_write ()); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send2).code); - auto election1 = node1.active.insert (send1); - auto election2 = node1.active.insert (send2); - ASSERT_EQ (1, election1.election->last_votes_size ()); - ASSERT_EQ (1, election2.election->last_votes_size ()); + ASSERT_EQ (nano::process_result::progress, node1.process_local (send1).code); + ASSERT_EQ (nano::process_result::progress, node1.process_local (send2).code); + auto election1 = node1.active.election (send1->qualified_root ()); + ASSERT_NE (nullptr, election1); + auto election2 = node1.active.election (send2->qualified_root ()); + ASSERT_NE (nullptr, election2); + ASSERT_EQ (1, election1->last_votes_size ()); + ASSERT_EQ (1, election2->last_votes_size ()); auto vote1 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 2, send1)); auto channel (std::make_shared (node1.network.udp_channels, node1.network.endpoint (), node1.network_params.protocol.protocol_version)); auto vote_result1 (node1.vote_processor.vote_blocking (vote1, channel)); ASSERT_EQ (nano::vote_code::vote, vote_result1); - ASSERT_EQ (2, election1.election->last_votes_size ()); - ASSERT_EQ (1, election2.election->last_votes_size ()); + ASSERT_EQ (2, election1->last_votes_size ()); + ASSERT_EQ (1, election2->last_votes_size ()); auto vote2 (std::make_shared (nano::test_genesis_key.pub, nano::test_genesis_key.prv, 1, send2)); auto vote_result2 (node1.vote_processor.vote_blocking (vote2, channel)); ASSERT_EQ (nano::vote_code::vote, vote_result2); - ASSERT_EQ (2, election1.election->last_votes_size ()); - ASSERT_EQ (2, election2.election->last_votes_size ()); + ASSERT_EQ (2, election1->last_votes_size ()); + ASSERT_EQ (2, election2->last_votes_size ()); nano::unique_lock lock (node1.active.mutex); - ASSERT_NE (election1.election->last_votes.end (), election1.election->last_votes.find (nano::test_genesis_key.pub)); - ASSERT_NE (election2.election->last_votes.end (), election2.election->last_votes.find (nano::test_genesis_key.pub)); - ASSERT_EQ (send1->hash (), election1.election->last_votes[nano::test_genesis_key.pub].hash); - ASSERT_EQ (send2->hash (), election2.election->last_votes[nano::test_genesis_key.pub].hash); - auto winner1 (*election1.election->tally ().begin ()); + ASSERT_NE (election1->last_votes.end (), election1->last_votes.find (nano::test_genesis_key.pub)); + ASSERT_NE (election2->last_votes.end (), election2->last_votes.find (nano::test_genesis_key.pub)); + ASSERT_EQ (send1->hash (), election1->last_votes[nano::test_genesis_key.pub].hash); + ASSERT_EQ (send2->hash (), election2->last_votes[nano::test_genesis_key.pub].hash); + auto winner1 (*election1->tally ().begin ()); ASSERT_EQ (*send1, *winner1.second); - auto winner2 (*election2.election->tally ().begin ()); + auto winner2 (*election2->tally ().begin ()); ASSERT_EQ (*send2, *winner2.second); } @@ -2659,20 +2660,23 @@ TEST (ledger, block_hash_account_conflict) node1.work_generate_blocking (*receive1); node1.work_generate_blocking (*send2); node1.work_generate_blocking (*open_epoch1); - auto transaction (node1.store.tx_begin_write ()); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send1).code); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *receive1).code); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *send2).code); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, *open_epoch1).code); - auto election1 = node1.active.insert (send1); - auto election2 = node1.active.insert (receive1); - auto election3 = node1.active.insert (send2); - auto election4 = node1.active.insert (open_epoch1); + ASSERT_EQ (nano::process_result::progress, node1.process_local (send1).code); + ASSERT_EQ (nano::process_result::progress, node1.process_local (receive1).code); + ASSERT_EQ (nano::process_result::progress, node1.process_local (send2).code); + ASSERT_EQ (nano::process_result::progress, node1.process_local (open_epoch1).code); + auto election1 = node1.active.election (send1->qualified_root ()); + ASSERT_NE (nullptr, election1); + auto election2 = node1.active.election (receive1->qualified_root ()); + ASSERT_NE (nullptr, election2); + auto election3 = node1.active.election (send2->qualified_root ()); + ASSERT_NE (nullptr, election3); + auto election4 = node1.active.election (open_epoch1->qualified_root ()); + ASSERT_NE (nullptr, election4); nano::lock_guard lock (node1.active.mutex); - auto winner1 (*election1.election->tally ().begin ()); - auto winner2 (*election2.election->tally ().begin ()); - auto winner3 (*election3.election->tally ().begin ()); - auto winner4 (*election4.election->tally ().begin ()); + auto winner1 (*election1->tally ().begin ()); + auto winner2 (*election2->tally ().begin ()); + auto winner3 (*election3->tally ().begin ()); + auto winner4 (*election4->tally ().begin ()); ASSERT_EQ (*send1, *winner1.second); ASSERT_EQ (*receive1, *winner2.second); ASSERT_EQ (*send2, *winner3.second); diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 41d727c157..a1d8f27186 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -493,8 +493,9 @@ void nano::active_transactions::stop () roots.clear (); } -nano::election_insertion_result nano::active_transactions::insert_impl (std::shared_ptr block_a, std::function)> const & confirmation_action_a) +nano::election_insertion_result nano::active_transactions::insert_impl (std::shared_ptr const & block_a, boost::optional const & previous_balance_a, std::function)> const & confirmation_action_a) { + debug_assert (block_a->has_sideband ()); nano::election_insertion_result result; if (!stopped) { @@ -507,10 +508,14 @@ nano::election_insertion_result nano::active_transactions::insert_impl (std::sha result.inserted = true; auto hash (block_a->hash ()); auto difficulty (block_a->difficulty ()); + auto epoch (block_a->sideband ().details.epoch); + auto previous_balance = block_a->previous ().is_zero () ? 0 : previous_balance_a.value_or_eval ([& node = node, &block_a] { + return node.ledger.balance (node.store.tx_begin_read (), block_a->previous ()); + }); double multiplier (normalized_multiplier (*block_a)); bool prioritized = roots.size () < prioritized_cutoff || multiplier > last_prioritized_multiplier.value_or (0); result.election = nano::make_shared (node, block_a, confirmation_action_a, prioritized); - roots.get ().emplace (nano::conflict_info{ root, multiplier, multiplier, result.election }); + roots.get ().emplace (nano::active_transactions::conflict_info{ root, multiplier, multiplier, result.election, epoch, previous_balance }); blocks.emplace (hash, result.election); add_adjust_difficulty (hash); result.election->insert_inactive_votes_cache (hash); @@ -524,10 +529,10 @@ nano::election_insertion_result nano::active_transactions::insert_impl (std::sha return result; } -nano::election_insertion_result nano::active_transactions::insert (std::shared_ptr block_a, std::function)> const & confirmation_action_a) +nano::election_insertion_result nano::active_transactions::insert (std::shared_ptr const & block_a, boost::optional const & previous_balance_a, std::function)> const & confirmation_action_a) { nano::lock_guard lock (mutex); - return insert_impl (block_a, confirmation_action_a); + return insert_impl (block_a, previous_balance_a, confirmation_action_a); } // Validate a vote and apply it to the current election if one exists @@ -628,30 +633,35 @@ std::shared_ptr nano::active_transactions::election (nano::quali return result; } -void nano::active_transactions::update_difficulty (std::shared_ptr block_a) +void nano::active_transactions::update_difficulty (nano::block const & block_a) { nano::unique_lock lock (mutex); - auto existing_election (roots.get ().find (block_a->qualified_root ())); + auto existing_election (roots.get ().find (block_a.qualified_root ())); if (existing_election != roots.get ().end ()) { - double multiplier (normalized_multiplier (*block_a, existing_election->election->blocks)); - if (multiplier > existing_election->multiplier) + update_difficulty_impl (existing_election, block_a); + } +} + +void nano::active_transactions::update_difficulty_impl (nano::active_transactions::roots_iterator const & root_it_a, nano::block const & block_a) +{ + double multiplier (normalized_multiplier (block_a, root_it_a)); + if (multiplier > root_it_a->multiplier) + { + if (node.config.logging.active_update_logging ()) { - if (node.config.logging.active_update_logging ()) - { - node.logger.try_log (boost::str (boost::format ("Block %1% was updated from multiplier %2% to %3%") % block_a->hash ().to_string () % existing_election->multiplier % multiplier)); - } - roots.get ().modify (existing_election, [multiplier](nano::conflict_info & info_a) { - info_a.multiplier = multiplier; - }); - existing_election->election->publish (block_a); - add_adjust_difficulty (block_a->hash ()); + node.logger.try_log (boost::str (boost::format ("Block %1% was updated from multiplier %2% to %3%") % block_a.hash ().to_string () % root_it_a->multiplier % multiplier)); } + roots.get ().modify (root_it_a, [multiplier](nano::active_transactions::conflict_info & info_a) { + info_a.multiplier = multiplier; + }); + add_adjust_difficulty (block_a.hash ()); } } -double nano::active_transactions::normalized_multiplier (nano::block const & block_a, std::unordered_map> const & blocks_a) +double nano::active_transactions::normalized_multiplier (nano::block const & block_a, boost::optional const & root_it_a) const { + debug_assert (!mutex.try_lock ()); auto difficulty (block_a.difficulty ()); uint64_t threshold (0); bool sideband_not_found (false); @@ -659,16 +669,23 @@ double nano::active_transactions::normalized_multiplier (nano::block const & blo { threshold = nano::work_threshold (block_a.work_version (), block_a.sideband ().details); } - else + else if (root_it_a.is_initialized ()) { - auto find_block (blocks_a.find (block_a.hash ())); - if (find_block != blocks_a.end () && find_block->second->has_sideband ()) + auto election (*root_it_a); + debug_assert (election != roots.end ()); + auto find_block (election->election->blocks.find (block_a.hash ())); + if (find_block != election->election->blocks.end () && find_block->second->has_sideband ()) { threshold = nano::work_threshold (block_a.work_version (), find_block->second->sideband ().details); } else { - threshold = nano::work_threshold_base (block_a.work_version ()); + // This can have incorrect results during an epoch upgrade, but it only affects prioritization + bool is_send = election->previous_balance > block_a.balance ().number (); + bool is_receive = election->previous_balance < block_a.balance ().number (); + nano::block_details details (election->epoch, is_send, is_receive, false); + + threshold = nano::work_threshold (block_a.work_version (), details); sideband_not_found = true; } } @@ -678,6 +695,11 @@ double nano::active_transactions::normalized_multiplier (nano::block const & blo { multiplier = nano::normalized_multiplier (multiplier, threshold); } + else + { + // Inferred threshold was incorrect + multiplier = 1; + } return multiplier; } @@ -763,7 +785,7 @@ void nano::active_transactions::update_adjusted_multiplier () double multiplier_a = avg_multiplier + (double)item.second * min_unit; if (existing_root->adjusted_multiplier != multiplier_a) { - roots.get ().modify (existing_root, [multiplier_a](nano::conflict_info & info_a) { + roots.get ().modify (existing_root, [multiplier_a](nano::active_transactions::conflict_info & info_a) { info_a.adjusted_multiplier = multiplier_a; }); } @@ -909,6 +931,7 @@ bool nano::active_transactions::publish (std::shared_ptr block_a) auto result (true); if (existing != roots.get ().end ()) { + update_difficulty_impl (existing, *block_a); auto election (existing->election); result = election->publish (block_a); if (!result) diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 9809d730ad..0c36302e15 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -31,15 +32,6 @@ class vote; class transaction; class confirmation_height_processor; -class conflict_info final -{ -public: - nano::qualified_root root; - double multiplier; - double adjusted_multiplier; - std::shared_ptr election; -}; - class cementable_account final { public: @@ -76,6 +68,17 @@ class election_insertion_result final // Holds all active blocks i.e. recently added blocks that need confirmation class active_transactions final { + class conflict_info final + { + public: + nano::qualified_root root; + double multiplier; + double adjusted_multiplier; + std::shared_ptr election; + nano::epoch epoch; + nano::uint128_t previous_balance; + }; + friend class nano::election; // clang-format off @@ -89,12 +92,24 @@ class active_transactions final // clang-format on public: + // clang-format off + using ordered_roots = boost::multi_index_container, + mi::member>, + mi::ordered_non_unique, + mi::member, + std::greater>>>; + // clang-format on + ordered_roots roots; + using roots_iterator = active_transactions::ordered_roots::index_iterator::type; + explicit active_transactions (nano::node &, nano::confirmation_height_processor &); ~active_transactions (); // Start an election for a block // Call action with confirmed block, may be different than what we started with // clang-format off - nano::election_insertion_result insert (std::shared_ptr, std::function)> const & = [](std::shared_ptr) {}); + nano::election_insertion_result insert (std::shared_ptr const &, boost::optional const & = boost::none, std::function)> const & = [](std::shared_ptr) {}); // clang-format on // Distinguishes replay votes, cannot be determined if the block is not in any election nano::vote_code vote (std::shared_ptr); @@ -102,8 +117,8 @@ class active_transactions final bool active (nano::block const &); bool active (nano::qualified_root const &); std::shared_ptr election (nano::qualified_root const &) const; - void update_difficulty (std::shared_ptr); - double normalized_multiplier (nano::block const &, std::unordered_map> const & = {}); + void update_difficulty (nano::block const &); + double normalized_multiplier (nano::block const &, boost::optional const & = boost::none) const; void add_adjust_difficulty (nano::block_hash const &); void update_adjusted_multiplier (); void update_active_multiplier (nano::unique_lock &); @@ -120,16 +135,6 @@ class active_transactions final boost::optional confirm_block (nano::transaction const &, std::shared_ptr); void block_cemented_callback (std::shared_ptr const & block_a); void block_already_cemented_callback (nano::block_hash const &); - // clang-format off - boost::multi_index_container, - mi::member>, - mi::ordered_non_unique, - mi::member, - std::greater>>> - roots; - // clang-format on boost::optional last_prioritized_multiplier{ boost::none }; std::unordered_map> blocks; std::deque list_recently_cemented (); @@ -159,8 +164,9 @@ class active_transactions final // Call action with confirmed block, may be different than what we started with // clang-format off - nano::election_insertion_result insert_impl (std::shared_ptr, std::function)> const & = [](std::shared_ptr) {}); + nano::election_insertion_result insert_impl (std::shared_ptr const &, boost::optional const & = boost::none, std::function)> const & = [](std::shared_ptr) {}); // clang-format on + void update_difficulty_impl (roots_iterator const &, nano::block const &); void request_loop (); void confirm_prioritized_frontiers (nano::transaction const & transaction_a); void request_confirm (nano::unique_lock &); diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index bf24af73cc..29915b88c5 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -264,7 +264,7 @@ void nano::block_processor::process_batch (nano::unique_lock & lock_ } } -void nano::block_processor::process_live (nano::block_hash const & hash_a, std::shared_ptr block_a, const bool watch_work_a, const bool initial_publish_a) +void nano::block_processor::process_live (nano::block_hash const & hash_a, std::shared_ptr block_a, nano::process_return const & process_return_a, const bool watch_work_a, const bool initial_publish_a) { // Add to work watcher to prevent dropping the election if (watch_work_a) @@ -273,7 +273,7 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std:: } // Start collecting quorum on block - auto election = node.active.insert (block_a); + auto election = node.active.insert (block_a, process_return_a.previous_balance.number ()); if (election.inserted) { election.election->transition_passive (); @@ -308,7 +308,7 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction } if (info_a.modified > nano::seconds_since_epoch () - 300 && node.block_arrival.recent (hash)) { - process_live (hash, info_a.block, watch_work_a, first_publish_a); + process_live (hash, info_a.block, result, watch_work_a, first_publish_a); } queue_unchecked (transaction_a, hash); break; @@ -368,7 +368,7 @@ nano::process_return nano::block_processor::process_one (nano::write_transaction node.logger.try_log (boost::str (boost::format ("Old for: %1%") % hash.to_string ())); } queue_unchecked (transaction_a, hash); - node.active.update_difficulty (info_a.block); + node.active.update_difficulty (*info_a.block); node.stats.inc (nano::stat::type::ledger, nano::stat::detail::old); break; } diff --git a/nano/node/blockprocessor.hpp b/nano/node/blockprocessor.hpp index ca6299ccf5..84c8dc8113 100644 --- a/nano/node/blockprocessor.hpp +++ b/nano/node/blockprocessor.hpp @@ -51,7 +51,7 @@ class block_processor final private: void queue_unchecked (nano::write_transaction const &, nano::block_hash const &); void process_batch (nano::unique_lock &); - void process_live (nano::block_hash const &, std::shared_ptr, const bool = false, const bool = false); + void process_live (nano::block_hash const &, std::shared_ptr, nano::process_return const &, const bool = false, const bool = false); void requeue_invalid (nano::block_hash const &, nano::unchecked_info const &); void process_verified_state_blocks (std::deque &, std::vector const &, std::vector const &, std::vector const &); bool stopped{ false }; diff --git a/nano/node/node.cpp b/nano/node/node.cpp index 91db5c05c7..36f30824af 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -545,7 +545,7 @@ void nano::node::process_fork (nano::transaction const & transaction_a, std::sha if (ledger_block && !block_confirmed_or_being_confirmed (transaction_a, ledger_block->hash ())) { std::weak_ptr this_w (shared_from_this ()); - auto election = active.insert (ledger_block, [this_w, root](std::shared_ptr) { + auto election = active.insert (ledger_block, boost::none, [this_w, root](std::shared_ptr) { if (auto this_l = this_w.lock ()) { auto attempt (this_l->bootstrap_initiator.current_attempt ()); diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 95fb8ce9d3..3413210e66 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -1482,7 +1482,7 @@ void nano::work_watcher::watching (nano::qualified_root const & root_a, std::sha if (!ec) { watcher_l->node.network.flood_block_initial (block); - watcher_l->node.active.update_difficulty (block); + watcher_l->node.active.update_difficulty (*block); watcher_l->update (root_a, block); } } diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index 3f5df09150..2b273d4616 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -330,6 +330,7 @@ class process_return final nano::account pending_account; boost::optional state_is_send; nano::signature_verification verified; + nano::amount previous_balance; }; enum class tally_result { diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 377215b1b7..01ad49e06c 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -280,8 +280,9 @@ void ledger_processor::state_block_impl (nano::state_block & block_a) auto account_error (ledger.store.account_get (transaction, block_a.hashables.account, info)); if (!account_error) { - epoch = info.epoch (); // Account already exists + epoch = info.epoch (); + result.previous_balance = info.balance; 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) { @@ -298,6 +299,7 @@ void ledger_processor::state_block_impl (nano::state_block & block_a) else { // Account does not yet exists + result.previous_balance = 0; 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) { @@ -399,6 +401,7 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a) if (!account_error) { // Account already exists + result.previous_balance = info.balance; 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) { @@ -411,6 +414,7 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a) } else { + result.previous_balance = 0; result.code = block_a.hashables.representative.is_zero () ? nano::process_result::progress : nano::process_result::representative_mismatch; // Non-exisitng account should have pending entries if (result.code == nano::process_result::progress) @@ -512,6 +516,7 @@ void ledger_processor::change_block (nano::change_block & block_a) ledger.store.frontier_put (transaction, hash, account); result.account = account; result.amount = 0; + result.previous_balance = info.balance; ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::change); } } @@ -572,6 +577,7 @@ void ledger_processor::send_block (nano::send_block & block_a) result.account = account; result.amount = amount; result.pending_account = block_a.hashables.destination; + result.previous_balance = info.balance; ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::send); } } @@ -644,6 +650,7 @@ void ledger_processor::receive_block (nano::receive_block & block_a) ledger.store.frontier_put (transaction, hash, account); result.account = account; result.amount = pending.amount; + result.previous_balance = info.balance; ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::receive); } } @@ -712,6 +719,7 @@ void ledger_processor::open_block (nano::open_block & block_a) ledger.store.frontier_put (transaction, hash, block_a.hashables.account); result.account = block_a.hashables.account; result.amount = pending.amount; + result.previous_balance = 0; ledger.stats.inc (nano::stat::type::ledger, nano::stat::detail::open); } }