diff --git a/rai/core_test/message.cpp b/rai/core_test/message.cpp index a847caf8..44f589dd 100644 --- a/rai/core_test/message.cpp +++ b/rai/core_test/message.cpp @@ -54,7 +54,7 @@ TEST (message, publish_serialization) ASSERT_EQ (rai::protocol_version, bytes[3]); ASSERT_EQ (rai::protocol_version_min, bytes[4]); ASSERT_EQ (static_cast (rai::message_type::publish), bytes[5]); - ASSERT_EQ (0x00, bytes[6]); // extensions + ASSERT_EQ (0x00, bytes[6]); // extensions ASSERT_EQ (static_cast (rai::block_type::send), bytes[7]); rai::bufferstream stream (bytes.data (), bytes.size ()); auto error (false); diff --git a/rai/core_test/work_pool.cpp b/rai/core_test/work_pool.cpp index 10968b55..dd9bbb46 100644 --- a/rai/core_test/work_pool.cpp +++ b/rai/core_test/work_pool.cpp @@ -8,16 +8,21 @@ TEST (work, one) rai::work_pool pool (std::numeric_limits::max (), nullptr); rai::change_block block (1, 1, rai::keypair ().prv, 3, 4); block.block_work_set (pool.generate (block.root ())); - ASSERT_FALSE (rai::work_validate (block)); + uint64_t difficulty; + ASSERT_FALSE (rai::work_validate (block, &difficulty)); + ASSERT_LT (rai::work_pool::publish_threshold, difficulty); } TEST (work, validate) { rai::work_pool pool (std::numeric_limits::max (), nullptr); rai::send_block send_block (1, 1, 2, rai::keypair ().prv, 4, 6); - ASSERT_TRUE (rai::work_validate (send_block)); + uint64_t difficulty; + ASSERT_TRUE (rai::work_validate (send_block, &difficulty)); + ASSERT_LT (difficulty, rai::work_pool::publish_threshold); send_block.block_work_set (pool.generate (send_block.root ())); - ASSERT_FALSE (rai::work_validate (send_block)); + ASSERT_FALSE (rai::work_validate (send_block, &difficulty)); + ASSERT_LT (rai::work_pool::publish_threshold, difficulty); } TEST (work, cancel) @@ -91,3 +96,28 @@ TEST (work, opencl_config) ASSERT_EQ (2, config2.device); ASSERT_EQ (3, config2.threads); } + +TEST (work, difficulty) +{ + rai::work_pool pool (std::numeric_limits::max (), nullptr); + rai::uint256_union root (1); + uint64_t difficulty1 (0xff00000000000000); + uint64_t difficulty2 (0xfff0000000000000); + uint64_t difficulty3 (0xffff000000000000); + uint64_t work1 (0); + uint64_t nonce1 (0); + do + { + work1 = pool.generate (root, difficulty1); + rai::work_validate (root, work1, &nonce1); + } while (nonce1 > difficulty2); + ASSERT_GT (nonce1, difficulty1); + uint64_t work2 (0); + uint64_t nonce2 (0); + do + { + work2 = pool.generate (root, difficulty2); + rai::work_validate (root, work2, &nonce2); + } while (nonce2 > difficulty3); + ASSERT_GT (nonce2, difficulty2); +} diff --git a/rai/lib/work.cpp b/rai/lib/work.cpp index 30fbe3d8..e703637c 100644 --- a/rai/lib/work.cpp +++ b/rai/lib/work.cpp @@ -5,14 +5,19 @@ #include -bool rai::work_validate (rai::block_hash const & root_a, uint64_t work_a) +bool rai::work_validate (rai::block_hash const & root_a, uint64_t work_a, uint64_t * difficulty_a) { - return rai::work_value (root_a, work_a) < rai::work_pool::publish_threshold; + auto value (rai::work_value (root_a, work_a)); + if (difficulty_a != nullptr) + { + *difficulty_a = value; + } + return value < rai::work_pool::publish_threshold; } -bool rai::work_validate (rai::block const & block_a) +bool rai::work_validate (rai::block const & block_a, uint64_t * difficulty_a) { - return work_validate (block_a.root (), block_a.block_work ()); + return work_validate (block_a.root (), block_a.block_work (), difficulty_a); } uint64_t rai::work_value (rai::block_hash const & root_a, uint64_t work_a) @@ -80,17 +85,17 @@ void rai::work_pool::loop (uint64_t thread) lock.unlock (); output = 0; // ticket != ticket_l indicates a different thread found a solution and we should stop - while (ticket == ticket_l && output < rai::work_pool::publish_threshold) + while (ticket == ticket_l && output < current_l.difficulty) { // Don't query main memory every iteration in order to reduce memory bus traffic // All operations here operate on stack memory // Count iterations down to zero since comparing to zero is easier than comparing to another number unsigned iteration (256); - while (iteration && output < rai::work_pool::publish_threshold) + while (iteration && output < current_l.difficulty) { work = rng.next (); blake2b_update (&hash, reinterpret_cast (&work), sizeof (work)); - blake2b_update (&hash, current_l.first.bytes.data (), current_l.first.bytes.size ()); + blake2b_update (&hash, current_l.item.bytes.data (), current_l.item.bytes.size ()); blake2b_final (&hash, reinterpret_cast (&output), sizeof (output)); blake2b_init (&hash, sizeof (output)); iteration -= 1; @@ -101,12 +106,12 @@ void rai::work_pool::loop (uint64_t thread) { // If the ticket matches what we started with, we're the ones that found the solution assert (output >= rai::work_pool::publish_threshold); - assert (work_value (current_l.first, work) == output); + assert (work_value (current_l.item, work) == output); // Signal other threads to stop their work next time they check ticket ++ticket; pending.pop_front (); lock.unlock (); - current_l.second (work); + current_l.callback (work); lock.lock (); } else @@ -127,16 +132,16 @@ void rai::work_pool::cancel (rai::uint256_union const & root_a) std::lock_guard lock (mutex); if (!pending.empty ()) { - if (pending.front ().first == root_a) + if (pending.front ().item == root_a) { ++ticket; } } pending.remove_if ([&root_a](decltype (pending)::value_type const & item_a) { bool result; - if (item_a.first == root_a) + if (item_a.item == root_a) { - item_a.second (boost::none); + item_a.callback (boost::none); result = true; } else @@ -154,7 +159,7 @@ void rai::work_pool::stop () producer_condition.notify_all (); } -void rai::work_pool::generate (rai::uint256_union const & root_a, std::function const &)> callback_a) +void rai::work_pool::generate (rai::uint256_union const & root_a, std::function const &)> callback_a, uint64_t difficulty_a) { assert (!root_a.is_zero ()); boost::optional result; @@ -165,7 +170,7 @@ void rai::work_pool::generate (rai::uint256_union const & root_a, std::function< if (!result) { std::lock_guard lock (mutex); - pending.push_back (std::make_pair (root_a, callback_a)); + pending.push_back ({ root_a, callback_a, difficulty_a }); producer_condition.notify_all (); } else @@ -174,12 +179,13 @@ void rai::work_pool::generate (rai::uint256_union const & root_a, std::function< } } -uint64_t rai::work_pool::generate (rai::uint256_union const & hash_a) +uint64_t rai::work_pool::generate (rai::uint256_union const & hash_a, uint64_t difficulty_a) { std::promise> work; generate (hash_a, [&work](boost::optional work_a) { work.set_value (work_a); - }); + }, + difficulty_a); auto result (work.get_future ().get ()); return result.value (); } diff --git a/rai/lib/work.hpp b/rai/lib/work.hpp index ea17f332..c81e4d8d 100644 --- a/rai/lib/work.hpp +++ b/rai/lib/work.hpp @@ -14,10 +14,17 @@ namespace rai { class block; -bool work_validate (rai::block_hash const &, uint64_t); -bool work_validate (rai::block const &); +bool work_validate (rai::block_hash const &, uint64_t, uint64_t * = nullptr); +bool work_validate (rai::block const &, uint64_t * = nullptr); uint64_t work_value (rai::block_hash const &, uint64_t); class opencl_work; +class work_item +{ +public: + rai::uint256_union item; + std::function const &)> callback; + uint64_t difficulty; +}; class work_pool { public: @@ -26,12 +33,12 @@ public: void loop (uint64_t); void stop (); void cancel (rai::uint256_union const &); - void generate (rai::uint256_union const &, std::function const &)>); - uint64_t generate (rai::uint256_union const &); + void generate (rai::uint256_union const &, std::function const &)>, uint64_t = rai::work_pool::publish_threshold); + uint64_t generate (rai::uint256_union const &, uint64_t = rai::work_pool::publish_threshold); std::atomic ticket; bool done; std::vector threads; - std::list const &)>>> pending; + std::list pending; std::mutex mutex; std::condition_variable producer_condition; std::function (rai::uint256_union const &)> opencl; diff --git a/rai/node/node.cpp b/rai/node/node.cpp index 6c6fc585..1d829e0d 100644 --- a/rai/node/node.cpp +++ b/rai/node/node.cpp @@ -1999,17 +1999,18 @@ public: class distributed_work : public std::enable_shared_from_this { public: - distributed_work (std::shared_ptr const & node_a, rai::block_hash const & root_a, std::function callback_a) : - distributed_work (1, node_a, root_a, callback_a) + distributed_work (std::shared_ptr const & node_a, rai::block_hash const & root_a, std::function callback_a, uint64_t difficulty_a) : + distributed_work (1, node_a, root_a, callback_a, difficulty_a) { assert (node_a != nullptr); } - distributed_work (unsigned int backoff_a, std::shared_ptr const & node_a, rai::block_hash const & root_a, std::function callback_a) : + distributed_work (unsigned int backoff_a, std::shared_ptr const & node_a, rai::block_hash const & root_a, std::function callback_a, uint64_t difficulty_a) : callback (callback_a), backoff (backoff_a), node (node_a), root (root_a), - need_resolve (node_a->config.work_peers) + need_resolve (node_a->config.work_peers), + difficulty (difficulty_a) { assert (node_a != nullptr); completed.clear (); @@ -2214,7 +2215,8 @@ public: auto callback_l (callback); node->work.generate (root, [callback_l](boost::optional const & work_a) { callback_l (work_a.value ()); - }); + }, + difficulty); } else { @@ -2227,10 +2229,10 @@ public: auto callback_l (callback); std::weak_ptr node_w (node); auto next_backoff (std::min (backoff * 2, (unsigned int)60 * 5)); - node->alarm.add (now + std::chrono::seconds (backoff), [node_w, root_l, callback_l, next_backoff] { + node->alarm.add (now + std::chrono::seconds (backoff), [ node_w, root_l, callback_l, next_backoff, difficulty = difficulty ] { if (auto node_l = node_w.lock ()) { - auto work_generation (std::make_shared (next_backoff, node_l, root_l, callback_l)); + auto work_generation (std::make_shared (next_backoff, node_l, root_l, callback_l, difficulty)); work_generation->start (); } }); @@ -2252,26 +2254,28 @@ public: std::map outstanding; std::vector> need_resolve; std::atomic_flag completed; + uint64_t difficulty; }; } -void rai::node::work_generate_blocking (rai::block & block_a) +void rai::node::work_generate_blocking (rai::block & block_a, uint64_t difficulty_a) { - block_a.block_work_set (work_generate_blocking (block_a.root ())); + block_a.block_work_set (work_generate_blocking (block_a.root (), difficulty_a)); } -void rai::node::work_generate (rai::uint256_union const & hash_a, std::function callback_a) +void rai::node::work_generate (rai::uint256_union const & hash_a, std::function callback_a, uint64_t difficulty_a) { - auto work_generation (std::make_shared (shared (), hash_a, callback_a)); + auto work_generation (std::make_shared (shared (), hash_a, callback_a, difficulty_a)); work_generation->start (); } -uint64_t rai::node::work_generate_blocking (rai::uint256_union const & hash_a) +uint64_t rai::node::work_generate_blocking (rai::uint256_union const & hash_a, uint64_t difficulty_a) { std::promise promise; work_generate (hash_a, [&promise](uint64_t work_a) { promise.set_value (work_a); - }); + }, + difficulty_a); return promise.get_future ().get (); } diff --git a/rai/node/node.hpp b/rai/node/node.hpp index 662d2be1..90b9860e 100644 --- a/rai/node/node.hpp +++ b/rai/node/node.hpp @@ -433,9 +433,9 @@ public: void backup_wallet (); void search_pending (); int price (rai::uint128_t const &, int); - void work_generate_blocking (rai::block &); - uint64_t work_generate_blocking (rai::uint256_union const &); - void work_generate (rai::uint256_union const &, std::function); + void work_generate_blocking (rai::block &, uint64_t = rai::work_pool::publish_threshold); + uint64_t work_generate_blocking (rai::uint256_union const &, uint64_t = rai::work_pool::publish_threshold); + void work_generate (rai::uint256_union const &, std::function, uint64_t = rai::work_pool::publish_threshold); void add_initial_peers (); void block_confirm (std::shared_ptr); void process_fork (rai::transaction const &, std::shared_ptr); diff --git a/rai/node/wallet.cpp b/rai/node/wallet.cpp index 8336798a..22ec9484 100644 --- a/rai/node/wallet.cpp +++ b/rai/node/wallet.cpp @@ -12,6 +12,8 @@ #include +uint64_t const rai::work_pool::publish_threshold; + rai::uint256_union rai::wallet_store::check (rai::transaction const & transaction_a) { rai::wallet_value value (entry_get_raw (transaction_a, rai::wallet_store::check_special));