From f9e6d8e81855a4802fde0ccaa256f921360357fb Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Thu, 26 Mar 2020 14:05:35 +0000 Subject: [PATCH] Handle epoch_2 work thresholds in the wallet and most RPCs (#2671) * Handle epoch_2 work thresholds in most RPCs This PR is quite large with work generation and validation being a central piece of the node. In lib/config, work thresholds have been added for the beta and test network. They are now placed under a `nano::work_thresholds`. Unlike the main network, the thresholds are 2x and 1/2x from the base threshold. For tests, the previous base threshold was lowered such that the new highest difficulty (2x) is the same as before. The reason for this is that work generation targets the highest difficulty. `nano::work_validate` was renamed to `work_validate_entry`, further validation must be done by using `work_threshold_entry` and `work_threshold_full`. Work generation was changed to always require `difficulty`, except for tests. The reason is that any caller should not assume a base difficulty anymore. These RPCs now fully support the new thresholds and are ready for epoch_2 (will add some tests): - `process` validates at the minimum, entry difficulty, and handles the new `insufficient_work` result from ledger processing (used in https://github.com/nanocurrency/nano-node/pull/2667) - `send` and `account_representative_set` get the account epoch version and targets the correct difficulty - `receive` aditionally checks if the `source` epoch is higher, allowing new epoch propagation - `epoch_upgrader` These RPCs have changes to be compatible but are not yet optimal: - `block_create` now accepts an optional `difficulty`, and will generate at the highest difficulty otherwise (8x on mainnet). - `work_generate` also generates at the highest difficulty if `difficulty` is not specified, and multipliers are now off the highest difficulty. - `work_validate` validates at the highest difficulty unless `difficulty` is specified, meaning this is a breeaking change and it can return "not valid" for valid blocks. Multipliers are now off the highest difficulty. Support is also limited for active difficulty. It is currently calculated off the `epoch_1` difficulty to avoid any changes. All these cases will be handled in separate PRs, as this one is large enough as-is. * Define in source file * Fix websocket active_difficulty * Extract system::work_generate_limited * Fix debug_asserts in active_transactions::update_active_difficulty and add a test to ensure it * Add disabled test for RPC process, to be enabled in https://github.com/nanocurrency/nano-node/pull/2667 * Add auxiliary system::upgrade_genesis_epoch_2 * Set sideband block details for legacy blocks, unused for now * Higher amplitude between thresholds for the test network for easier testing * Handle work thresholds in the node wallet * Tests validating the node wallet handles thresholds * Final tests and adjustments * Fix ASSERT_NE * Add test ensuring the reduced work is also used when opening accounts previously upgraded * Ensure blocks from wallet meet the minimum difficulty * No need to change sideband for legacy, since the wallet never uses them * Enable test rpc.process_ledger_insufficient_work * Fix a couple of RPC tests being slow due to testing unrelated functionality --- nano/core_test/active_transactions.cpp | 34 +++- nano/core_test/difficulty.cpp | 71 ++++++- nano/core_test/distributed_work.cpp | 36 ++-- nano/core_test/fakes/work_peer.hpp | 9 +- nano/core_test/node.cpp | 33 +++- nano/core_test/wallet.cpp | 161 +++++++++++++++- nano/core_test/websocket.cpp | 4 +- nano/core_test/work_pool.cpp | 22 ++- nano/lib/config.cpp | 10 +- nano/lib/errors.cpp | 2 + nano/lib/errors.hpp | 1 + nano/lib/work.cpp | 78 ++++---- nano/lib/work.hpp | 31 ++-- nano/nano_node/entry.cpp | 25 +-- nano/node/active_transactions.cpp | 14 +- nano/node/blockprocessor.cpp | 2 +- nano/node/bootstrap/bootstrap_bulk_pull.cpp | 2 +- nano/node/bootstrap/bootstrap_bulk_push.cpp | 2 +- nano/node/common.cpp | 6 +- nano/node/distributed_work.cpp | 8 +- nano/node/distributed_work_factory.cpp | 2 +- nano/node/distributed_work_factory.hpp | 2 +- nano/node/json_handler.cpp | 48 +++-- nano/node/node.cpp | 31 ++-- nano/node/node.hpp | 7 +- nano/node/testing.cpp | 32 ++++ nano/node/testing.hpp | 3 + nano/node/wallet.cpp | 40 ++-- nano/node/wallet.hpp | 2 +- nano/node/websocket.cpp | 4 +- nano/qt/qt.cpp | 2 +- nano/qt_test/qt.cpp | 2 +- nano/rpc_test/rpc.cpp | 196 +++++++++++++++++--- nano/secure/ledger.cpp | 2 +- 34 files changed, 680 insertions(+), 244 deletions(-) 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)