diff --git a/rai/node/node.cpp b/rai/node/node.cpp index 1ad0eaddaa..1f118dccf2 100644 --- a/rai/node/node.cpp +++ b/rai/node/node.cpp @@ -1083,8 +1083,9 @@ void rai::block_processor::add (std::shared_ptr block_a, std::chrono { if (!rai::work_validate (block_a->root (), block_a->block_work ())) { + auto hash (block_a->hash ()); std::lock_guard lock (mutex); - if (blocks_hashes.find (block_a->hash ()) == blocks_hashes.end ()) + if (blocks_hashes.find (hash) == blocks_hashes.end () && rolled_back.get<1> ().find (hash) == rolled_back.get<1> ().end ()) { if (block_a->type () == rai::block_type::state && !node.ledger.is_epoch_link (block_a->link ())) { @@ -1094,8 +1095,9 @@ void rai::block_processor::add (std::shared_ptr block_a, std::chrono { blocks.push_back (std::make_pair (block_a, origination)); } - condition.notify_all (); + blocks_hashes.insert (hash); } + condition.notify_all (); } else { @@ -1278,6 +1280,20 @@ void rai::block_processor::process_receive_many (std::unique_lock & // Replace our block with the winner and roll back any dependent blocks BOOST_LOG (node.log) << boost::str (boost::format ("Rolling back %1% and replacing with %2%") % successor->hash ().to_string () % hash.to_string ()); node.ledger.rollback (transaction, successor->hash ()); + lock_a.lock (); + // Prevent rolled back blocks second insertion + auto inserted (rolled_back.insert (rai::rolled_hash{ std::chrono::steady_clock::now (), successor->hash () })); + if (inserted.second) + { + // Possible election winner change + rolled_back.get<1> ().erase (hash); + // Prevent overflow + if (rolled_back.size () > rolled_back_max) + { + rolled_back.erase (rolled_back.begin ()); + } + } + lock_a.unlock (); } } /* Forced state blocks are not validated in verify_state_blocks () function diff --git a/rai/node/node.hpp b/rai/node/node.hpp index 5edfbf5c6b..2367d56735 100644 --- a/rai/node/node.hpp +++ b/rai/node/node.hpp @@ -373,6 +373,12 @@ class rep_crawler std::mutex mutex; std::unordered_set active; }; +class rolled_hash +{ +public: + std::chrono::steady_clock::time_point time; + rai::block_hash hash; +}; // Processing blocks is a potentially long IO operation // This class isolates block insertion from other operations like servicing network operations class block_processor @@ -403,6 +409,13 @@ class block_processor std::deque, std::chrono::steady_clock::time_point>> state_blocks; std::unordered_set blocks_hashes; std::deque> forced; + boost::multi_index_container< + rai::rolled_hash, + boost::multi_index::indexed_by< + boost::multi_index::ordered_non_unique>, + boost::multi_index::hashed_unique>>> + rolled_back; + static size_t const rolled_back_max = 1024; std::condition_variable condition; rai::node & node; std::mutex mutex;