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
This commit is contained in:
Guilherme Lawless 2020-03-26 14:05:35 +00:00 committed by GitHub
commit f9e6d8e818
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 680 additions and 244 deletions

View file

@ -5,6 +5,8 @@
#include <gtest/gtest.h>
#include <numeric>
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::state_block> (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::state_block> (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<nano::state_block> (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<nano::state_block> (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<nano::state_block> (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<nano::state_block> (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::state_block> (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::state_block> (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<nano::state_block> (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<nano::state_block> (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<nano::state_block> (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<nano::state_block> (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<std::mutex> 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);
}

View file

@ -1,8 +1,28 @@
#include <nano/lib/blocks.hpp>
#include <nano/lib/config.hpp>
#include <nano/lib/epoch.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/work.hpp>
#include <nano/node/testing.hpp>
#include <gtest/gtest.h>
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)));
}

View file

@ -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<fake_work_peer> (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);
}

View file

@ -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<uint64_t> work_a) {
work_pool.generate (version, hash, request_difficulty, [this_l, hash](boost::optional<uint64_t> 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);

View file

@ -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<uint64_t> 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<boost::asio::io_context> ());
@ -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<nano::send_block> (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<std::mutex> 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 ();
}

View file

@ -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<nano::uint128_t>::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<std::mutex> 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<std::mutex> guard (node.active.mutex);
node.active.trended_active_difficulty = std::numeric_limits<uint64_t>::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);
}

View file

@ -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<std::string> ("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<std::string> ("network_current"), network_current);
ASSERT_EQ (network_current, node1->active.active_difficulty ());
double multiplier = message_contents.get<double> ("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

View file

@ -20,7 +20,7 @@ TEST (work, one)
nano::work_pool pool (std::numeric_limits<unsigned>::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<unsigned>::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<uint64_t> work_a) {
pool.generate (
nano::work_version::work_1, key, nano::network_constants ().publish_thresholds.base, [&done](boost::optional<uint64_t> 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<uint64_t>) {});
pool.generate (key2, [](boost::optional<uint64_t>) {});
pool.generate (key3, [](boost::optional<uint64_t>) {});
pool.generate (key4, [](boost::optional<uint64_t>) {});
pool.generate (key5, [](boost::optional<uint64_t>) {});
pool.generate (key6, [](boost::optional<uint64_t>) {});
nano::network_constants constants;
pool.generate (nano::work_version::work_1, key1, constants.publish_thresholds.base, [](boost::optional<uint64_t>) {});
pool.generate (nano::work_version::work_1, key2, constants.publish_thresholds.base, [](boost::optional<uint64_t>) {});
pool.generate (nano::work_version::work_1, key3, constants.publish_thresholds.base, [](boost::optional<uint64_t>) {});
pool.generate (nano::work_version::work_1, key4, constants.publish_thresholds.base, [](boost::optional<uint64_t>) {});
pool.generate (nano::work_version::work_1, key5, constants.publish_thresholds.base, [](boost::optional<uint64_t>) {});
pool.generate (nano::work_version::work_1, key6, constants.publish_thresholds.base, [](boost::optional<uint64_t>) {});
pool.cancel (key1);
}

View file

@ -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.";

View file

@ -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";
}

View file

@ -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
};

View file

@ -1,5 +1,6 @@
#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/blocks.hpp>
#include <nano/lib/epoch.hpp>
#include <nano/lib/threading.hpp>
#include <nano/lib/work.hpp>
#include <nano/node/xorshift.hpp>
@ -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<uint64_t>::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<uint64_t>::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<uint64_t>::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<void(boost::optional<uint64_t> 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<void(boost::optional<uint64_t> 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<void(boost::optional<uint64_t> 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<void(boost::optional<uint64_t> 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<void(boost::optional<uint64_t> const &)> callback_a)
{
debug_assert (!root_a.is_zero ());
if (!threads.empty ())
{
{
nano::lock_guard<std::mutex> 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<uint64_t> 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<uint64_t> 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<uint64_t> nano::work_pool::generate (nano::root const & root_a, uint64_t difficulty_a)
@ -345,11 +341,9 @@ boost::optional<uint64_t> nano::work_pool::generate (nano::work_version const ve
{
std::promise<boost::optional<uint64_t>> work;
std::future<boost::optional<uint64_t>> future = work.get_future ();
generate (
version_a, root_a, [&work](boost::optional<uint64_t> work_a) {
generate (version_a, root_a, difficulty_a, [&work](boost::optional<uint64_t> work_a) {
work.set_value (work_a);
},
difficulty_a);
});
result = future.get ().value ();
}
return result;

View file

@ -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<void(boost::optional<uint64_t> 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<void(boost::optional<uint64_t> 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<void(boost::optional<uint64_t> const &)> callback;
uint64_t difficulty;
nano::work_version const version;
nano::root const item;
uint64_t const difficulty;
std::function<void(boost::optional<uint64_t> 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<void(boost::optional<uint64_t> const &)>);
void generate (nano::work_version const, nano::root const &, std::function<void(boost::optional<uint64_t> const &)>, uint64_t);
boost::optional<uint64_t> generate (nano::work_version const, nano::root const &);
void generate (nano::work_version const, nano::root const &, uint64_t, std::function<void(boost::optional<uint64_t> const &)>);
boost::optional<uint64_t> generate (nano::work_version const, nano::root const &, uint64_t);
// For tests only
void generate (nano::root const &, std::function<void(boost::optional<uint64_t> const &)>);
// For tests only
void generate (nano::root const &, std::function<void(boost::optional<uint64_t> const &)>, uint64_t);
boost::optional<uint64_t> generate (nano::root const &);
boost::optional<uint64_t> generate (nano::root const &, uint64_t);
size_t size ();

View file

@ -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<nano::uint128_t>::max ());
nano::open_block genesis_block (reinterpret_cast<const nano::block_hash &> (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<const nano::block_hash &> (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<std::chrono::microseconds> (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<nano::block_hash const &> (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 ()));
}

View file

@ -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<size_t> (1, node_a.config.active_elections_size / 10)),
@ -708,7 +708,7 @@ void nano::active_transactions::update_adjusted_difficulty ()
auto existing_root (roots.get<tag_root> ().find (root));
if (existing_root != roots.get<tag_root> ().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<std::uint64_t>::max () - average < static_cast<uint64_t> (highest_level))
@ -778,18 +778,18 @@ void nano::active_transactions::update_active_difficulty (nano::unique_lock<std:
}
if (prioritized.size () > 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);

View file

@ -85,7 +85,7 @@ void nano::block_processor::add (std::shared_ptr<nano::block> 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);

View file

@ -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<nano::block> 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 ())

View file

@ -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 ();

View file

@ -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<std::shared_ptr<nano::block>> (vote_block));
if (nano::work_validate (*block))
if (nano::work_validate_entry (*block))
{
status = parse_status::insufficient_work;
break;

View file

@ -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<uint64_t> const & work_a) {
node.work.generate (request.version, request.root, request.difficulty, [this_l](boost::optional<uint64_t> 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 ()));
}
}

View file

@ -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<std::pair<std::string, uint16_t>> const & peers_a, std::function<void(boost::optional<uint64_t>)> const & callback_a, uint64_t difficulty_a, boost::optional<nano::account> const & account_a)
bool nano::distributed_work_factory::make (nano::work_version const version_a, nano::root const & root_a, std::vector<std::pair<std::string, uint16_t>> const & peers_a, uint64_t difficulty_a, std::function<void(boost::optional<uint64_t>)> const & callback_a, boost::optional<nano::account> const & account_a)
{
return make (std::chrono::seconds (1), nano::work_request{ version_a, root_a, difficulty_a, account_a, callback_a, peers_a });
}

View file

@ -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<std::pair<std::string, uint16_t>> const &, std::function<void(boost::optional<uint64_t>)> const &, uint64_t, boost::optional<nano::account> const & = boost::none);
bool make (nano::work_version const, nano::root const &, std::vector<std::pair<std::string, uint16_t>> const &, uint64_t, std::function<void(boost::optional<uint64_t>)> const &, boost::optional<nano::account> 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 ();

View file

@ -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<bool> ("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<std::string> ("type"));
nano::wallet_id wallet (0);
auto difficulty_l (difficulty_optional_impl ());
boost::optional<std::string> wallet_text (request.get_optional<std::string> ("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<nano::node> 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<nano::node> 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<nano::node> 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<nano::node> 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 ();

View file

@ -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<std::pair<std::string, uin
return !peers_a.empty () || local_work_generation_enabled ();
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a)
{
return work_generate_blocking (block_a, network_params.network.publish_thresholds.base);
}
boost::optional<uint64_t> 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<uint64_t> 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<void(boost::optional<uint64_t>)> callback_a, boost::optional<nano::account> 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<void(boost::optional<uint64_t>)> callback_a, uint64_t difficulty_a, boost::optional<nano::account> 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<void(boost::optional<uint64_t>)> callback_a, boost::optional<nano::account> 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<uint64_t> nano::node::work_generate_blocking (nano::work_version const version_a, nano::root const & root_a, boost::optional<nano::account> const & account_a)
{
return work_generate_blocking (version_a, root_a, network_params.network.publish_thresholds.base, account_a);
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, boost::optional<nano::account> const & account_a)
{
std::promise<boost::optional<uint64_t>> promise;
work_generate (
version_a, root_a, [&promise](boost::optional<uint64_t> opt_work_a) {
version_a, root_a, difficulty_a, [&promise](boost::optional<uint64_t> opt_work_a) {
promise.set_value (opt_work_a);
},
difficulty_a, account_a);
account_a);
return promise.get_future ().get ();
}
boost::optional<uint64_t> 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<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a)
{
debug_assert (network_params.network.is_test_network ());

View file

@ -130,11 +130,8 @@ public:
bool work_generation_enabled () const;
bool work_generation_enabled (std::vector<std::pair<std::string, uint16_t>> const &) const;
boost::optional<uint64_t> work_generate_blocking (nano::block &, uint64_t);
boost::optional<uint64_t> work_generate_blocking (nano::block &);
boost::optional<uint64_t> work_generate_blocking (nano::work_version const, nano::root const &, uint64_t, boost::optional<nano::account> const & = boost::none);
boost::optional<uint64_t> work_generate_blocking (nano::work_version const, nano::root const &, boost::optional<nano::account> const & = boost::none);
void work_generate (nano::work_version const, nano::root const &, std::function<void(boost::optional<uint64_t>)>, uint64_t, boost::optional<nano::account> const & = boost::none, bool const = false);
void work_generate (nano::work_version const, nano::root const &, std::function<void(boost::optional<uint64_t>)>, boost::optional<nano::account> const & = boost::none);
void work_generate (nano::work_version const, nano::root const &, uint64_t, std::function<void(boost::optional<uint64_t>)>, boost::optional<nano::account> const & = boost::none, bool const = false);
void add_initial_peers ();
void block_confirm (std::shared_ptr<nano::block>);
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<uint64_t> work_generate_blocking (nano::block &);
// For tests only
boost::optional<uint64_t> work_generate_blocking (nano::root const &, uint64_t);
// For tests only
boost::optional<uint64_t> work_generate_blocking (nano::root const &);

View file

@ -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::state_block> 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<double, std::nano> const & delta_a)
{
deadline = std::chrono::steady_clock::now () + delta_a * deadline_scaling_factor;

View file

@ -31,8 +31,11 @@ public:
void generate_receive (nano::node &);
void generate_send_new (nano::node &, std::vector<nano::account> &);
void generate_send_existing (nano::node &, std::vector<nano::account> &);
std::unique_ptr<nano::state_block> upgrade_genesis_epoch (nano::node &, nano::epoch const);
std::shared_ptr<nano::wallet> 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

View file

@ -935,6 +935,8 @@ std::shared_ptr<nano::block> nano::wallet::receive_action (nano::block const & s
nano::account account;
auto hash (send_a.hash ());
std::shared_ptr<nano::block> 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::block> nano::wallet::receive_action (nano::block const & s
if (!new_account)
{
block = std::make_shared<nano::state_block> (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<nano::state_block> (account, 0, representative_a, pending_info.amount, reinterpret_cast<nano::link const &> (hash), prv, account, work_a);
details.epoch = send_a.sideband ().details.epoch;
}
}
else
@ -985,7 +989,7 @@ std::shared_ptr<nano::block> 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::block> nano::wallet::receive_action (nano::block const & s
std::shared_ptr<nano::block> 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<nano::block> 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::block> nano::wallet::change_action (nano::account const &
store.work_get (transaction, source_a, work_a);
}
block = std::make_shared<nano::state_block> (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::block> nano::wallet::send_action (nano::account const & so
auto error (false);
auto cached_block (false);
std::shared_ptr<nano::block> 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::block> nano::wallet::send_action (nano::account const & so
store.work_get (transaction, source_a, work_a);
}
block = std::make_shared<nano::state_block> (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::block> 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<std::shared_ptr<nano::block>, bool, bool> result;
std::tuple<std::shared_ptr<nano::block>, bool, bool, nano::block_details> result;
{
if (id_mdb_val)
{
@ -1118,11 +1127,12 @@ std::shared_ptr<nano::block> nano::wallet::send_action (nano::account const & so
std::shared_ptr<nano::block> 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::block> nano::wallet::send_action (nano::account const & so
return block;
}
bool nano::wallet::action_complete (std::shared_ptr<nano::block> const & block_a, nano::account const & account_a, bool const generate_work_a)
bool nano::wallet::action_complete (std::shared_ptr<nano::block> 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<uint64_t> work_a) {
block_a->work_version (), block_a->root (), active_difficulty, [watcher_l, block_a, root_a](boost::optional<uint64_t> 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
{

View file

@ -124,7 +124,7 @@ public:
std::shared_ptr<nano::block> change_action (nano::account const &, nano::account const &, uint64_t = 0, bool = true);
std::shared_ptr<nano::block> receive_action (nano::block const &, nano::account const &, nano::uint128_union const &, uint64_t = 0, bool = true);
std::shared_ptr<nano::block> send_action (nano::account const &, nano::account const &, nano::uint128_t const &, uint64_t = 0, bool = true, boost::optional<std::string> = {});
bool action_complete (std::shared_ptr<nano::block> const &, nano::account const &, bool const);
bool action_complete (std::shared_ptr<nano::block> 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 ();

View file

@ -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);
}

View file

@ -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));
}

View file

@ -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)

View file

@ -619,6 +619,55 @@ TEST (rpc, send_idempotent)
ASSERT_EQ (std::error_code (nano::error_common::insufficient_balance).message (), response3.json.get<std::string> ("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<std::string> ("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::state_block> (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<nano::state_block> (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<std::string> ("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<nano::state_block> (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<std::string> ("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<std::string> ("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<double> ("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<std::string> ("work"));
auto work_text (response.json.get_optional<std::string> ("work"));
if (!work_text)
{
std::cout << response.json.get<std::string> ("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<std::string> ("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<uint64_t> work_a) {
system.work.generate (nano::work_version::work_1, hash1, node1.network_params.network.publish_thresholds.base, [&done](boost::optional<uint64_t> 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<uint64_t> work (0);
node2.work_generate (nano::work_version::work_1, hash1, [&work](boost::optional<uint64_t> work_a) {
node2.work_generate (nano::work_version::work_1, hash1, node2.network_params.network.publish_thresholds.base, [&work](boost::optional<uint64_t> 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<uint64_t> work_a) {
std::atomic<uint64_t> work (0);
node2.work_generate (nano::work_version::work_1, key1.pub, node1.network_params.network.publish_thresholds.base, [&work](boost::optional<uint64_t> 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<uint64_t> work_a) {
node1.work_generate (nano::work_version::work_1, key1.pub, node1.network_params.network.publish_thresholds.base, [& work = works[i]](boost::optional<uint64_t> 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<std::string> ("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<std::string> ("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<double> ("multiplier"));
ASSERT_NEAR (expected_multiplier, multiplier, 1e-6);
auto network_current_text (response.json.get<std::string> ("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

View file

@ -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)