diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index 8bf8ac3a..868f832a 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -5,6 +5,8 @@ #include +#include + using namespace std::chrono_literals; namespace nano @@ -147,12 +149,12 @@ TEST (active_transactions, adjusted_difficulty_priority) //genesis and key1,key2 are opened //start chain of 2 on each - auto send3 (std::make_shared (nano::test_genesis_key.pub, send2->hash (), nano::test_genesis_key.pub, 9 * nano::xrb_ratio, key3.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send2->hash (), nano::difficulty::from_multiplier (1500, node1.network_params.network.publish_thresholds.base)))); - auto send4 (std::make_shared (nano::test_genesis_key.pub, send3->hash (), nano::test_genesis_key.pub, 8 * nano::xrb_ratio, key3.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send3->hash (), nano::difficulty::from_multiplier (1500, node1.network_params.network.publish_thresholds.base)))); - auto send5 (std::make_shared (key1.pub, open1->hash (), key1.pub, 9 * nano::xrb_ratio, key3.pub, key1.prv, key1.pub, *system.work.generate (open1->hash (), nano::difficulty::from_multiplier (100, node1.network_params.network.publish_thresholds.base)))); - auto send6 (std::make_shared (key1.pub, send5->hash (), key1.pub, 8 * nano::xrb_ratio, key3.pub, key1.prv, key1.pub, *system.work.generate (send5->hash (), nano::difficulty::from_multiplier (100, node1.network_params.network.publish_thresholds.base)))); - auto send7 (std::make_shared (key2.pub, open2->hash (), key2.pub, 9 * nano::xrb_ratio, key3.pub, key2.prv, key2.pub, *system.work.generate (open2->hash (), nano::difficulty::from_multiplier (500, node1.network_params.network.publish_thresholds.base)))); - auto send8 (std::make_shared (key2.pub, send7->hash (), key2.pub, 8 * nano::xrb_ratio, key3.pub, key2.prv, key2.pub, *system.work.generate (send7->hash (), nano::difficulty::from_multiplier (500, node1.network_params.network.publish_thresholds.base)))); + auto send3 (std::make_shared (nano::test_genesis_key.pub, send2->hash (), nano::test_genesis_key.pub, 9 * nano::xrb_ratio, key3.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send2->hash (), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))); + auto send4 (std::make_shared (nano::test_genesis_key.pub, send3->hash (), nano::test_genesis_key.pub, 8 * nano::xrb_ratio, key3.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send3->hash (), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))); + auto send5 (std::make_shared (key1.pub, open1->hash (), key1.pub, 9 * nano::xrb_ratio, key3.pub, key1.prv, key1.pub, system.work_generate_limited (open1->hash (), nano::difficulty::from_multiplier (10, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base)))); + auto send6 (std::make_shared (key1.pub, send5->hash (), key1.pub, 8 * nano::xrb_ratio, key3.pub, key1.prv, key1.pub, system.work_generate_limited (send5->hash (), nano::difficulty::from_multiplier (10, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base)))); + auto send7 (std::make_shared (key2.pub, open2->hash (), key2.pub, 9 * nano::xrb_ratio, key3.pub, key2.prv, key2.pub, system.work_generate_limited (open2->hash (), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))); + auto send8 (std::make_shared (key2.pub, send7->hash (), key2.pub, 8 * nano::xrb_ratio, key3.pub, key2.prv, key2.pub, system.work_generate_limited (send7->hash (), nano::difficulty::from_multiplier (50, node1.network_params.network.publish_thresholds.base), nano::difficulty::from_multiplier (150, node1.network_params.network.publish_thresholds.base)))); node1.process_active (send3); // genesis node1.process_active (send5); // key1 @@ -894,3 +896,23 @@ TEST (active_transactions, insertion_prioritization) update_active_difficulty (); ASSERT_FALSE (node.active.insert (blocks[6]).prioritized); } + +TEST (active_difficulty, less_than_one) +{ + nano::system system (1); + auto & node (*system.nodes[0]); + nano::unique_lock lock (node.active.mutex); + auto base_active_difficulty = node.network_params.network.publish_thresholds.epoch_1; + auto min_active_difficulty = node.network_params.network.publish_thresholds.entry; + auto min_multiplier = nano::difficulty::to_multiplier (min_active_difficulty, base_active_difficulty); + ASSERT_EQ (node.active.trended_active_difficulty, base_active_difficulty); + for (int i = 0; i < node.active.multipliers_cb.size () - 1; ++i) + { + node.active.multipliers_cb.push_front (min_multiplier); + } + auto sum (std::accumulate (node.active.multipliers_cb.begin (), node.active.multipliers_cb.end (), double(0))); + auto difficulty = nano::difficulty::from_multiplier (sum / node.active.multipliers_cb.size (), node.network_params.network.publish_thresholds.epoch_1); + node.active.multipliers_cb.push_front (min_multiplier); + node.active.update_active_difficulty (lock); + ASSERT_EQ (node.active.trended_active_difficulty, difficulty); +} diff --git a/nano/core_test/difficulty.cpp b/nano/core_test/difficulty.cpp index 6ea13e01..e27bc0c9 100644 --- a/nano/core_test/difficulty.cpp +++ b/nano/core_test/difficulty.cpp @@ -1,8 +1,28 @@ +#include #include +#include #include +#include +#include #include +TEST (system, work_generate_limited) +{ + nano::system system; + nano::block_hash key (1); + nano::network_constants constants; + auto min = constants.publish_thresholds.entry; + auto max = constants.publish_thresholds.base; + for (int i = 0; i < 5; ++i) + { + auto work = system.work_generate_limited (key, min, max); + auto difficulty = nano::work_difficulty (nano::work_version::work_1, key, work); + ASSERT_GE (difficulty, min); + ASSERT_LT (difficulty, max); + } +} + TEST (difficulty, multipliers) { // For ASSERT_DEATH_IF_SUPPORTED @@ -60,16 +80,6 @@ TEST (difficulty, multipliers) } } -TEST (difficulty, network_constants) -{ - nano::network_constants constants; - ASSERT_NEAR (1., nano::difficulty::to_multiplier (constants.publish_full.epoch_2_receive, constants.publish_full.entry), 1e-10); - ASSERT_NEAR (1., nano::difficulty::to_multiplier (constants.publish_full.epoch_2, constants.publish_full.base), 1e-10); - ASSERT_NEAR (8., nano::difficulty::to_multiplier (constants.publish_full.epoch_2, constants.publish_full.epoch_1), 1e-10); - ASSERT_NEAR (1 / 8., nano::difficulty::to_multiplier (constants.publish_full.epoch_2_receive, constants.publish_full.epoch_1), 1e-10); - ASSERT_NEAR (1 / 64., nano::difficulty::to_multiplier (constants.publish_beta.base, constants.publish_full.epoch_1), 1e-10); -} - TEST (difficulty, overflow) { // Overflow max (attempt to overflow & receive lower difficulty) @@ -110,3 +120,44 @@ TEST (difficulty, zero) ASSERT_EQ (difficulty, nano::difficulty::from_multiplier (multiplier, base)); } } + +TEST (difficulty, network_constants) +{ + nano::network_constants constants; + auto & full_thresholds = constants.publish_full; + auto & beta_thresholds = constants.publish_beta; + auto & test_thresholds = constants.publish_test; + + ASSERT_NEAR (8., nano::difficulty::to_multiplier (full_thresholds.epoch_2, full_thresholds.epoch_1), 1e-10); + ASSERT_NEAR (1 / 8., nano::difficulty::to_multiplier (full_thresholds.epoch_2_receive, full_thresholds.epoch_1), 1e-10); + ASSERT_NEAR (1., nano::difficulty::to_multiplier (full_thresholds.epoch_2_receive, full_thresholds.entry), 1e-10); + ASSERT_NEAR (1., nano::difficulty::to_multiplier (full_thresholds.epoch_2, full_thresholds.base), 1e-10); + + ASSERT_NEAR (1 / 64., nano::difficulty::to_multiplier (beta_thresholds.epoch_1, full_thresholds.epoch_1), 1e-10); + ASSERT_NEAR (2., nano::difficulty::to_multiplier (beta_thresholds.epoch_2, beta_thresholds.epoch_1), 1e-10); + ASSERT_NEAR (1 / 2., nano::difficulty::to_multiplier (beta_thresholds.epoch_2_receive, beta_thresholds.epoch_1), 1e-10); + ASSERT_NEAR (1., nano::difficulty::to_multiplier (beta_thresholds.epoch_2_receive, beta_thresholds.entry), 1e-10); + ASSERT_NEAR (1., nano::difficulty::to_multiplier (beta_thresholds.epoch_2, beta_thresholds.base), 1e-10); + + ASSERT_NEAR (8., nano::difficulty::to_multiplier (test_thresholds.epoch_2, test_thresholds.epoch_1), 1e-10); + ASSERT_NEAR (1 / 8., nano::difficulty::to_multiplier (test_thresholds.epoch_2_receive, test_thresholds.epoch_1), 1e-10); + ASSERT_NEAR (1., nano::difficulty::to_multiplier (test_thresholds.epoch_2_receive, test_thresholds.entry), 1e-10); + ASSERT_NEAR (1., nano::difficulty::to_multiplier (test_thresholds.epoch_2, test_thresholds.base), 1e-10); + + nano::work_version version{ nano::work_version::work_1 }; + ASSERT_EQ (constants.publish_thresholds.base, constants.publish_thresholds.epoch_2); + ASSERT_EQ (constants.publish_thresholds.base, nano::work_threshold_base (version)); + ASSERT_EQ (constants.publish_thresholds.entry, nano::work_threshold_entry (version)); + ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_0, false, false, false))); + ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_1, false, false, false))); + ASSERT_EQ (constants.publish_thresholds.epoch_1, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_1, false, false, false))); + + // Send [+ change] + ASSERT_EQ (constants.publish_thresholds.epoch_2, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_2, true, false, false))); + // Change + ASSERT_EQ (constants.publish_thresholds.epoch_2, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_2, false, false, false))); + // Receive [+ change] / Open + ASSERT_EQ (constants.publish_thresholds.epoch_2_receive, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_2, false, true, false))); + // Epoch + ASSERT_EQ (constants.publish_thresholds.epoch_2_receive, nano::work_threshold (version, nano::block_details (nano::epoch::epoch_2, false, false, true))); +} diff --git a/nano/core_test/distributed_work.cpp b/nano/core_test/distributed_work.cpp index b503a30c..9c8a8eed 100644 --- a/nano/core_test/distributed_work.cpp +++ b/nano/core_test/distributed_work.cpp @@ -10,7 +10,7 @@ TEST (distributed_work, stopped) { nano::system system (1); system.nodes[0]->distributed_work.stop (); - ASSERT_TRUE (system.nodes[0]->distributed_work.make (nano::work_version::work_1, nano::block_hash (), {}, {}, nano::network_constants ().publish_test.base)); + ASSERT_TRUE (system.nodes[0]->distributed_work.make (nano::work_version::work_1, nano::block_hash (), {}, nano::network_constants ().publish_thresholds.base, {})); } TEST (distributed_work, no_peers) @@ -25,13 +25,13 @@ TEST (distributed_work, no_peers) work = work_a; done = true; }; - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, node->config.work_peers, callback, node->network_params.network.publish_thresholds.base, nano::account ())); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, node->config.work_peers, node->network_params.network.publish_thresholds.base, callback, nano::account ())); system.deadline_set (5s); while (!done) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (nano::work_validate (nano::work_version::work_1, hash, *work)); + ASSERT_GE (nano::work_difficulty (nano::work_version::work_1, hash, *work), node->network_params.network.publish_thresholds.base); // should only be removed after cleanup ASSERT_EQ (1, node->distributed_work.items.size ()); while (!node->distributed_work.items.empty ()) @@ -47,7 +47,7 @@ TEST (distributed_work, no_peers_disabled) nano::node_config node_config (nano::get_available_port (), system.logging); node_config.work_threads = 0; auto & node = *system.add_node (node_config); - ASSERT_TRUE (node.distributed_work.make (nano::work_version::work_1, nano::block_hash (), node.config.work_peers, {}, nano::network_constants ().publish_test.base)); + ASSERT_TRUE (node.distributed_work.make (nano::work_version::work_1, nano::block_hash (), node.config.work_peers, nano::network_constants ().publish_thresholds.base, {})); } TEST (distributed_work, no_peers_cancel) @@ -55,7 +55,7 @@ TEST (distributed_work, no_peers_cancel) nano::system system; nano::node_config node_config (nano::get_available_port (), system.logging); node_config.max_work_generate_multiplier = 1e6; - node_config.max_work_generate_difficulty = nano::difficulty::from_multiplier (node_config.max_work_generate_multiplier, nano::network_constants ().publish_test.base); + node_config.max_work_generate_difficulty = nano::difficulty::from_multiplier (node_config.max_work_generate_multiplier, nano::network_constants ().publish_thresholds.base); auto & node = *system.add_node (node_config); nano::block_hash hash{ 1 }; bool done{ false }; @@ -63,7 +63,7 @@ TEST (distributed_work, no_peers_cancel) ASSERT_FALSE (work_a.is_initialized ()); done = true; }; - ASSERT_FALSE (node.distributed_work.make (nano::work_version::work_1, hash, node.config.work_peers, callback_to_cancel, nano::difficulty::from_multiplier (1e6, node.network_params.network.publish_thresholds.base))); + ASSERT_FALSE (node.distributed_work.make (nano::work_version::work_1, hash, node.config.work_peers, nano::difficulty::from_multiplier (1e6, node.network_params.network.publish_thresholds.base), callback_to_cancel)); ASSERT_EQ (1, node.distributed_work.items.size ()); // cleanup should not cancel or remove an ongoing work node.distributed_work.cleanup_finished (); @@ -79,7 +79,7 @@ TEST (distributed_work, no_peers_cancel) // now using observer done = false; - ASSERT_FALSE (node.distributed_work.make (nano::work_version::work_1, hash, node.config.work_peers, callback_to_cancel, nano::difficulty::from_multiplier (1e6, node.network_params.network.publish_thresholds.base))); + ASSERT_FALSE (node.distributed_work.make (nano::work_version::work_1, hash, node.config.work_peers, nano::difficulty::from_multiplier (1e6, node.network_params.network.publish_thresholds.base), callback_to_cancel)); ASSERT_EQ (1, node.distributed_work.items.size ()); node.observers.work_cancel.notify (hash); system.deadline_set (20s); @@ -103,7 +103,7 @@ TEST (distributed_work, no_peers_multi) // Test many works for the same root for (unsigned i{ 0 }; i < total; ++i) { - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, node->config.work_peers, callback, nano::difficulty::from_multiplier (10, node->network_params.network.publish_thresholds.base))); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, node->config.work_peers, nano::difficulty::from_multiplier (10, node->network_params.network.publish_thresholds.base), callback)); } // 1 root, and _total_ requests for that root are expected, but some may have already finished ASSERT_EQ (1, node->distributed_work.items.size ()); @@ -128,7 +128,7 @@ TEST (distributed_work, no_peers_multi) for (unsigned i{ 0 }; i < total; ++i) { nano::block_hash hash_i (i + 1); - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash_i, node->config.work_peers, callback, node->network_params.network.publish_thresholds.base)); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash_i, node->config.work_peers, node->network_params.network.publish_thresholds.base, callback)); } // 10 roots expected with 1 work each, but some may have completed so test for some ASSERT_GT (node->distributed_work.items.size (), 5); @@ -171,13 +171,13 @@ TEST (distributed_work, peer) work_peer->start (); decltype (node->config.work_peers) peers; peers.emplace_back ("::ffff:127.0.0.1", work_peer->port ()); - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_thresholds.base, nano::account ())); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.network.publish_thresholds.base, callback, nano::account ())); system.deadline_set (5s); while (!done) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (nano::work_validate (nano::work_version::work_1, hash, *work)); + ASSERT_GE (nano::work_difficulty (nano::work_version::work_1, hash, *work), node->network_params.network.publish_thresholds.base); ASSERT_EQ (1, work_peer->generations_good); ASSERT_EQ (0, work_peer->generations_bad); ASSERT_NO_ERROR (system.poll ()); @@ -201,13 +201,13 @@ TEST (distributed_work, peer_malicious) malicious_peer->start (); decltype (node->config.work_peers) peers; peers.emplace_back ("::ffff:127.0.0.1", malicious_peer->port ()); - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_thresholds.base, nano::account ())); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.network.publish_thresholds.base, callback, nano::account ())); system.deadline_set (5s); while (!done) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (nano::work_validate (nano::work_version::work_1, hash, *work)); + ASSERT_GE (nano::work_difficulty (nano::work_version::work_1, hash, *work), node->network_params.network.publish_thresholds.base); system.deadline_set (5s); while (malicious_peer->generations_bad < 1) { @@ -226,7 +226,7 @@ TEST (distributed_work, peer_malicious) auto malicious_peer2 (std::make_shared (node->work, node->io_ctx, nano::get_available_port (), work_peer_type::malicious)); malicious_peer2->start (); peers[0].second = malicious_peer2->port (); - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, nullptr, node->network_params.network.publish_thresholds.base, nano::account ())); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.network.publish_thresholds.base, {}, nano::account ())); system.deadline_set (5s); while (malicious_peer2->generations_bad < 2) { @@ -259,13 +259,13 @@ TEST (distributed_work, peer_multi) peers.emplace_back ("localhost", malicious_peer->port ()); peers.emplace_back ("localhost", slow_peer->port ()); peers.emplace_back ("localhost", good_peer->port ()); - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_thresholds.base, nano::account ())); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.network.publish_thresholds.base, callback, nano::account ())); system.deadline_set (5s); while (!done) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (nano::work_validate (nano::work_version::work_1, hash, *work)); + ASSERT_GE (nano::work_difficulty (nano::work_version::work_1, hash, *work), node->network_params.network.publish_thresholds.base); system.deadline_set (5s); while (slow_peer->cancels < 1) { @@ -298,11 +298,11 @@ TEST (distributed_work, fail_resolve) }; decltype (node->config.work_peers) peers; peers.emplace_back ("beeb.boop.123z", 0); - ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_thresholds.base, nano::account ())); + ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, node->network_params.network.publish_thresholds.base, callback, nano::account ())); system.deadline_set (5s); while (!done) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_FALSE (nano::work_validate (nano::work_version::work_1, hash, *work)); + ASSERT_GE (nano::work_difficulty (nano::work_version::work_1, hash, *work), node->network_params.network.publish_thresholds.base); } diff --git a/nano/core_test/fakes/work_peer.hpp b/nano/core_test/fakes/work_peer.hpp index cec4456e..2f54c09d 100644 --- a/nano/core_test/fakes/work_peer.hpp +++ b/nano/core_test/fakes/work_peer.hpp @@ -131,15 +131,16 @@ private: if (type == work_peer_type::good) { auto hash = hash_a; + auto request_difficulty = nano::work_threshold_base (version); auto this_l (shared_from_this ()); - work_pool.generate (version, hash, [this_l, hash](boost::optional work_a) { + work_pool.generate (version, hash, request_difficulty, [this_l, hash](boost::optional work_a) { auto result = work_a.value_or (0); - auto difficulty (nano::work_difficulty (this_l->version, hash, result)); + auto result_difficulty (nano::work_difficulty (this_l->version, hash, result)); static nano::network_params params; ptree::ptree message_l; message_l.put ("work", nano::to_string_hex (result)); - message_l.put ("difficulty", nano::to_string_hex (difficulty)); - message_l.put ("multiplier", nano::to_string (nano::difficulty::to_multiplier (difficulty, nano::work_threshold (this_l->version)))); + message_l.put ("difficulty", nano::to_string_hex (result_difficulty)); + message_l.put ("multiplier", nano::to_string (nano::difficulty::to_multiplier (result_difficulty, nano::work_threshold_base (this_l->version)))); message_l.put ("hash", hash.to_string ()); std::stringstream ostream; ptree::write_json (ostream, message_l); diff --git a/nano/core_test/node.cpp b/nano/core_test/node.cpp index 84b227ae..83bd3ca7 100644 --- a/nano/core_test/node.cpp +++ b/nano/core_test/node.cpp @@ -28,6 +28,31 @@ TEST (node, stop) ASSERT_TRUE (true); } +TEST (node, work_generate) +{ + nano::system system (1); + auto & node (*system.nodes[0]); + nano::block_hash root{ 1 }; + nano::work_version version{ nano::work_version::work_1 }; + { + auto difficulty = nano::difficulty::from_multiplier (1.5, node.network_params.network.publish_thresholds.base); + auto work = node.work_generate_blocking (version, root, difficulty); + ASSERT_TRUE (work.is_initialized ()); + ASSERT_TRUE (nano::work_difficulty (version, root, *work) >= difficulty); + } + { + auto difficulty = nano::difficulty::from_multiplier (0.5, node.network_params.network.publish_thresholds.base); + boost::optional work; + do + { + work = node.work_generate_blocking (version, root, difficulty); + } while (nano::work_difficulty (version, root, *work) >= node.network_params.network.publish_thresholds.base); + ASSERT_TRUE (work.is_initialized ()); + ASSERT_TRUE (nano::work_difficulty (version, root, *work) >= difficulty); + ASSERT_FALSE (nano::work_difficulty (version, root, *work) >= node.network_params.network.publish_thresholds.base); + } +} + TEST (node, block_store_path_failure) { auto service (boost::make_shared ()); @@ -3732,10 +3757,10 @@ TEST (active_difficulty, recalculate_work) auto & node1 = *system.add_node (node_config); nano::genesis genesis; nano::keypair key1; - ASSERT_EQ (node1.network_params.network.publish_thresholds.base, node1.active.active_difficulty ()); + ASSERT_EQ (node1.network_params.network.publish_thresholds.epoch_1, node1.active.active_difficulty ()); auto send1 (std::make_shared (genesis.hash (), key1.pub, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, 0)); node1.work_generate_blocking (*send1); - auto multiplier1 = nano::difficulty::to_multiplier (send1->difficulty (), nano::work_threshold (send1->work_version ())); + auto multiplier1 = nano::difficulty::to_multiplier (send1->difficulty (), node1.network_params.network.publish_thresholds.epoch_1); // Process as local block node1.process_active (send1); system.deadline_set (2s); @@ -3744,7 +3769,7 @@ TEST (active_difficulty, recalculate_work) ASSERT_NO_ERROR (system.poll ()); } auto sum (std::accumulate (node1.active.multipliers_cb.begin (), node1.active.multipliers_cb.end (), double(0))); - ASSERT_EQ (node1.active.active_difficulty (), nano::difficulty::from_multiplier (sum / node1.active.multipliers_cb.size (), node1.network_params.network.publish_thresholds.base)); + ASSERT_EQ (node1.active.active_difficulty (), nano::difficulty::from_multiplier (sum / node1.active.multipliers_cb.size (), node1.network_params.network.publish_thresholds.epoch_1)); nano::unique_lock lock (node1.active.mutex); // Fake history records to force work recalculation for (auto i (0); i < node1.active.multipliers_cb.size (); i++) @@ -3755,7 +3780,7 @@ TEST (active_difficulty, recalculate_work) node1.process_active (send1); node1.active.update_active_difficulty (lock); sum = std::accumulate (node1.active.multipliers_cb.begin (), node1.active.multipliers_cb.end (), double(0)); - ASSERT_EQ (node1.active.trended_active_difficulty, nano::difficulty::from_multiplier (sum / node1.active.multipliers_cb.size (), node1.network_params.network.publish_thresholds.base)); + ASSERT_EQ (node1.active.trended_active_difficulty, nano::difficulty::from_multiplier (sum / node1.active.multipliers_cb.size (), node1.network_params.network.publish_thresholds.epoch_1)); lock.unlock (); } diff --git a/nano/core_test/wallet.cpp b/nano/core_test/wallet.cpp index f4c85086..fe14874d 100644 --- a/nano/core_test/wallet.cpp +++ b/nano/core_test/wallet.cpp @@ -649,7 +649,7 @@ TEST (wallet, work) uint64_t work (0); if (!wallet->store.work_get (transaction, nano::test_genesis_key.pub, work)) { - done = !nano::work_validate (genesis.open->work_version (), genesis.hash (), work); + done = nano::work_difficulty (genesis.open->work_version (), genesis.hash (), work) >= nano::work_threshold_base (genesis.open->work_version ()); } ASSERT_NO_ERROR (system.poll ()); } @@ -683,7 +683,7 @@ TEST (wallet, work_generate) ASSERT_NO_ERROR (system.poll ()); auto block_transaction (node1.store.tx_begin_read ()); auto transaction (system.wallet (0)->wallets.tx_begin_read ()); - again = wallet->store.work_get (transaction, account1, work1) || nano::work_validate (block->work_version (), node1.ledger.latest_root (block_transaction, account1), work1); + again = wallet->store.work_get (transaction, account1, work1) || nano::work_difficulty (block->work_version (), node1.ledger.latest_root (block_transaction, account1), work1) < nano::work_threshold_base (block->work_version ()); } } @@ -949,7 +949,7 @@ TEST (wallet, no_work) auto block (system.wallet (0)->send_action (nano::test_genesis_key.pub, key2.pub, std::numeric_limits::max (), false)); ASSERT_NE (nullptr, block); ASSERT_NE (0, block->block_work ()); - ASSERT_FALSE (nano::work_validate (*block)); + ASSERT_GE (block->difficulty (), nano::work_threshold (block->work_version (), block->sideband ().details)); auto transaction (system.wallet (0)->wallets.tx_begin_read ()); uint64_t cached_work (0); system.wallet (0)->store.work_get (transaction, nano::test_genesis_key.pub, cached_work); @@ -1185,7 +1185,7 @@ TEST (wallet, work_watcher_generation_disabled) node.wallets.watcher->add (block); ASSERT_FALSE (node.process_local (block).code != nano::process_result::progress); ASSERT_TRUE (node.wallets.watcher->is_watched (block->qualified_root ())); - auto multiplier = nano::difficulty::to_multiplier (difficulty, nano::work_threshold (block->work_version ())); + auto multiplier = nano::difficulty::to_multiplier (difficulty, nano::work_threshold_base (block->work_version ())); uint64_t updated_difficulty{ difficulty }; { nano::unique_lock lock (node.active.mutex); @@ -1238,7 +1238,7 @@ TEST (wallet, work_watcher_cancel) nano::node_config node_config (nano::get_available_port (), system.logging); node_config.work_watcher_period = 1s; node_config.max_work_generate_multiplier = 1e6; - node_config.max_work_generate_difficulty = nano::difficulty::from_multiplier (node_config.max_work_generate_multiplier, nano::network_constants ().publish_test.base); + node_config.max_work_generate_difficulty = nano::difficulty::from_multiplier (node_config.max_work_generate_multiplier, nano::network_constants ().publish_thresholds.base); node_config.enable_voting = false; auto & node = *system.add_node (node_config); auto & wallet (*system.wallet (0)); @@ -1282,3 +1282,154 @@ TEST (wallet, work_watcher_cancel) ASSERT_TRUE (wallet.wallets.watcher->is_watched (block1->qualified_root ())); } } + +// Ensure the minimum limited difficulty is enough for the highest threshold +TEST (wallet, limited_difficulty) +{ + nano::system system; + nano::node_config node_config (nano::get_available_port (), system.logging); + node_config.max_work_generate_difficulty = nano::network_constants ().publish_thresholds.base; + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node = *system.add_node (node_config, node_flags); + auto & wallet (*system.wallet (0)); + // Upgrade the genesis account to epoch 2 + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_1)); + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_2)); + ASSERT_EQ (nano::epoch::epoch_2, node.store.block_version (node.store.tx_begin_read (), node.latest (nano::test_genesis_key.pub))); + wallet.insert_adhoc (nano::test_genesis_key.prv, false); + { + // Force active difficulty to an impossibly high value + nano::lock_guard guard (node.active.mutex); + node.active.trended_active_difficulty = std::numeric_limits::max (); + } + ASSERT_EQ (node_config.max_work_generate_difficulty, node.active.limited_active_difficulty ()); + auto send = wallet.send_action (nano::test_genesis_key.pub, nano::keypair ().pub, 1, 1); + ASSERT_NE (nullptr, send); +} + +TEST (wallet, epoch_2_validation) +{ + nano::system system (1); + auto & node (*system.nodes[0]); + auto & wallet (*system.wallet (0)); + + // Upgrade the genesis account to epoch 2 + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_1)); + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_2)); + + wallet.insert_adhoc (nano::test_genesis_key.prv, false); + + // Test send and receive blocks + // An epoch 2 receive block should be generated with lower difficulty with high probability + auto tries = 0; + auto max_tries = 20; + auto amount = node.config.receive_minimum.number (); + while (++tries < max_tries) + { + auto send = wallet.send_action (nano::test_genesis_key.pub, nano::test_genesis_key.pub, amount, 1); + ASSERT_NE (nullptr, send); + + auto receive = wallet.receive_action (*send, nano::test_genesis_key.pub, amount, 1); + ASSERT_NE (nullptr, receive); + if (receive->difficulty () < node.network_params.network.publish_thresholds.base) + { + ASSERT_GE (receive->difficulty (), node.network_params.network.publish_thresholds.epoch_2_receive); + break; + } + } + ASSERT_LT (tries, max_tries); + + // Test a change block + ASSERT_NE (nullptr, wallet.change_action (nano::test_genesis_key.pub, nano::keypair ().pub, 1)); +} + +// Receiving from an upgraded account uses the lower threshold and upgrades the receiving account +TEST (wallet, epoch_2_receive_propagation) +{ + auto tries = 0; + auto const max_tries = 20; + while (++tries < max_tries) + { + nano::system system (1); + auto & node (*system.nodes[0]); + auto & wallet (*system.wallet (0)); + + // Upgrade the genesis account to epoch 1 + auto epoch1 = system.upgrade_genesis_epoch (node, nano::epoch::epoch_1); + ASSERT_NE (nullptr, epoch1); + + nano::keypair key; + nano::state_block_builder builder; + + // Send and open the account + wallet.insert_adhoc (nano::test_genesis_key.prv, false); + wallet.insert_adhoc (key.prv, false); + auto amount = node.config.receive_minimum.number (); + auto send1 = wallet.send_action (nano::test_genesis_key.pub, key.pub, amount, 1); + ASSERT_NE (nullptr, send1); + ASSERT_NE (nullptr, wallet.receive_action (*send1, nano::test_genesis_key.pub, amount, 1)); + + // Upgrade the genesis account to epoch 2 + auto epoch2 = system.upgrade_genesis_epoch (node, nano::epoch::epoch_2); + ASSERT_NE (nullptr, epoch2); + + // Send a block + auto send2 = wallet.send_action (nano::test_genesis_key.pub, key.pub, amount, 1); + ASSERT_NE (nullptr, send2); + + // Receiving should use the lower difficulty + auto receive2 = wallet.receive_action (*send2, key.pub, amount, 1); + ASSERT_NE (nullptr, receive2); + if (receive2->difficulty () < node.network_params.network.publish_thresholds.base) + { + ASSERT_GE (receive2->difficulty (), node.network_params.network.publish_thresholds.epoch_2_receive); + ASSERT_EQ (nano::epoch::epoch_2, node.store.block_version (node.store.tx_begin_read (), receive2->hash ())); + break; + } + } + ASSERT_LT (tries, max_tries); +} + +// Opening an upgraded account uses the lower threshold +TEST (wallet, epoch_2_receive_unopened) +{ + // Ensure the lower receive work is used when receiving + auto tries = 0; + auto const max_tries = 20; + while (++tries < max_tries) + { + nano::system system (1); + auto & node (*system.nodes[0]); + auto & wallet (*system.wallet (0)); + + // Upgrade the genesis account to epoch 1 + auto epoch1 = system.upgrade_genesis_epoch (node, nano::epoch::epoch_1); + ASSERT_NE (nullptr, epoch1); + + nano::keypair key; + nano::state_block_builder builder; + + // Send + wallet.insert_adhoc (nano::test_genesis_key.prv, false); + auto amount = node.config.receive_minimum.number (); + auto send1 = wallet.send_action (nano::test_genesis_key.pub, key.pub, amount, 1); + + // Upgrade unopened account to epoch_2 + auto epoch2_unopened = nano::state_block (key.pub, 0, 0, 0, node.network_params.ledger.epochs.link (nano::epoch::epoch_2), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (key.pub, node.network_params.network.publish_thresholds.epoch_2)); + ASSERT_EQ (nano::process_result::progress, node.process (epoch2_unopened).code); + + wallet.insert_adhoc (key.prv, false); + + // Receiving should use the lower difficulty + auto receive1 = wallet.receive_action (*send1, key.pub, amount, 1); + ASSERT_NE (nullptr, receive1); + if (receive1->difficulty () < node.network_params.network.publish_thresholds.base) + { + ASSERT_GE (receive1->difficulty (), node.network_params.network.publish_thresholds.epoch_2_receive); + ASSERT_EQ (nano::epoch::epoch_2, node.store.block_version (node.store.tx_begin_read (), receive1->hash ())); + break; + } + } + ASSERT_LT (tries, max_tries); +} diff --git a/nano/core_test/websocket.cpp b/nano/core_test/websocket.cpp index 5ea706a9..f6c123f5 100644 --- a/nano/core_test/websocket.cpp +++ b/nano/core_test/websocket.cpp @@ -104,14 +104,14 @@ TEST (websocket, active_difficulty) auto message_contents = event.get_child ("message"); uint64_t network_minimum; nano::from_string_hex (message_contents.get ("network_minimum"), network_minimum); - ASSERT_EQ (network_minimum, node1->network_params.network.publish_thresholds.base); + ASSERT_EQ (network_minimum, node1->network_params.network.publish_thresholds.epoch_1); uint64_t network_current; nano::from_string_hex (message_contents.get ("network_current"), network_current); ASSERT_EQ (network_current, node1->active.active_difficulty ()); double multiplier = message_contents.get ("multiplier"); - ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (node1->active.active_difficulty (), node1->network_params.network.publish_thresholds.base), 1e-6); + ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (node1->active.active_difficulty (), node1->network_params.network.publish_thresholds.epoch_1), 1e-6); } // Subscribes to block confirmations, confirms a block and then awaits websocket notification diff --git a/nano/core_test/work_pool.cpp b/nano/core_test/work_pool.cpp index 87970f58..a04b5921 100644 --- a/nano/core_test/work_pool.cpp +++ b/nano/core_test/work_pool.cpp @@ -20,7 +20,7 @@ TEST (work, one) nano::work_pool pool (std::numeric_limits::max ()); nano::change_block block (1, 1, nano::keypair ().prv, 3, 4); block.block_work_set (*pool.generate (block.root ())); - ASSERT_LT (nano::work_threshold (block.work_version ()), block.difficulty ()); + ASSERT_LT (nano::work_threshold_base (block.work_version ()), block.difficulty ()); } TEST (work, disabled) @@ -36,9 +36,9 @@ TEST (work, validate) nano::network_constants network_constants; nano::work_pool pool (std::numeric_limits::max ()); nano::send_block send_block (1, 1, 2, nano::keypair ().prv, 4, 6); - ASSERT_LT (send_block.difficulty (), nano::work_threshold (send_block.work_version ())); + ASSERT_LT (send_block.difficulty (), nano::work_threshold_base (send_block.work_version ())); send_block.block_work_set (*pool.generate (send_block.root ())); - ASSERT_LT (nano::work_threshold (send_block.work_version ()), send_block.difficulty ()); + ASSERT_LT (nano::work_threshold_base (send_block.work_version ()), send_block.difficulty ()); } TEST (work, cancel) @@ -49,7 +49,8 @@ TEST (work, cancel) while (!done) { nano::root key (1); - pool.generate (key, [&done](boost::optional work_a) { + pool.generate ( + nano::work_version::work_1, key, nano::network_constants ().publish_thresholds.base, [&done](boost::optional work_a) { done = !work_a; }); pool.cancel (key); @@ -67,12 +68,13 @@ TEST (work, cancel_many) nano::root key4 (1); nano::root key5 (3); nano::root key6 (1); - pool.generate (key1, [](boost::optional) {}); - pool.generate (key2, [](boost::optional) {}); - pool.generate (key3, [](boost::optional) {}); - pool.generate (key4, [](boost::optional) {}); - pool.generate (key5, [](boost::optional) {}); - pool.generate (key6, [](boost::optional) {}); + nano::network_constants constants; + pool.generate (nano::work_version::work_1, key1, constants.publish_thresholds.base, [](boost::optional) {}); + pool.generate (nano::work_version::work_1, key2, constants.publish_thresholds.base, [](boost::optional) {}); + pool.generate (nano::work_version::work_1, key3, constants.publish_thresholds.base, [](boost::optional) {}); + pool.generate (nano::work_version::work_1, key4, constants.publish_thresholds.base, [](boost::optional) {}); + pool.generate (nano::work_version::work_1, key5, constants.publish_thresholds.base, [](boost::optional) {}); + pool.generate (nano::work_version::work_1, key6, constants.publish_thresholds.base, [](boost::optional) {}); pool.cancel (key1); } diff --git a/nano/lib/config.cpp b/nano/lib/config.cpp index 74a02bb9..7d462981 100644 --- a/nano/lib/config.cpp +++ b/nano/lib/config.cpp @@ -15,14 +15,14 @@ work_thresholds const network_constants::publish_full ( work_thresholds const network_constants::publish_beta ( 0xfffff00000000000, // 64x lower than publish_full.epoch_1 -0xfffff00000000000, -0xfffff00000000000 // +0xfffff80000000000, // 2x higher than epoch_1 +0xffffe00000000000 // 2x lower than epoch_1 ); work_thresholds const network_constants::publish_test ( -0xff00000000000000, // Very low for tests -0xff00000000000000, -0xff00000000000000 // +0xfe00000000000000, // Very low for tests +0xffc0000000000000, // 8x higher than epoch_1 +0xf000000000000000 // 8x lower than epoch_1 ); const char * network_constants::active_network_err_msg = "Invalid network. Valid values are live, beta and test."; diff --git a/nano/lib/errors.cpp b/nano/lib/errors.cpp index 5f9971ce..0fea83b9 100644 --- a/nano/lib/errors.cpp +++ b/nano/lib/errors.cpp @@ -244,6 +244,8 @@ std::string nano::error_process_messages::message (int ev) const return "Balance and amount delta do not match"; case nano::error_process::block_position: return "This block cannot follow the previous block"; + case nano::error_process::insufficient_work: + return "Block work is insufficient"; case nano::error_process::other: return "Error processing block"; } diff --git a/nano/lib/errors.hpp b/nano/lib/errors.hpp index a8ac11f5..72420374 100644 --- a/nano/lib/errors.hpp +++ b/nano/lib/errors.hpp @@ -135,6 +135,7 @@ enum class error_process 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 block_position, // This block cannot follow the previous block + insufficient_work, // Insufficient work for this block, even though it passed the minimal validation other }; diff --git a/nano/lib/work.cpp b/nano/lib/work.cpp index 884c3b01..2efb70ae 100644 --- a/nano/lib/work.cpp +++ b/nano/lib/work.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -21,14 +22,14 @@ std::string nano::to_string (nano::work_version const version_a) return result; } -bool nano::work_validate (nano::block const & block_a) +bool nano::work_validate_entry (nano::block const & block_a) { - return block_a.difficulty () < nano::work_threshold (block_a.work_version ()); + return block_a.difficulty () < nano::work_threshold_entry (block_a.work_version ()); } -bool nano::work_validate (nano::work_version const version_a, nano::root const & root_a, uint64_t const work_a) +bool nano::work_validate_entry (nano::work_version const version_a, nano::root const & root_a, uint64_t const work_a) { - return nano::work_difficulty (version_a, root_a, work_a) < nano::work_threshold (version_a); + return nano::work_difficulty (version_a, root_a, work_a) < nano::work_threshold_entry (version_a); } uint64_t nano::work_difficulty (nano::work_version const version_a, nano::root const & root_a, uint64_t const work_a) @@ -45,16 +46,30 @@ uint64_t nano::work_difficulty (nano::work_version const version_a, nano::root c return result; } -uint64_t nano::work_threshold (nano::work_version const version_a) +uint64_t nano::work_threshold_base (nano::work_version const version_a) { uint64_t result{ std::numeric_limits::max () }; switch (version_a) { case nano::work_version::work_1: - result = nano::work_v1::threshold (); + result = nano::work_v1::threshold_base (); break; default: - debug_assert (false && "Invalid version specified to entry work_threshold"); + debug_assert (false && "Invalid version specified to work_threshold_base"); + } + return result; +} + +uint64_t nano::work_threshold_entry (nano::work_version const version_a) +{ + uint64_t result{ std::numeric_limits::max () }; + switch (version_a) + { + case nano::work_version::work_1: + result = nano::work_v1::threshold_entry (); + break; + default: + debug_assert (false && "Invalid version specified to work_threshold_entry"); } return result; } @@ -73,31 +88,32 @@ uint64_t nano::work_threshold (nano::work_version const version_a, nano::block_d return result; } -uint64_t nano::work_v1::threshold () +uint64_t nano::work_v1::threshold_base () { static nano::network_constants network_constants; return network_constants.publish_thresholds.base; } +uint64_t nano::work_v1::threshold_entry () +{ + static nano::network_constants network_constants; + return network_constants.publish_thresholds.entry; +} + uint64_t nano::work_v1::threshold (nano::block_details const details_a) { static_assert (nano::epoch::max == nano::epoch::epoch_2, "work_v1::threshold is ill-defined"); static nano::network_constants network_constants; - if (!network_constants.is_live_network ()) - { - return network_constants.publish_thresholds.base; - } - uint64_t result{ std::numeric_limits::max () }; switch (details_a.epoch) { case nano::epoch::epoch_2: - result = (details_a.is_receive || details_a.is_epoch) ? network_constants.publish_full.epoch_2_receive : network_constants.publish_full.epoch_2; + result = (details_a.is_receive || details_a.is_epoch) ? network_constants.publish_thresholds.epoch_2_receive : network_constants.publish_thresholds.epoch_2; break; case nano::epoch::epoch_1: case nano::epoch::epoch_0: - result = network_constants.publish_full.epoch_1; + result = network_constants.publish_thresholds.epoch_1; break; default: debug_assert (false && "Invalid epoch specified to work_v1 ledger work_threshold"); @@ -287,29 +303,14 @@ void nano::work_pool::stop () producer_condition.notify_all (); } -void nano::work_pool::generate (nano::root const & root_a, std::function const &)> callback_a) -{ - generate (nano::work_version::work_1, root_a, callback_a); -} - -void nano::work_pool::generate (nano::work_version const version_a, nano::root const & root_a, std::function const &)> callback_a) -{ - generate (version_a, root_a, callback_a, network_constants.publish_thresholds.base); -} - -void nano::work_pool::generate (nano::root const & root_a, std::function const &)> callback_a, uint64_t difficulty_a) -{ - generate (nano::work_version::work_1, root_a, callback_a, difficulty_a); -} - -void nano::work_pool::generate (nano::work_version const version_a, nano::root const & root_a, std::function const &)> callback_a, uint64_t difficulty_a) +void nano::work_pool::generate (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::function const &)> callback_a) { debug_assert (!root_a.is_zero ()); if (!threads.empty ()) { { nano::lock_guard lock (mutex); - pending.emplace_back (version_a, root_a, callback_a, difficulty_a); + pending.emplace_back (version_a, root_a, difficulty_a, callback_a); } producer_condition.notify_all (); } @@ -323,12 +324,7 @@ boost::optional nano::work_pool::generate (nano::root const & root_a) { static nano::network_constants network_constants; debug_assert (network_constants.is_test_network ()); - return generate (nano::work_version::work_1, root_a); -} - -boost::optional nano::work_pool::generate (nano::work_version const version_a, nano::root const & root_a) -{ - return generate (version_a, root_a, network_constants.publish_thresholds.base); + return generate (nano::work_version::work_1, root_a, network_constants.publish_thresholds.base); } boost::optional nano::work_pool::generate (nano::root const & root_a, uint64_t difficulty_a) @@ -345,11 +341,9 @@ boost::optional nano::work_pool::generate (nano::work_version const ve { std::promise> work; std::future> future = work.get_future (); - generate ( - version_a, root_a, [&work](boost::optional work_a) { + generate (version_a, root_a, difficulty_a, [&work](boost::optional work_a) { work.set_value (work_a); - }, - difficulty_a); + }); result = future.get ().value (); } return result; diff --git a/nano/lib/work.hpp b/nano/lib/work.hpp index 031d21e3..eec1b627 100644 --- a/nano/lib/work.hpp +++ b/nano/lib/work.hpp @@ -22,33 +22,35 @@ std::string to_string (nano::work_version const version_a); class block; class block_details; -bool work_validate (nano::block const &); -bool work_validate (nano::work_version const, nano::root const &, uint64_t const); +bool work_validate_entry (nano::block const &); +bool work_validate_entry (nano::work_version const, nano::root const &, uint64_t const); uint64_t work_difficulty (nano::work_version const, nano::root const &, uint64_t const); -// Entry threshold -uint64_t work_threshold (nano::work_version const); + +uint64_t work_threshold_base (nano::work_version const); +uint64_t work_threshold_entry (nano::work_version const); // Ledger threshold uint64_t work_threshold (nano::work_version const, nano::block_details const); namespace work_v1 { uint64_t value (nano::root const & root_a, uint64_t work_a); - uint64_t threshold (); + uint64_t threshold_base (); + uint64_t threshold_entry (); uint64_t threshold (nano::block_details const); } class opencl_work; class work_item final { public: - work_item (nano::work_version const version_a, nano::root const & item_a, std::function const &)> const & callback_a, uint64_t difficulty_a) : - version (version_a), item (item_a), callback (callback_a), difficulty (difficulty_a) + work_item (nano::work_version const version_a, nano::root const & item_a, uint64_t difficulty_a, std::function const &)> const & callback_a) : + version (version_a), item (item_a), difficulty (difficulty_a), callback (callback_a) { } - nano::work_version version; - nano::root item; - std::function const &)> callback; - uint64_t difficulty; + nano::work_version const version; + nano::root const item; + uint64_t const difficulty; + std::function const &)> const callback; }; class work_pool final { @@ -58,14 +60,9 @@ public: void loop (uint64_t); void stop (); void cancel (nano::root const &); - void generate (nano::work_version const, nano::root const &, std::function const &)>); - void generate (nano::work_version const, nano::root const &, std::function const &)>, uint64_t); - boost::optional generate (nano::work_version const, nano::root const &); + void generate (nano::work_version const, nano::root const &, uint64_t, std::function const &)>); boost::optional generate (nano::work_version const, nano::root const &, uint64_t); // For tests only - void generate (nano::root const &, std::function const &)>); - // For tests only - void generate (nano::root const &, std::function const &)>, uint64_t); boost::optional generate (nano::root const &); boost::optional generate (nano::root const &, uint64_t); size_t size (); diff --git a/nano/nano_node/entry.cpp b/nano/nano_node/entry.cpp index 9b93c330..b3b19ec5 100644 --- a/nano/nano_node/entry.cpp +++ b/nano/nano_node/entry.cpp @@ -188,8 +188,9 @@ int main (int argc, char * const * argv) << "Public: " << rep.pub.to_string () << "\n" << "Account: " << rep.pub.to_account () << "\n"; } + nano::network_constants network_constants; nano::uint128_t balance (std::numeric_limits::max ()); - nano::open_block genesis_block (reinterpret_cast (genesis.pub), genesis.pub, genesis.pub, genesis.prv, genesis.pub, *work.generate (nano::work_version::work_1, genesis.pub)); + nano::open_block genesis_block (reinterpret_cast (genesis.pub), genesis.pub, genesis.pub, genesis.prv, genesis.pub, *work.generate (nano::work_version::work_1, genesis.pub, network_constants.publish_thresholds.epoch_1)); std::cout << genesis_block.to_json (); std::cout.flush (); nano::block_hash previous (genesis_block.hash ()); @@ -201,7 +202,7 @@ int main (int argc, char * const * argv) { debug_assert (balance > weekly_distribution); balance = balance < (weekly_distribution * 2) ? 0 : balance - weekly_distribution; - nano::send_block send (previous, landing.pub, balance, genesis.prv, genesis.pub, *work.generate (nano::work_version::work_1, previous)); + nano::send_block send (previous, landing.pub, balance, genesis.prv, genesis.pub, *work.generate (nano::work_version::work_1, previous, network_constants.publish_thresholds.epoch_1)); previous = send.hash (); std::cout << send.to_json (); std::cout.flush (); @@ -306,6 +307,8 @@ int main (int argc, char * const * argv) } else if (vm.count ("debug_profile_generate")) { + nano::network_constants network_constants; + uint64_t difficulty{ network_constants.publish_full.base }; auto pow_rate_limiter = std::chrono::nanoseconds (0); auto pow_sleep_interval_it = vm.find ("pow_sleep_interval"); if (pow_sleep_interval_it != vm.cend ()) @@ -320,7 +323,7 @@ int main (int argc, char * const * argv) { block.hashables.previous.qwords[0] += 1; auto begin1 (std::chrono::high_resolution_clock::now ()); - block.block_work_set (*work.generate (nano::work_version::work_1, block.root ())); + block.block_work_set (*work.generate (nano::work_version::work_1, block.root (), difficulty)); auto end1 (std::chrono::high_resolution_clock::now ()); std::cerr << boost::str (boost::format ("%|1$ 12d|\n") % std::chrono::duration_cast (end1 - begin1).count ()); } @@ -737,7 +740,7 @@ int main (int argc, char * const * argv) .balance (genesis_balance) .link (keys[i].pub) .sign (test_params.ledger.test_genesis_key.prv, test_params.ledger.test_genesis_key.pub) - .work (*work.generate (nano::work_version::work_1, genesis_latest)) + .work (*work.generate (nano::work_version::work_1, genesis_latest, node->network_params.network.publish_thresholds.epoch_1)) .build (); genesis_latest = send->hash (); @@ -750,7 +753,7 @@ int main (int argc, char * const * argv) .balance (balances[i]) .link (genesis_latest) .sign (keys[i].prv, keys[i].pub) - .work (*work.generate (nano::work_version::work_1, keys[i].pub)) + .work (*work.generate (nano::work_version::work_1, keys[i].pub, node->network_params.network.publish_thresholds.epoch_1)) .build (); frontiers[i] = open->hash (); @@ -771,7 +774,7 @@ int main (int argc, char * const * argv) .balance (balances[j]) .link (keys[other].pub) .sign (keys[j].prv, keys[j].pub) - .work (*work.generate (nano::work_version::work_1, frontiers[j])) + .work (*work.generate (nano::work_version::work_1, frontiers[j], node->network_params.network.publish_thresholds.epoch_1)) .build (); frontiers[j] = send->hash (); @@ -786,7 +789,7 @@ int main (int argc, char * const * argv) .balance (balances[other]) .link (static_cast (frontiers[j])) .sign (keys[other].prv, keys[other].pub) - .work (*work.generate (nano::work_version::work_1, frontiers[other])) + .work (*work.generate (nano::work_version::work_1, frontiers[other], node->network_params.network.publish_thresholds.epoch_1)) .build (); frontiers[other] = receive->hash (); @@ -858,7 +861,7 @@ int main (int argc, char * const * argv) .balance (genesis_balance) .link (keys[i].pub) .sign (test_params.ledger.test_genesis_key.prv, test_params.ledger.test_genesis_key.pub) - .work (*work.generate (nano::work_version::work_1, genesis_latest)) + .work (*work.generate (nano::work_version::work_1, genesis_latest, node->network_params.network.publish_thresholds.epoch_1)) .build (); genesis_latest = send->hash (); @@ -871,7 +874,7 @@ int main (int argc, char * const * argv) .balance (balance) .link (genesis_latest) .sign (keys[i].prv, keys[i].pub) - .work (*work.generate (nano::work_version::work_1, keys[i].pub)) + .work (*work.generate (nano::work_version::work_1, keys[i].pub, node->network_params.network.publish_thresholds.epoch_1)) .build (); node->ledger.process (transaction, *open); @@ -890,7 +893,7 @@ int main (int argc, char * const * argv) .balance (genesis_balance) .link (destination.pub) .sign (test_params.ledger.test_genesis_key.prv, test_params.ledger.test_genesis_key.pub) - .work (*work.generate (nano::work_version::work_1, genesis_latest)) + .work (*work.generate (nano::work_version::work_1, genesis_latest, node->network_params.network.publish_thresholds.epoch_1)) .build (); genesis_latest = send->hash (); @@ -1104,7 +1107,7 @@ int main (int argc, char * const * argv) std::cerr << boost::str (boost::format ("Incorrect sideband block details for block %1%\n") % hash.to_string ()); } // Check if block work value is correct - if (nano::work_validate (*block)) + if (block->difficulty () < nano::work_threshold (block->work_version (), block->sideband ().details)) { std::cerr << boost::str (boost::format ("Invalid work for block %1% value: %2%\n") % hash.to_string () % nano::to_string_hex (block->block_work ())); } diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index ebebcadd..d15a8a30 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -18,7 +18,7 @@ nano::active_transactions::active_transactions (nano::node & node_a, nano::confi confirmation_height_processor (confirmation_height_processor_a), node (node_a), multipliers_cb (20, 1.), -trended_active_difficulty (node_a.network_params.network.publish_thresholds.base), +trended_active_difficulty (node_a.network_params.network.publish_thresholds.epoch_1), check_all_elections_period (node_a.network_params.network.is_test_network () ? 10ms : 5s), election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s), prioritized_cutoff (std::max (1, node_a.config.active_elections_size / 10)), @@ -708,7 +708,7 @@ void nano::active_transactions::update_adjusted_difficulty () auto existing_root (roots.get ().find (root)); if (existing_root != roots.get ().end ()) { - sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_thresholds.base); + sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_thresholds.epoch_1); elections_list.emplace_back (root, level); if (level > highest_level) { @@ -726,7 +726,7 @@ void nano::active_transactions::update_adjusted_difficulty () if (!elections_list.empty ()) { double multiplier = sum / elections_list.size (); - uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_thresholds.base); + uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_thresholds.epoch_1); // Prevent overflow int64_t limiter (0); if (std::numeric_limits::max () - average < static_cast (highest_level)) @@ -778,18 +778,18 @@ void nano::active_transactions::update_active_difficulty (nano::unique_lock 10 || (node.network_params.network.is_test_network () && !prioritized.empty ())) { - multiplier = nano::difficulty::to_multiplier (prioritized[prioritized.size () / 2], node.network_params.network.publish_thresholds.base); + multiplier = nano::difficulty::to_multiplier (prioritized[prioritized.size () / 2], node.network_params.network.publish_thresholds.epoch_1); } if (!prioritized.empty ()) { last_prioritized_difficulty = prioritized.back (); } } - debug_assert (multiplier >= 1); + debug_assert (multiplier >= nano::difficulty::to_multiplier (node.network_params.network.publish_thresholds.entry, node.network_params.network.publish_thresholds.epoch_1)); multipliers_cb.push_front (multiplier); auto sum (std::accumulate (multipliers_cb.begin (), multipliers_cb.end (), double(0))); - auto difficulty = nano::difficulty::from_multiplier (sum / multipliers_cb.size (), node.network_params.network.publish_thresholds.base); - debug_assert (difficulty >= node.network_params.network.publish_thresholds.base); + auto difficulty = nano::difficulty::from_multiplier (sum / multipliers_cb.size (), node.network_params.network.publish_thresholds.epoch_1); + debug_assert (difficulty >= node.network_params.network.publish_thresholds.entry); trended_active_difficulty = difficulty; node.observers.difficulty.notify (trended_active_difficulty); diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 18124dc0..e348b3bb 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -85,7 +85,7 @@ 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)); + debug_assert (!nano::work_validate_entry (*info_a.block)); 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); diff --git a/nano/node/bootstrap/bootstrap_bulk_pull.cpp b/nano/node/bootstrap/bootstrap_bulk_pull.cpp index 68b41672..fc050471 100644 --- a/nano/node/bootstrap/bootstrap_bulk_pull.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_pull.cpp @@ -213,7 +213,7 @@ void nano::bulk_pull_client::received_block (boost::system::error_code const & e { nano::bufferstream stream (connection->receive_buffer->data (), size_a); std::shared_ptr block (nano::deserialize_block (stream, type_a)); - if (block != nullptr && !nano::work_validate (*block)) + if (block != nullptr && !nano::work_validate_entry (*block)) { auto hash (block->hash ()); if (connection->node->config.logging.bulk_pull_logging ()) diff --git a/nano/node/bootstrap/bootstrap_bulk_push.cpp b/nano/node/bootstrap/bootstrap_bulk_push.cpp index ef53fa96..1ee7b9c2 100644 --- a/nano/node/bootstrap/bootstrap_bulk_push.cpp +++ b/nano/node/bootstrap/bootstrap_bulk_push.cpp @@ -232,7 +232,7 @@ void nano::bulk_push_server::received_block (boost::system::error_code const & e { nano::bufferstream stream (receive_buffer->data (), size_a); auto block (nano::deserialize_block (stream, type_a)); - if (block != nullptr && !nano::work_validate (*block)) + if (block != nullptr && !nano::work_validate_entry (*block)) { connection->node->process_active (std::move (block)); throttled_receive (); diff --git a/nano/node/common.cpp b/nano/node/common.cpp index a5e2f587..d2fd6e4f 100644 --- a/nano/node/common.cpp +++ b/nano/node/common.cpp @@ -431,7 +431,7 @@ void nano::message_parser::deserialize_publish (nano::stream & stream_a, nano::m nano::publish incoming (error, stream_a, header_a, digest_a, &block_uniquer); if (!error && at_end (stream_a)) { - if (!nano::work_validate (*incoming.block)) + if (!nano::work_validate_entry (*incoming.block)) { visitor.publish (incoming); } @@ -452,7 +452,7 @@ void nano::message_parser::deserialize_confirm_req (nano::stream & stream_a, nan nano::confirm_req incoming (error, stream_a, header_a, &block_uniquer); if (!error && at_end (stream_a)) { - if (incoming.block == nullptr || !nano::work_validate (*incoming.block)) + if (incoming.block == nullptr || !nano::work_validate_entry (*incoming.block)) { visitor.confirm_req (incoming); } @@ -478,7 +478,7 @@ void nano::message_parser::deserialize_confirm_ack (nano::stream & stream_a, nan if (!vote_block.which ()) { auto block (boost::get> (vote_block)); - if (nano::work_validate (*block)) + if (nano::work_validate_entry (*block)) { status = parse_status::insufficient_work; break; diff --git a/nano/node/distributed_work.cpp b/nano/node/distributed_work.cpp index 74368d79..a21771cd 100644 --- a/nano/node/distributed_work.cpp +++ b/nano/node/distributed_work.cpp @@ -107,8 +107,7 @@ void nano::distributed_work::start_local () { auto this_l (shared_from_this ()); local_generation_started = true; - node.work.generate ( - request.version, request.root, [this_l](boost::optional const & work_a) { + node.work.generate (request.version, request.root, request.difficulty, [this_l](boost::optional const & work_a) { if (work_a.is_initialized ()) { this_l->set_once (*work_a); @@ -122,8 +121,7 @@ void nano::distributed_work::start_local () } } this_l->stop_once (false); - }, - request.difficulty); + }); } void nano::distributed_work::do_request (nano::tcp_endpoint const & endpoint_a) @@ -323,7 +321,7 @@ void nano::distributed_work::set_once (uint64_t const work_a, std::string const if (node.config.logging.work_generation_time ()) { boost::format unformatted_l ("Work generation for %1%, with a threshold difficulty of %2% (multiplier %3%x) complete: %4% ms"); - auto multiplier_text_l (nano::to_string (nano::difficulty::to_multiplier (request.difficulty, nano::work_threshold (request.version)), 2)); + auto multiplier_text_l (nano::to_string (nano::difficulty::to_multiplier (request.difficulty, node.network_params.network.publish_thresholds.base), 2)); node.logger.try_log (boost::str (unformatted_l % request.root.to_string () % nano::to_string_hex (request.difficulty) % multiplier_text_l % elapsed.value ().count ())); } } diff --git a/nano/node/distributed_work_factory.cpp b/nano/node/distributed_work_factory.cpp index fba9261a..69703a54 100644 --- a/nano/node/distributed_work_factory.cpp +++ b/nano/node/distributed_work_factory.cpp @@ -11,7 +11,7 @@ nano::distributed_work_factory::~distributed_work_factory () stop (); } -bool nano::distributed_work_factory::make (nano::work_version const version_a, nano::root const & root_a, std::vector> const & peers_a, std::function)> const & callback_a, uint64_t difficulty_a, boost::optional const & account_a) +bool nano::distributed_work_factory::make (nano::work_version const version_a, nano::root const & root_a, std::vector> const & peers_a, uint64_t difficulty_a, std::function)> const & callback_a, boost::optional const & account_a) { return make (std::chrono::seconds (1), nano::work_request{ version_a, root_a, difficulty_a, account_a, callback_a, peers_a }); } diff --git a/nano/node/distributed_work_factory.hpp b/nano/node/distributed_work_factory.hpp index 56905dad..350b1735 100644 --- a/nano/node/distributed_work_factory.hpp +++ b/nano/node/distributed_work_factory.hpp @@ -22,7 +22,7 @@ class distributed_work_factory final public: distributed_work_factory (nano::node &); ~distributed_work_factory (); - bool make (nano::work_version const, nano::root const &, std::vector> const &, std::function)> const &, uint64_t, boost::optional const & = boost::none); + bool make (nano::work_version const, nano::root const &, std::vector> const &, uint64_t, std::function)> const &, boost::optional const & = boost::none); bool make (std::chrono::seconds const &, nano::work_request const &); void cancel (nano::root const &, bool const local_stop = false); void cleanup_finished (); diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index c567e6a7..50651816 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -716,7 +716,8 @@ void nano::json_handler::account_representative_set () auto info (rpc_l->account_info_impl (block_transaction, account)); if (!rpc_l->ec) { - if (nano::work_validate (nano::work_version::work_1, info.head, work)) + nano::block_details details (info.epoch (), false, false, false); + if (nano::work_difficulty (nano::work_version::work_1, info.head, work) < nano::work_threshold (nano::work_version::work_1, details)) { rpc_l->ec = nano::error_common::invalid_work; } @@ -909,10 +910,10 @@ void nano::json_handler::accounts_pending () void nano::json_handler::active_difficulty () { auto include_trend (request.get ("include_trend", false)); - response_l.put ("network_minimum", nano::to_string_hex (node.network_params.network.publish_thresholds.base)); + response_l.put ("network_minimum", nano::to_string_hex (node.network_params.network.publish_thresholds.epoch_1)); auto difficulty_active = node.active.active_difficulty (); response_l.put ("network_current", nano::to_string_hex (difficulty_active)); - auto multiplier = nano::difficulty::to_multiplier (difficulty_active, node.network_params.network.publish_thresholds.base); + auto multiplier = nano::difficulty::to_multiplier (difficulty_active, node.network_params.network.publish_thresholds.epoch_1); response_l.put ("multiplier", nano::to_string (multiplier)); if (include_trend) { @@ -1247,8 +1248,9 @@ void nano::json_handler::block_create () { std::string type (request.get ("type")); nano::wallet_id wallet (0); + auto difficulty_l (difficulty_optional_impl ()); boost::optional wallet_text (request.get_optional ("wallet")); - if (wallet_text.is_initialized ()) + if (!ec && wallet_text.is_initialized ()) { if (wallet.decode_hex (wallet_text.get ())) { @@ -1559,7 +1561,7 @@ void nano::json_handler::block_create () { if (work == 0) { - node.work_generate (work_version, root_l, get_callback_l (block_l), nano::account (pub)); + node.work_generate (work_version, root_l, difficulty_l, get_callback_l (block_l), nano::account (pub)); } else { @@ -2120,6 +2122,7 @@ void epoch_upgrader (std::shared_ptr node_a, nano::private_key const nano::account_info info; if (!node_a->store.account_get (transaction, i->account, info) && info.epoch () < epoch_a) { + auto difficulty (nano::work_threshold (nano::work_version::work_1, nano::block_details (epoch_a, false, false, true))); auto epoch = builder.state () .account (i->account) .previous (info.head) @@ -2127,10 +2130,10 @@ void epoch_upgrader (std::shared_ptr node_a, nano::private_key const .balance (info.balance) .link (link) .sign (raw_key, signer) - .work (node_a->work_generate_blocking (nano::work_version::work_1, info.head).value_or (0)) + .work (node_a->work_generate_blocking (nano::work_version::work_1, info.head, difficulty).value_or (0)) .build (); bool valid_signature (!nano::validate_message (signer, epoch->hash (), epoch->block_signature ())); - bool valid_work (!nano::work_validate (*epoch.get ())); + bool valid_work (epoch->difficulty () >= difficulty); nano::process_result result (nano::process_result::old); if (valid_signature && valid_work) { @@ -2179,6 +2182,7 @@ void epoch_upgrader (std::shared_ptr node_a, nano::private_key const if (info.epoch < epoch_a) { release_assert (nano::epochs::is_sequential (info.epoch, epoch_a)); + auto difficulty (nano::work_threshold (nano::work_version::work_1, nano::block_details (epoch_a, false, false, true))); auto epoch = builder.state () .account (key.account) .previous (0) @@ -2186,10 +2190,10 @@ void epoch_upgrader (std::shared_ptr node_a, nano::private_key const .balance (0) .link (link) .sign (raw_key, signer) - .work (node_a->work_generate_blocking (nano::work_version::work_1, key.account).value_or (0)) + .work (node_a->work_generate_blocking (nano::work_version::work_1, key.account, difficulty).value_or (0)) .build (); bool valid_signature (!nano::validate_message (signer, epoch->hash (), epoch->block_signature ())); - bool valid_work (!nano::work_validate (*epoch.get ())); + bool valid_work (epoch->difficulty () >= difficulty); nano::process_result result (nano::process_result::old); if (valid_signature && valid_work) { @@ -3255,7 +3259,7 @@ void nano::json_handler::process () } if (!rpc_l->ec) { - if (!nano::work_validate (*block)) + if (!nano::work_validate_entry (*block)) { auto result (rpc_l->node.process_local (block, watch_work_l)); switch (result.code) @@ -3321,6 +3325,11 @@ void nano::json_handler::process () } break; } + case nano::process_result::insufficient_work: + { + rpc_l->ec = nano::error_process::insufficient_work; + break; + } default: { rpc_l->ec = nano::error_process::other; @@ -3360,15 +3369,19 @@ void nano::json_handler::receive () { nano::account_info info; nano::root head; + nano::epoch epoch = block->sideband ().details.epoch; if (!node.store.account_get (block_transaction, account, info)) { head = info.head; + // When receiving, epoch version is the higher between the previous and the source blocks + epoch = std::max (info.epoch (), epoch); } else { head = account; } - if (nano::work_validate (nano::work_version::work_1, head, work)) + nano::block_details details (epoch, false, true, false); + if (nano::work_difficulty (nano::work_version::work_1, head, work) < nano::work_threshold (nano::work_version::work_1, details)) { ec = nano::error_common::invalid_work; } @@ -3711,7 +3724,8 @@ void nano::json_handler::send () } if (!ec && work) { - if (nano::work_validate (nano::work_version::work_1, info.head, work)) + nano::block_details details (info.epoch (), true, false, false); + if (nano::work_difficulty (nano::work_version::work_1, info.head, work) < nano::work_threshold (nano::work_version::work_1, details)) { ec = nano::error_common::invalid_work; } @@ -4938,7 +4952,7 @@ void nano::json_handler::work_generate () auto hash (hash_impl ()); auto difficulty (difficulty_optional_impl ()); multiplier_optional_impl (difficulty); - if (!ec && (difficulty > node.config.max_work_generate_difficulty || difficulty < node.network_params.network.publish_thresholds.base)) + if (!ec && (difficulty > node.config.max_work_generate_difficulty || difficulty < node.network_params.network.publish_thresholds.entry)) { ec = nano::error_rpc::difficulty_limit; } @@ -4956,7 +4970,7 @@ void nano::json_handler::work_generate () std::stringstream ostream; auto result_difficulty (nano::work_difficulty (work_version, hash, work)); response_l.put ("difficulty", nano::to_string_hex (result_difficulty)); - auto result_multiplier = nano::difficulty::to_multiplier (result_difficulty, nano::work_threshold (work_version)); + auto result_multiplier = nano::difficulty::to_multiplier (result_difficulty, node.network_params.network.publish_thresholds.base); response_l.put ("multiplier", nano::to_string (result_multiplier)); boost::property_tree::write_json (ostream, response_l); rpc_l->response (ostream.str ()); @@ -4970,7 +4984,7 @@ void nano::json_handler::work_generate () { if (node.local_work_generation_enabled ()) { - node.work.generate (work_version, hash, callback, difficulty); + node.work.generate (work_version, hash, difficulty, callback); } else { @@ -4992,7 +5006,7 @@ void nano::json_handler::work_generate () auto const & peers_l (secondary_work_peers_l ? node.config.secondary_work_peers : node.config.work_peers); if (node.work_generation_enabled (peers_l)) { - node.work_generate (work_version, hash, callback, difficulty, account, secondary_work_peers_l); + node.work_generate (work_version, hash, difficulty, callback, account, secondary_work_peers_l); } else { @@ -5071,7 +5085,7 @@ void nano::json_handler::work_validate () auto result_difficulty (nano::work_difficulty (work_version, hash, work)); response_l.put ("valid", (result_difficulty >= difficulty) ? "1" : "0"); response_l.put ("difficulty", nano::to_string_hex (result_difficulty)); - auto result_multiplier = nano::difficulty::to_multiplier (result_difficulty, nano::work_threshold (work_version)); + auto result_multiplier = nano::difficulty::to_multiplier (result_difficulty, node.network_params.network.publish_thresholds.base); response_l.put ("multiplier", nano::to_string (result_multiplier)); } response_errors (); diff --git a/nano/node/node.cpp b/nano/node/node.cpp index b263d64e..c3eed906 100644 --- a/nano/node/node.cpp +++ b/nano/node/node.cpp @@ -259,7 +259,7 @@ startup_time (std::chrono::steady_clock::now ()) if (this->websocket_server->any_subscriber (nano::websocket::topic::active_difficulty)) { nano::websocket::message_builder builder; - auto msg (builder.difficulty_changed (network_params.network.publish_thresholds.base, active_difficulty)); + auto msg (builder.difficulty_changed (network_params.network.publish_thresholds.epoch_1, active_difficulty)); this->websocket_server->broadcast (msg); } }); @@ -1011,11 +1011,6 @@ bool nano::node::work_generation_enabled (std::vector nano::node::work_generate_blocking (nano::block & block_a) -{ - return work_generate_blocking (block_a, network_params.network.publish_thresholds.base); -} - boost::optional nano::node::work_generate_blocking (nano::block & block_a, uint64_t difficulty_a) { auto opt_work_l (work_generate_blocking (block_a.work_version (), block_a.root (), difficulty_a, block_a.account ())); @@ -1026,37 +1021,33 @@ boost::optional nano::node::work_generate_blocking (nano::block & bloc return opt_work_l; } -void nano::node::work_generate (nano::work_version const version_a, nano::root const & root_a, std::function)> callback_a, boost::optional const & account_a) -{ - work_generate (version_a, root_a, callback_a, network_params.network.publish_thresholds.base, account_a); -} - -void nano::node::work_generate (nano::work_version const version_a, nano::root const & root_a, std::function)> callback_a, uint64_t difficulty_a, boost::optional const & account_a, bool secondary_work_peers_a) +void nano::node::work_generate (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::function)> callback_a, boost::optional const & account_a, bool secondary_work_peers_a) { auto const & peers_l (secondary_work_peers_a ? config.secondary_work_peers : config.work_peers); - if (distributed_work.make (version_a, root_a, peers_l, callback_a, difficulty_a, account_a)) + if (distributed_work.make (version_a, root_a, peers_l, difficulty_a, callback_a, account_a)) { // Error in creating the job (either stopped or work generation is not possible) callback_a (boost::none); } } -boost::optional nano::node::work_generate_blocking (nano::work_version const version_a, nano::root const & root_a, boost::optional const & account_a) -{ - return work_generate_blocking (version_a, root_a, network_params.network.publish_thresholds.base, account_a); -} - boost::optional nano::node::work_generate_blocking (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, boost::optional const & account_a) { std::promise> promise; work_generate ( - version_a, root_a, [&promise](boost::optional opt_work_a) { + version_a, root_a, difficulty_a, [&promise](boost::optional opt_work_a) { promise.set_value (opt_work_a); }, - difficulty_a, account_a); + account_a); return promise.get_future ().get (); } +boost::optional nano::node::work_generate_blocking (nano::block & block_a) +{ + debug_assert (network_params.network.is_test_network ()); + return work_generate_blocking (block_a, network_params.network.publish_thresholds.base); +} + boost::optional nano::node::work_generate_blocking (nano::root const & root_a) { debug_assert (network_params.network.is_test_network ()); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index a24bc728..81510eec 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -130,11 +130,8 @@ public: bool work_generation_enabled () const; bool work_generation_enabled (std::vector> const &) const; boost::optional work_generate_blocking (nano::block &, uint64_t); - boost::optional work_generate_blocking (nano::block &); boost::optional work_generate_blocking (nano::work_version const, nano::root const &, uint64_t, boost::optional const & = boost::none); - boost::optional work_generate_blocking (nano::work_version const, nano::root const &, boost::optional const & = boost::none); - void work_generate (nano::work_version const, nano::root const &, std::function)>, uint64_t, boost::optional const & = boost::none, bool const = false); - void work_generate (nano::work_version const, nano::root const &, std::function)>, boost::optional const & = boost::none); + void work_generate (nano::work_version const, nano::root const &, uint64_t, std::function)>, boost::optional const & = boost::none, bool const = false); void add_initial_peers (); void block_confirm (std::shared_ptr); bool block_confirmed_or_being_confirmed (nano::transaction const &, nano::block_hash const &); @@ -195,6 +192,8 @@ public: static double constexpr price_max = 16.0; static double constexpr free_cutoff = 1024.0; // For tests only + boost::optional work_generate_blocking (nano::block &); + // For tests only boost::optional work_generate_blocking (nano::root const &, uint64_t); // For tests only boost::optional work_generate_blocking (nano::root const &); diff --git a/nano/node/testing.cpp b/nano/node/testing.cpp index d546798e..947347f9 100644 --- a/nano/node/testing.cpp +++ b/nano/node/testing.cpp @@ -162,6 +162,38 @@ nano::account nano::system::account (nano::transaction const & transaction_a, si return nano::account (result); } +uint64_t nano::system::work_generate_limited (nano::block_hash const & root_a, uint64_t min_a, uint64_t max_a) +{ + uint64_t result = 0; + do + { + result = *work.generate (root_a, min_a); + } while (nano::work_difficulty (nano::work_version::work_1, root_a, result) >= max_a); + return result; +} + +std::unique_ptr nano::system::upgrade_genesis_epoch (nano::node & node_a, nano::epoch const epoch_a) +{ + bool error{ true }; + nano::state_block_builder builder; + std::error_code ec; + auto latest (node_a.latest (nano::test_genesis_key.pub)); + auto epoch = builder + .account (nano::test_genesis_key.pub) + .previous (latest) + .balance (node_a.balance (nano::test_genesis_key.pub)) + .link (node_a.ledger.epoch_link (epoch_a)) + .representative (nano::test_genesis_key.pub) + .sign (nano::test_genesis_key.prv, nano::test_genesis_key.pub) + .work (*work.generate (latest, nano::work_threshold (nano::work_version::work_1, nano::block_details (epoch_a, false, false, true)))) + .build (ec); + if (!ec && epoch) + { + error = node_a.process (*epoch).code != nano::process_result::progress; + } + return !error ? std::move (epoch) : nullptr; +} + void nano::system::deadline_set (std::chrono::duration const & delta_a) { deadline = std::chrono::steady_clock::now () + delta_a * deadline_scaling_factor; diff --git a/nano/node/testing.hpp b/nano/node/testing.hpp index 4b1a860d..1780b362 100644 --- a/nano/node/testing.hpp +++ b/nano/node/testing.hpp @@ -31,8 +31,11 @@ public: void generate_receive (nano::node &); void generate_send_new (nano::node &, std::vector &); void generate_send_existing (nano::node &, std::vector &); + std::unique_ptr upgrade_genesis_epoch (nano::node &, nano::epoch const); std::shared_ptr wallet (size_t); nano::account account (nano::transaction const &, size_t); + /** Generate work with difficulty between \p min_difficulty_a (inclusive) and \p max_difficulty_a (exclusive) */ + uint64_t work_generate_limited (nano::block_hash const & root_a, uint64_t min_difficulty_a, uint64_t max_difficulty_a); /** * Polls, sleep if there's no work to be done (default 50ms), then check the deadline * @returns 0 or nano::deadline_expired diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 07153afa..25061062 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -935,6 +935,8 @@ std::shared_ptr nano::wallet::receive_action (nano::block const & s nano::account account; auto hash (send_a.hash ()); std::shared_ptr block; + nano::block_details details; + details.is_receive = true; if (wallets.node.config.receive_minimum.number () <= amount_a.number ()) { auto block_transaction (wallets.node.ledger.store.tx_begin_read ()); @@ -957,10 +959,12 @@ std::shared_ptr nano::wallet::receive_action (nano::block const & s if (!new_account) { block = std::make_shared (account, info.head, info.representative, info.balance.number () + pending_info.amount.number (), hash, prv, account, work_a); + details.epoch = std::max (info.epoch (), send_a.sideband ().details.epoch); } else { block = std::make_shared (account, 0, representative_a, pending_info.amount, reinterpret_cast (hash), prv, account, work_a); + details.epoch = send_a.sideband ().details.epoch; } } else @@ -985,7 +989,7 @@ std::shared_ptr nano::wallet::receive_action (nano::block const & s } if (block != nullptr) { - if (action_complete (block, account, generate_work_a)) + if (action_complete (block, account, generate_work_a, details)) { // Return null block after work generation or ledger process error block = nullptr; @@ -997,6 +1001,7 @@ std::shared_ptr nano::wallet::receive_action (nano::block const & s std::shared_ptr nano::wallet::change_action (nano::account const & source_a, nano::account const & representative_a, uint64_t work_a, bool generate_work_a) { std::shared_ptr block; + nano::block_details details; { auto transaction (wallets.tx_begin_read ()); auto block_transaction (wallets.node.store.tx_begin_read ()); @@ -1018,12 +1023,13 @@ std::shared_ptr nano::wallet::change_action (nano::account const & store.work_get (transaction, source_a, work_a); } block = std::make_shared (source_a, info.head, representative_a, info.balance, 0, prv, source_a, work_a); + details.epoch = info.epoch (); } } } if (block != nullptr) { - if (action_complete (block, source_a, generate_work_a)) + if (action_complete (block, source_a, generate_work_a, details)) { // Return null block after work generation or ledger process error block = nullptr; @@ -1045,6 +1051,8 @@ std::shared_ptr nano::wallet::send_action (nano::account const & so auto error (false); auto cached_block (false); std::shared_ptr block; + nano::block_details details; + details.is_send = true; if (id_mdb_val) { nano::mdb_val result; @@ -1087,6 +1095,7 @@ std::shared_ptr nano::wallet::send_action (nano::account const & so store.work_get (transaction, source_a, work_a); } block = std::make_shared (source_a, info.head, info.representative, balance - amount_a, account_a, prv, source_a, work_a); + details.epoch = info.epoch (); if (id_mdb_val && block != nullptr) { auto status (mdb_put (wallets.env.tx (transaction), wallets.node.wallets.send_action_ids, *id_mdb_val, nano::mdb_val (block->hash ()), 0)); @@ -1100,10 +1109,10 @@ std::shared_ptr nano::wallet::send_action (nano::account const & so } } } - return std::make_tuple (block, error, cached_block); + return std::make_tuple (block, error, cached_block, details); }; - std::tuple, bool, bool> result; + std::tuple, bool, bool, nano::block_details> result; { if (id_mdb_val) { @@ -1118,11 +1127,12 @@ std::shared_ptr nano::wallet::send_action (nano::account const & so std::shared_ptr block; bool error; bool cached_block; - std::tie (block, error, cached_block) = result; + nano::block_details details; + std::tie (block, error, cached_block, details) = result; if (!error && block != nullptr && !cached_block) { - if (action_complete (block, source_a, generate_work_a)) + if (action_complete (block, source_a, generate_work_a, details)) { // Return null block after work generation or ledger process error block = nullptr; @@ -1131,19 +1141,23 @@ std::shared_ptr nano::wallet::send_action (nano::account const & so return block; } -bool nano::wallet::action_complete (std::shared_ptr const & block_a, nano::account const & account_a, bool const generate_work_a) +bool nano::wallet::action_complete (std::shared_ptr const & block_a, nano::account const & account_a, bool const generate_work_a, nano::block_details const & details_a) { bool error{ false }; if (block_a != nullptr) { - if (nano::work_validate (*block_a)) + auto required_difficulty{ nano::work_threshold (block_a->work_version (), details_a) }; + if (block_a->difficulty () < required_difficulty) { wallets.node.logger.try_log (boost::str (boost::format ("Cached or provided work for block %1% account %2% is invalid, regenerating") % block_a->hash ().to_string () % account_a.to_account ())); - error = !wallets.node.work_generate_blocking (*block_a, wallets.node.active.limited_active_difficulty ()).is_initialized (); + debug_assert (required_difficulty <= wallets.node.config.max_work_generate_difficulty); + auto target_difficulty = std::max (required_difficulty, wallets.node.active.limited_active_difficulty ()); + error = !wallets.node.work_generate_blocking (*block_a, target_difficulty).is_initialized (); } if (!error) { error = wallets.node.process_local (block_a, true).code != nano::process_result::progress; + debug_assert (error || block_a->sideband ().details == details_a); } if (!error && generate_work_a) { @@ -1219,7 +1233,7 @@ void nano::wallet::send_async (nano::account const & source_a, nano::account con // Update work for account if latest root is root_a void nano::wallet::work_update (nano::transaction const & transaction_a, nano::account const & account_a, nano::root const & root_a, uint64_t work_a) { - debug_assert (!nano::work_validate (nano::work_version::work_1, root_a, work_a)); + debug_assert (!nano::work_validate_entry (nano::work_version::work_1, root_a, work_a)); debug_assert (store.exists (transaction_a, account_a)); auto block_transaction (wallets.node.store.tx_begin_read ()); auto latest (wallets.node.ledger.latest_root (block_transaction, account_a)); @@ -1369,7 +1383,7 @@ void nano::wallet::work_cache_blocking (nano::account const & account_a, nano::r { if (wallets.node.work_generation_enabled ()) { - auto opt_work_l (wallets.node.work_generate_blocking (nano::work_version::work_1, root_a, account_a)); + auto opt_work_l (wallets.node.work_generate_blocking (nano::work_version::work_1, root_a, wallets.node.network_params.network.publish_thresholds.base, account_a)); if (opt_work_l.is_initialized ()) { auto transaction_l (wallets.tx_begin_write ()); @@ -1444,7 +1458,7 @@ void nano::work_watcher::watching (nano::qualified_root const & root_a, std::sha if (active_difficulty > block_a->difficulty () && watcher_l->node.work_generation_enabled ()) { watcher_l->node.work_generate ( - block_a->work_version (), block_a->root (), [watcher_l, block_a, root_a](boost::optional work_a) { + block_a->work_version (), block_a->root (), active_difficulty, [watcher_l, block_a, root_a](boost::optional work_a) { if (block_a != nullptr && watcher_l != nullptr && !watcher_l->stopped) { bool updated_l{ false }; @@ -1469,7 +1483,7 @@ void nano::work_watcher::watching (nano::qualified_root const & root_a, std::sha } } }, - active_difficulty, block_a->account ()); + block_a->account ()); } else { diff --git a/nano/node/wallet.hpp b/nano/node/wallet.hpp index aa5577a0..cdd00959 100644 --- a/nano/node/wallet.hpp +++ b/nano/node/wallet.hpp @@ -124,7 +124,7 @@ public: std::shared_ptr change_action (nano::account const &, nano::account const &, uint64_t = 0, bool = true); std::shared_ptr receive_action (nano::block const &, nano::account const &, nano::uint128_union const &, uint64_t = 0, bool = true); std::shared_ptr send_action (nano::account const &, nano::account const &, nano::uint128_t const &, uint64_t = 0, bool = true, boost::optional = {}); - bool action_complete (std::shared_ptr const &, nano::account const &, bool const); + bool action_complete (std::shared_ptr const &, nano::account const &, bool const, nano::block_details const &); wallet (bool &, nano::transaction &, nano::wallets &, std::string const &); wallet (bool &, nano::transaction &, nano::wallets &, std::string const &, std::string const &); void enter_initial_password (); diff --git a/nano/node/websocket.cpp b/nano/node/websocket.cpp index f8727d51..9a0285ce 100644 --- a/nano/node/websocket.cpp +++ b/nano/node/websocket.cpp @@ -797,7 +797,7 @@ nano::websocket::message nano::websocket::message_builder::work_generation (nano request_l.put ("version", nano::to_string (version_a)); request_l.put ("hash", root_a.to_string ()); request_l.put ("difficulty", nano::to_string_hex (difficulty_a)); - auto request_multiplier_l (nano::difficulty::to_multiplier (difficulty_a, nano::work_threshold (version_a))); + auto request_multiplier_l (nano::difficulty::to_multiplier (difficulty_a, publish_threshold_a)); request_l.put ("multiplier", nano::to_string (request_multiplier_l)); work_l.add_child ("request", request_l); @@ -808,7 +808,7 @@ nano::websocket::message nano::websocket::message_builder::work_generation (nano result_l.put ("work", nano::to_string_hex (work_a)); auto result_difficulty_l (nano::work_difficulty (version_a, root_a, work_a)); result_l.put ("difficulty", nano::to_string_hex (result_difficulty_l)); - auto result_multiplier_l (nano::difficulty::to_multiplier (result_difficulty_l, nano::work_threshold (version_a))); + auto result_multiplier_l (nano::difficulty::to_multiplier (result_difficulty_l, publish_threshold_a)); result_l.put ("multiplier", nano::to_string (result_multiplier_l)); work_l.add_child ("result", result_l); } diff --git a/nano/qt/qt.cpp b/nano/qt/qt.cpp index 60cac071..dcf10eda 100644 --- a/nano/qt/qt.cpp +++ b/nano/qt/qt.cpp @@ -2002,7 +2002,7 @@ wallet (wallet_a) { show_label_ok (*status); this->status->setText (""); - if (!nano::work_validate (*block_l)) + if (!nano::work_validate_entry (*block_l)) { this->wallet.node.process_active (std::move (block_l)); } diff --git a/nano/qt_test/qt.cpp b/nano/qt_test/qt.cpp index 8e3c83b8..b6875737 100644 --- a/nano/qt_test/qt.cpp +++ b/nano/qt_test/qt.cpp @@ -727,7 +727,7 @@ TEST (wallet, seed_work_generation) ASSERT_NO_ERROR (ec); } auto transaction (system.nodes[0]->store.tx_begin_read ()); - ASSERT_FALSE (nano::work_validate (nano::work_version::work_1, system.nodes[0]->ledger.latest_root (transaction, pub), work)); + ASSERT_GE (nano::work_difficulty (nano::work_version::work_1, system.nodes[0]->ledger.latest_root (transaction, pub), work), nano::work_threshold_base (nano::work_version::work_1)); } TEST (wallet, backup_seed) diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index c1fcbcdb..11a7bc1e 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -619,6 +619,55 @@ TEST (rpc, send_idempotent) ASSERT_EQ (std::error_code (nano::error_common::insufficient_balance).message (), response3.json.get ("error")); } +TEST (rpc, send_epoch_2) +{ + nano::system system; + auto & node = *add_ipc_enabled_node (system); + + // Upgrade the genesis account to epoch 2 + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_1)); + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_2)); + + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv, false); + + auto target_difficulty = nano::work_threshold (nano::work_version::work_1, nano::block_details (nano::epoch::epoch_2, true, false, false)); + ASSERT_LT (node.network_params.network.publish_thresholds.entry, target_difficulty); + auto min_difficulty = node.network_params.network.publish_thresholds.entry; + + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node.config.ipc_config.transport_tcp.port; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + boost::property_tree::ptree request; + std::string wallet; + node.wallets.items.begin ()->first.encode_hex (wallet); + request.put ("wallet", wallet); + request.put ("action", "send"); + request.put ("source", nano::test_genesis_key.pub.to_account ()); + request.put ("destination", nano::keypair ().pub.to_account ()); + request.put ("amount", "1"); + + // Test that the correct error is given if there is insufficient work + auto insufficient = system.work_generate_limited (nano::genesis_hash, min_difficulty, target_difficulty); + request.put ("work", nano::to_string_hex (insufficient)); + { + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + std::error_code ec (nano::error_common::invalid_work); + ASSERT_EQ (1, response.json.count ("error")); + ASSERT_EQ (response.json.get ("error"), ec.message ()); + } +} + TEST (rpc, stop) { nano::system system; @@ -1789,7 +1838,7 @@ TEST (rpc, process_block_with_work_watcher) auto latest (node1.latest (nano::test_genesis_key.pub)); auto send (std::make_shared (nano::test_genesis_key.pub, latest, nano::test_genesis_key.pub, nano::genesis_amount - 100, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (latest))); auto difficulty1 (send->difficulty ()); - auto multiplier1 = nano::difficulty::to_multiplier (difficulty1, nano::work_threshold (send->work_version ())); + auto multiplier1 = nano::difficulty::to_multiplier (difficulty1, node1.active.active_difficulty ()); nano::node_rpc_config node_rpc_config; nano::ipc::ipc_server ipc_server (node1, node_rpc_config); nano::rpc_config rpc_config (nano::get_available_port (), true); @@ -2003,14 +2052,12 @@ TEST (rpc, process_subtype_open) { nano::system system; auto & node1 = *add_ipc_enabled_node (system); - system.add_node (); + auto & node2 = *system.add_node (); nano::keypair key; auto latest (node1.latest (nano::test_genesis_key.pub)); nano::state_block send (nano::genesis_account, latest, nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node1.work_generate_blocking (latest)); - { - auto transaction (node1.store.tx_begin_write ()); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, send).code); - } + ASSERT_EQ (nano::process_result::progress, node1.process (send).code); + ASSERT_EQ (nano::process_result::progress, node2.process (send).code); scoped_io_thread_name_change scoped_thread_name_io; node1.active.insert (std::make_shared (send)); nano::state_block open (key.pub, 0, key.pub, nano::Gxrb_ratio, send.hash (), key.prv, key.pub, *node1.work_generate_blocking (key.pub)); @@ -2053,7 +2100,8 @@ TEST (rpc, process_subtype_open) ASSERT_EQ (200, response3.status); ASSERT_EQ (open.hash ().to_string (), response3.json.get ("hash")); system.deadline_set (10s); - while (system.nodes[1]->latest (key.pub) != open.hash ()) + auto now (std::chrono::steady_clock::now ()); + while (node2.latest (key.pub) != open.hash ()) { ASSERT_NO_ERROR (system.poll ()); } @@ -2063,13 +2111,11 @@ TEST (rpc, process_subtype_receive) { nano::system system; auto & node1 = *add_ipc_enabled_node (system); - system.add_node (); + auto & node2 = *system.add_node (); auto latest (node1.latest (nano::test_genesis_key.pub)); nano::state_block send (nano::genesis_account, latest, nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node1.work_generate_blocking (latest)); - { - auto transaction (node1.store.tx_begin_write ()); - ASSERT_EQ (nano::process_result::progress, node1.ledger.process (transaction, send).code); - } + ASSERT_EQ (nano::process_result::progress, node1.process (send).code); + ASSERT_EQ (nano::process_result::progress, node2.process (send).code); scoped_io_thread_name_change scoped_thread_name_io; node1.active.insert (std::make_shared (send)); nano::state_block receive (nano::test_genesis_key.pub, send.hash (), nano::test_genesis_key.pub, nano::genesis_amount, send.hash (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *node1.work_generate_blocking (send.hash ())); @@ -2113,12 +2159,49 @@ TEST (rpc, process_subtype_receive) ASSERT_EQ (200, response3.status); ASSERT_EQ (receive.hash ().to_string (), response3.json.get ("hash")); system.deadline_set (10s); - while (system.nodes[1]->latest (nano::test_genesis_key.pub) != receive.hash ()) + while (node2.latest (nano::test_genesis_key.pub) != receive.hash ()) { ASSERT_NO_ERROR (system.poll ()); } } +TEST (rpc, process_ledger_insufficient_work) +{ + nano::system system; + auto & node = *add_ipc_enabled_node (system); + ASSERT_LT (node.network_params.network.publish_thresholds.entry, node.network_params.network.publish_thresholds.epoch_1); + auto latest (node.latest (nano::test_genesis_key.pub)); + auto min_difficulty = node.network_params.network.publish_thresholds.entry; + auto max_difficulty = node.network_params.network.publish_thresholds.epoch_1; + nano::state_block send (nano::genesis_account, latest, nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, nano::test_genesis_key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work_generate_limited (latest, min_difficulty, max_difficulty)); + ASSERT_LT (send.difficulty (), max_difficulty); + ASSERT_GE (send.difficulty (), min_difficulty); + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node.config.ipc_config.transport_tcp.port; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + boost::property_tree::ptree request; + request.put ("action", "process"); + std::string json; + send.serialize_json (json); + request.put ("block", json); + request.put ("subtype", "send"); + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + std::error_code ec (nano::error_process::insufficient_work); + ASSERT_EQ (1, response.json.count ("error")); + ASSERT_EQ (response.json.get ("error"), ec.message ()); +} + TEST (rpc, keepalive) { nano::system system; @@ -2221,13 +2304,13 @@ TEST (rpc, payment_begin_end) root1 = node1->ledger.latest_root (transaction, account); } uint64_t work (0); - while (!nano::work_validate (nano::work_version::work_1, root1, work)) + while (nano::work_difficulty (nano::work_version::work_1, root1, work) >= nano::work_threshold_base (nano::work_version::work_1)) { ++work; ASSERT_LT (work, 50); } system.deadline_set (10s); - while (nano::work_validate (nano::work_version::work_1, root1, work)) + while (nano::work_difficulty (nano::work_version::work_1, root1, work) < nano::work_threshold_base (nano::work_version::work_1)) { auto ec = system.poll (); auto transaction (wallet->wallets.tx_begin_read ()); @@ -2882,7 +2965,7 @@ TEST (rpc, work_generate_difficulty) ASSERT_EQ (result_difficulty, response_difficulty); auto multiplier = response.json.get ("multiplier"); // Expected multiplier from base threshold, not from the given difficulty - ASSERT_EQ (nano::difficulty::to_multiplier (result_difficulty, node->network_params.network.publish_thresholds.base), multiplier); + ASSERT_NEAR (nano::difficulty::to_multiplier (result_difficulty, node->network_params.network.publish_thresholds.base), multiplier, 1e-10); ASSERT_GE (result_difficulty, difficulty); } { @@ -2920,7 +3003,7 @@ TEST (rpc, work_generate_multiplier) { nano::system system; nano::node_config node_config (nano::get_available_port (), system.logging); - node_config.max_work_generate_difficulty = 0xffff000000000000; + node_config.max_work_generate_difficulty = 0xfffff00000000000; auto node = add_ipc_enabled_node (system, node_config); scoped_io_thread_name_change scoped_thread_name_io; nano::node_rpc_config node_rpc_config; @@ -2947,9 +3030,14 @@ TEST (rpc, work_generate_multiplier) ASSERT_NO_ERROR (system.poll ()); } ASSERT_EQ (200, response.status); - auto work_text (response.json.get ("work")); + auto work_text (response.json.get_optional ("work")); + if (!work_text) + { + std::cout << response.json.get ("error") << std::endl; + } + ASSERT_TRUE (work_text.is_initialized ()); uint64_t work; - ASSERT_FALSE (nano::from_string_hex (work_text, work)); + ASSERT_FALSE (nano::from_string_hex (*work_text, work)); auto result_difficulty (nano::work_difficulty (nano::work_version::work_1, hash, work)); auto response_difficulty_text (response.json.get ("difficulty")); uint64_t response_difficulty; @@ -3008,7 +3096,7 @@ TEST (rpc, work_cancel) system.deadline_set (10s); while (!done) { - system.work.generate (hash1, [&done](boost::optional work_a) { + system.work.generate (nano::work_version::work_1, hash1, node1.network_params.network.publish_thresholds.base, [&done](boost::optional work_a) { done = !work_a; }); test_response response1 (request1, rpc.config.port, system.io_ctx); @@ -3041,12 +3129,12 @@ TEST (rpc, work_peer_bad) node2.config.work_peers.push_back (std::make_pair (boost::asio::ip::address_v6::any ().to_string (), 0)); nano::block_hash hash1 (1); std::atomic work (0); - node2.work_generate (nano::work_version::work_1, hash1, [&work](boost::optional work_a) { + node2.work_generate (nano::work_version::work_1, hash1, node2.network_params.network.publish_thresholds.base, [&work](boost::optional work_a) { ASSERT_TRUE (work_a.is_initialized ()); work = *work_a; }); system.deadline_set (5s); - while (nano::work_validate (nano::work_version::work_1, hash1, work)) + while (nano::work_difficulty (nano::work_version::work_1, hash1, work) < nano::work_threshold_base (nano::work_version::work_1)) { ASSERT_NO_ERROR (system.poll ()); } @@ -3070,13 +3158,13 @@ TEST (rpc, work_peer_one) rpc.start (); node2.config.work_peers.push_back (std::make_pair (node1.network.endpoint ().address ().to_string (), rpc.config.port)); nano::keypair key1; - uint64_t work (0); - node2.work_generate (nano::work_version::work_1, key1.pub, [&work](boost::optional work_a) { + std::atomic work (0); + node2.work_generate (nano::work_version::work_1, key1.pub, node1.network_params.network.publish_thresholds.base, [&work](boost::optional work_a) { ASSERT_TRUE (work_a.is_initialized ()); work = *work_a; }); system.deadline_set (5s); - while (nano::work_validate (nano::work_version::work_1, key1.pub, work)) + while (nano::work_difficulty (nano::work_version::work_1, key1.pub, work) < nano::work_threshold_base (nano::work_version::work_1)) { ASSERT_NO_ERROR (system.poll ()); } @@ -3118,10 +3206,10 @@ TEST (rpc, work_peer_many) for (auto i (0); i < works.size (); ++i) { nano::keypair key1; - node1.work_generate (nano::work_version::work_1, key1.pub, [& work = works[i]](boost::optional work_a) { + node1.work_generate (nano::work_version::work_1, key1.pub, node1.network_params.network.publish_thresholds.base, [& work = works[i]](boost::optional work_a) { work = *work_a; }); - while (nano::work_validate (nano::work_version::work_1, key1.pub, works[i])) + while (nano::work_difficulty (nano::work_version::work_1, key1.pub, works[i]) < nano::work_threshold_base (nano::work_version::work_1)) { system1.poll (); system2.poll (); @@ -3571,6 +3659,54 @@ TEST (rpc, account_representative_set_work_disabled) } } +TEST (rpc, account_representative_set_epoch_2) +{ + nano::system system; + auto & node = *add_ipc_enabled_node (system); + + // Upgrade the genesis account to epoch 2 + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_1)); + ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node, nano::epoch::epoch_2)); + + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv, false); + + auto target_difficulty = nano::work_threshold (nano::work_version::work_1, nano::block_details (nano::epoch::epoch_2, false, false, false)); + ASSERT_LT (node.network_params.network.publish_thresholds.entry, target_difficulty); + auto min_difficulty = node.network_params.network.publish_thresholds.entry; + + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node.config.ipc_config.transport_tcp.port; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + boost::property_tree::ptree request; + std::string wallet; + node.wallets.items.begin ()->first.encode_hex (wallet); + request.put ("wallet", wallet); + request.put ("action", "account_representative_set"); + request.put ("account", nano::test_genesis_key.pub.to_account ()); + request.put ("representative", nano::keypair ().pub.to_account ()); + + // Test that the correct error is given if there is insufficient work + auto insufficient = system.work_generate_limited (nano::genesis_hash, min_difficulty, target_difficulty); + request.put ("work", nano::to_string_hex (insufficient)); + { + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + std::error_code ec (nano::error_common::invalid_work); + ASSERT_EQ (1, response.json.count ("error")); + ASSERT_EQ (response.json.get ("error"), ec.message ()); + } +} + TEST (rpc, bootstrap) { nano::system system0; @@ -5775,7 +5911,7 @@ TEST (rpc, block_create_state_request_work) boost::property_tree::read_json (block_stream, block_l); auto block (nano::deserialize_block_json (block_l)); ASSERT_NE (nullptr, block); - ASSERT_FALSE (nano::work_validate (*block)); + ASSERT_GE (block->difficulty (), nano::work_threshold_base (nano::work_version::work_1)); } } @@ -7248,13 +7384,13 @@ TEST (rpc, active_difficulty) auto network_minimum_text (response.json.get ("network_minimum")); uint64_t network_minimum; ASSERT_FALSE (nano::from_string_hex (network_minimum_text, network_minimum)); - ASSERT_EQ (node->network_params.network.publish_thresholds.base, network_minimum); + ASSERT_EQ (node->network_params.network.publish_thresholds.epoch_1, network_minimum); auto multiplier (response.json.get ("multiplier")); ASSERT_NEAR (expected_multiplier, multiplier, 1e-6); auto network_current_text (response.json.get ("network_current")); uint64_t network_current; ASSERT_FALSE (nano::from_string_hex (network_current_text, network_current)); - ASSERT_EQ (nano::difficulty::from_multiplier (expected_multiplier, node->network_params.network.publish_thresholds.base), network_current); + ASSERT_EQ (nano::difficulty::from_multiplier (expected_multiplier, node->network_params.network.publish_thresholds.epoch_1), network_current); ASSERT_EQ (response.json.not_found (), response.json.find ("difficulty_trend")); } // Test include_trend optional diff --git a/nano/secure/ledger.cpp b/nano/secure/ledger.cpp index 2539a7a8..ef60eaac 100644 --- a/nano/secure/ledger.cpp +++ b/nano/secure/ledger.cpp @@ -783,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 (network_params.network.is_test_network () || !nano::work_validate (block_a)); + debug_assert (!nano::work_validate_entry (block_a) || network_params.network.is_test_network ()); ledger_processor processor (*this, transaction_a, verification); block_a.visit (processor); if (processor.result.code == nano::process_result::progress)