Work version concept (#2569)

* Work version concept

Adds nano::work_version which is passed to all work_generate and work_validate methods (except for tests, ensured with an assert).

The functional changes are quite minimal, as every caller defaults to the only work version available (work_1):

- RPC work_generate, work_validate and block_create can receive an optional "work_version"
- The work websocket outputs the work version

These changes constitute the pipeline necessary for conditional work validation.

* Use nano::to_string to match the work version text
This commit is contained in:
Guilherme Lawless 2020-02-19 15:03:23 +00:00 committed by GitHub
commit eb5febfdf3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 352 additions and 156 deletions

View file

@ -534,8 +534,8 @@ TEST (active_transactions, update_difficulty)
ASSERT_NO_ERROR (system.poll ());
}
// Update work with higher difficulty
auto work1 = node1.work_generate_blocking (send1->root (), difficulty1 + 1, boost::none);
auto work2 = node1.work_generate_blocking (send2->root (), difficulty2 + 1, boost::none);
auto work1 = node1.work_generate_blocking (send1->root (), difficulty1 + 1);
auto work2 = node1.work_generate_blocking (send2->root (), difficulty2 + 1);
std::error_code ec;
nano::state_block_builder builder;

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::block_hash (), {}, {}, nano::network_constants::publish_test_threshold));
ASSERT_TRUE (system.nodes[0]->distributed_work.make (nano::work_version::work_1, nano::block_hash (), {}, {}, nano::network_constants::publish_test_threshold));
}
TEST (distributed_work, no_peers)
@ -25,7 +25,7 @@ TEST (distributed_work, no_peers)
work = work_a;
done = true;
};
ASSERT_FALSE (node->distributed_work.make (hash, node->config.work_peers, callback, node->network_params.network.publish_threshold, nano::account ()));
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, node->config.work_peers, callback, node->network_params.network.publish_threshold, nano::account ()));
system.deadline_set (5s);
while (!done)
{
@ -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::block_hash (), node.config.work_peers, {}, nano::network_constants::publish_test_threshold));
ASSERT_TRUE (node.distributed_work.make (nano::work_version::work_1, nano::block_hash (), node.config.work_peers, {}, nano::network_constants::publish_test_threshold));
}
TEST (distributed_work, no_peers_cancel)
@ -63,7 +63,7 @@ TEST (distributed_work, no_peers_cancel)
ASSERT_FALSE (work_a.is_initialized ());
done = true;
};
ASSERT_FALSE (node.distributed_work.make (hash, node.config.work_peers, callback_to_cancel, nano::difficulty::from_multiplier (1e6, node.network_params.network.publish_threshold)));
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_threshold)));
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 (hash, node.config.work_peers, callback_to_cancel, nano::difficulty::from_multiplier (1e6, node.network_params.network.publish_threshold)));
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_threshold)));
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 (hash, node->config.work_peers, callback, nano::difficulty::from_multiplier (10, node->network_params.network.publish_threshold)));
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_threshold)));
}
// 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 (hash_i, node->config.work_peers, callback, node->network_params.network.publish_threshold));
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash_i, node->config.work_peers, callback, node->network_params.network.publish_threshold));
}
// 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,7 +171,7 @@ 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 (hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
system.deadline_set (5s);
while (!done)
{
@ -201,7 +201,7 @@ 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 (hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
system.deadline_set (5s);
while (!done)
{
@ -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 (hash, peers, nullptr, node->network_params.network.publish_threshold, nano::account ()));
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, nullptr, node->network_params.network.publish_threshold, nano::account ()));
system.deadline_set (5s);
while (malicious_peer2->generations_bad < 2)
{
@ -259,7 +259,7 @@ 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 (hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
system.deadline_set (5s);
while (!done)
{
@ -298,7 +298,7 @@ TEST (distributed_work, fail_resolve)
};
decltype (node->config.work_peers) peers;
peers.emplace_back ("beeb.boop.123z", 0);
ASSERT_FALSE (node->distributed_work.make (hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
ASSERT_FALSE (node->distributed_work.make (nano::work_version::work_1, hash, peers, callback, node->network_params.network.publish_threshold, nano::account ()));
system.deadline_set (5s);
while (!done)
{

View file

@ -1190,7 +1190,7 @@ TEST (wallet, work_watcher_generation_disabled)
auto block (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - nano::Mxrb_ratio, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *pool.generate (genesis.hash ())));
uint64_t difficulty (0);
ASSERT_FALSE (nano::work_validate (*block, &difficulty));
node.wallets.watcher->add (block);
node.wallets.watcher->add (block, nano::work_version::work_1);
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, node.network_params.network.publish_threshold);

View file

@ -677,6 +677,7 @@ TEST (websocket, work)
ASSERT_EQ (1, contents.count ("request"));
auto & request = contents.get_child ("request");
ASSERT_EQ (request.get<std::string> ("version"), nano::to_string (nano::work_version::work_1));
ASSERT_EQ (request.get<std::string> ("hash"), hash.to_string ());
ASSERT_EQ (request.get<std::string> ("difficulty"), nano::to_string_hex (node1->network_params.network.publish_threshold));
ASSERT_EQ (request.get<double> ("multiplier"), 1.0);

View file

@ -96,8 +96,8 @@ TEST (work, opencl)
if (opencl != nullptr)
{
// 0 threads, should add 1 for managing OpenCL
nano::work_pool pool (0, std::chrono::nanoseconds (0), [&opencl](nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> & ticket_a) {
return opencl->generate_work (root_a, difficulty_a);
nano::work_pool pool (0, std::chrono::nanoseconds (0), [&opencl](nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> & ticket_a) {
return opencl->generate_work (version_a, root_a, difficulty_a);
});
ASSERT_NE (nullptr, pool.opencl);
nano::root root;

View file

@ -144,6 +144,8 @@ std::string nano::error_rpc_messages::message (int ev) const
return "Bad source";
case nano::error_rpc::bad_timeout:
return "Bad timeout number";
case nano::error_rpc::bad_work_version:
return "Bad work version";
case nano::error_rpc::block_create_balance_mismatch:
return "Balance mismatch for previous block";
case nano::error_rpc::block_create_key_required:

View file

@ -84,6 +84,7 @@ enum class error_rpc
bad_representative_number,
bad_source,
bad_timeout,
bad_work_version,
block_create_balance_mismatch,
block_create_key_required,
block_create_public_key_mismatch,

View file

@ -6,24 +6,65 @@
#include <future>
bool nano::work_validate (nano::root const & root_a, uint64_t work_a, uint64_t * difficulty_a)
std::string nano::to_string (nano::work_version const version_a)
{
static nano::network_constants network_constants;
auto value (nano::work_value (root_a, work_a));
if (difficulty_a != nullptr)
std::string result ("invalid");
switch (version_a)
{
*difficulty_a = value;
case nano::work_version::work_1:
result = "work_1";
break;
case nano::work_version::unspecified:
result = "unspecified";
break;
}
return value < network_constants.publish_threshold;
return result;
}
bool nano::work_validate (nano::work_version const version_a, nano::block const & block_a, uint64_t * difficulty_a)
{
return nano::work_validate (version_a, block_a.root (), block_a.block_work (), difficulty_a);
}
bool nano::work_validate (nano::work_version const version_a, nano::root const & root_a, uint64_t const work_a, uint64_t * difficulty_a)
{
bool invalid (true);
switch (version_a)
{
case nano::work_version::work_1:
invalid = nano::work_v1::validate (root_a, work_a, difficulty_a);
break;
default:
assert (false && "Invalid version specified to work_validate");
}
return invalid;
}
bool nano::work_validate (nano::block const & block_a, uint64_t * difficulty_a)
{
return work_validate (block_a.root (), block_a.block_work (), difficulty_a);
return nano::work_validate (block_a.root (), block_a.block_work (), difficulty_a);
}
bool nano::work_validate (nano::root const & root_a, uint64_t const work_a, uint64_t * difficulty_a)
{
static nano::network_constants network_constants;
assert (network_constants.is_test_network ());
return nano::work_validate (nano::work_version::work_1, root_a, work_a, difficulty_a);
}
bool nano::work_v1::validate (nano::root const & root_a, uint64_t work_a, uint64_t * difficulty_a)
{
static nano::network_constants network_constants;
auto work_value (value (root_a, work_a));
if (difficulty_a != nullptr)
{
*difficulty_a = work_value;
}
return work_value < network_constants.publish_threshold;
}
#ifndef NANO_FUZZER_TEST
uint64_t nano::work_value (nano::root const & root_a, uint64_t work_a)
uint64_t nano::work_v1::value (nano::root const & root_a, uint64_t work_a)
{
uint64_t result;
blake2b_state hash;
@ -34,7 +75,7 @@ uint64_t nano::work_value (nano::root const & root_a, uint64_t work_a)
return result;
}
#else
uint64_t nano::work_value (nano::root const & root_a, uint64_t work_a)
uint64_t nano::work_v1::value (nano::root const & root_a, uint64_t work_a)
{
static nano::network_constants network_constants;
if (!network_constants.is_test_network ())
@ -46,7 +87,7 @@ uint64_t nano::work_value (nano::root const & root_a, uint64_t work_a)
}
#endif
nano::work_pool::work_pool (unsigned max_threads_a, std::chrono::nanoseconds pow_rate_limiter_a, std::function<boost::optional<uint64_t> (nano::root const &, uint64_t, std::atomic<int> &)> opencl_a) :
nano::work_pool::work_pool (unsigned max_threads_a, std::chrono::nanoseconds pow_rate_limiter_a, std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> opencl_a) :
ticket (0),
done (false),
pow_rate_limiter (pow_rate_limiter_a),
@ -108,12 +149,12 @@ void nano::work_pool::loop (uint64_t thread)
boost::optional<uint64_t> opt_work;
if (thread == 0 && opencl)
{
opt_work = opencl (current_l.item, current_l.difficulty, ticket);
opt_work = opencl (current_l.version, current_l.item, current_l.difficulty, ticket);
}
if (opt_work.is_initialized ())
{
work = *opt_work;
output = work_value (current_l.item, work);
output = nano::work_v1::value (current_l.item, work);
}
else
{
@ -146,7 +187,7 @@ void nano::work_pool::loop (uint64_t thread)
{
// If the ticket matches what we started with, we're the ones that found the solution
assert (output >= current_l.difficulty);
assert (current_l.difficulty == 0 || work_value (current_l.item, work) == output);
assert (current_l.difficulty == 0 || nano::work_v1::value (current_l.item, work) == output);
// Signal other threads to stop their work next time they check ticket
++ticket;
pending.pop_front ();
@ -206,17 +247,27 @@ void nano::work_pool::stop ()
void nano::work_pool::generate (nano::root const & root_a, std::function<void(boost::optional<uint64_t> const &)> callback_a)
{
generate (root_a, callback_a, network_constants.publish_threshold);
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_threshold);
}
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)
{
assert (!root_a.is_zero ());
if (!threads.empty ())
{
{
nano::lock_guard<std::mutex> lock (mutex);
pending.emplace_back (root_a, callback_a, difficulty_a);
pending.emplace_back (version_a, root_a, callback_a, difficulty_a);
}
producer_condition.notify_all ();
}
@ -228,10 +279,24 @@ void nano::work_pool::generate (nano::root const & root_a, std::function<void(bo
boost::optional<uint64_t> nano::work_pool::generate (nano::root const & root_a)
{
return generate (root_a, network_constants.publish_threshold);
static nano::network_constants network_constants;
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_threshold);
}
boost::optional<uint64_t> nano::work_pool::generate (nano::root const & root_a, uint64_t difficulty_a)
{
static nano::network_constants network_constants;
assert (network_constants.is_test_network ());
return generate (nano::work_version::work_1, root_a, difficulty_a);
}
boost::optional<uint64_t> nano::work_pool::generate (nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a)
{
boost::optional<uint64_t> result;
if (!threads.empty ())
@ -239,7 +304,7 @@ boost::optional<uint64_t> nano::work_pool::generate (nano::root const & root_a,
std::promise<boost::optional<uint64_t>> work;
std::future<boost::optional<uint64_t>> future = work.get_future ();
generate (
root_a, [&work](boost::optional<uint64_t> work_a) {
version_a, root_a, [&work](boost::optional<uint64_t> work_a) {
work.set_value (work_a);
},
difficulty_a);

View file

@ -13,20 +13,35 @@
namespace nano
{
class block;
enum class work_version
{
unspecified,
work_1
};
std::string to_string (nano::work_version const version_a);
bool work_validate (nano::root const &, uint64_t, uint64_t * = nullptr);
class block;
bool work_validate (nano::work_version const, nano::block const &, uint64_t * = nullptr);
bool work_validate (nano::work_version const, nano::root const &, uint64_t const, uint64_t * = nullptr);
// For tests only
bool work_validate (nano::block const &, uint64_t * = nullptr);
uint64_t work_value (nano::root const &, uint64_t);
// For tests only
bool work_validate (nano::root const &, uint64_t const, uint64_t * = nullptr);
namespace work_v1
{
bool validate (nano::root const &, uint64_t const, uint64_t * = nullptr);
uint64_t value (nano::root const &, uint64_t);
}
class opencl_work;
class work_item final
{
public:
work_item (nano::root const & item_a, std::function<void(boost::optional<uint64_t> const &)> const & callback_a, uint64_t difficulty_a) :
item (item_a), callback (callback_a), difficulty (difficulty_a)
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)
{
}
nano::work_version version;
nano::root item;
std::function<void(boost::optional<uint64_t> const &)> callback;
uint64_t difficulty;
@ -34,12 +49,18 @@ public:
class work_pool final
{
public:
work_pool (unsigned, std::chrono::nanoseconds = std::chrono::nanoseconds (0), std::function<boost::optional<uint64_t> (nano::root const &, uint64_t, std::atomic<int> &)> = nullptr);
work_pool (unsigned, std::chrono::nanoseconds = std::chrono::nanoseconds (0), std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> = nullptr);
~work_pool ();
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 &);
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);
@ -52,7 +73,7 @@ public:
std::mutex mutex;
nano::condition_variable producer_condition;
std::chrono::nanoseconds pow_rate_limiter;
std::function<boost::optional<uint64_t> (nano::root const &, uint64_t, std::atomic<int> &)> opencl;
std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> opencl;
nano::observer_set<bool> work_observers;
};

View file

@ -47,10 +47,10 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
nano::logger_mt logger{ config.node.logging.min_time_between_log_output };
boost::asio::io_context io_ctx;
auto opencl (nano::opencl_work::create (config.opencl_enable, config.opencl, logger));
nano::work_pool opencl_work (config.node.work_threads, config.node.pow_sleep_interval, opencl ? [&opencl](nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> & ticket_a) {
return opencl->generate_work (root_a, difficulty_a, ticket_a);
nano::work_pool opencl_work (config.node.work_threads, config.node.pow_sleep_interval, opencl ? [&opencl](nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> & ticket_a) {
return opencl->generate_work (version_a, root_a, difficulty_a, ticket_a);
}
: std::function<boost::optional<uint64_t> (nano::root const &, uint64_t, std::atomic<int> &)> (nullptr));
: std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> (nullptr));
nano::alarm alarm (io_ctx);
try
{

View file

@ -193,7 +193,7 @@ int main (int argc, char * const * argv)
<< "Account: " << rep.pub.to_account () << "\n";
}
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 (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));
std::cout << genesis_block.to_json ();
std::cout.flush ();
nano::block_hash previous (genesis_block.hash ());
@ -205,7 +205,7 @@ int main (int argc, char * const * argv)
{
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 (previous));
nano::send_block send (previous, landing.pub, balance, genesis.prv, genesis.pub, *work.generate (nano::work_version::work_1, previous));
previous = send.hash ();
std::cout << send.to_json ();
std::cout.flush ();
@ -321,7 +321,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 (block.root ()));
block.block_work_set (*work.generate (nano::work_version::work_1, block.root ()));
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 ());
}
@ -336,7 +336,7 @@ int main (int argc, char * const * argv)
uint64_t count{ 10000000U }; // 10M
for (uint64_t i (0); i < count; ++i)
{
valid = nano::work_value (hash, i) > difficulty;
valid = nano::work_v1::value (hash, i) > difficulty;
}
std::ostringstream oss (valid ? "true" : "false"); // IO forces compiler to not dismiss the variable
auto total_time (std::chrono::duration_cast<std::chrono::nanoseconds> (std::chrono::steady_clock::now () - start).count ());
@ -419,17 +419,17 @@ int main (int argc, char * const * argv)
nano::logger_mt logger;
nano::opencl_config config (platform, device, threads);
auto opencl (nano::opencl_work::create (true, config, logger));
nano::work_pool work_pool (std::numeric_limits<unsigned>::max (), std::chrono::nanoseconds (0), opencl ? [&opencl](nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> &) {
return opencl->generate_work (root_a, difficulty_a);
nano::work_pool work_pool (std::numeric_limits<unsigned>::max (), std::chrono::nanoseconds (0), opencl ? [&opencl](nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> &) {
return opencl->generate_work (version_a, root_a, difficulty_a);
}
: std::function<boost::optional<uint64_t> (nano::root const &, uint64_t, std::atomic<int> &)> (nullptr));
: std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> (nullptr));
nano::change_block block (0, 0, nano::keypair ().prv, 0, 0);
std::cerr << boost::str (boost::format ("Starting OpenCL generation profiling. Platform: %1%. Device: %2%. Threads: %3%. Difficulty: %4$#x\n") % platform % device % threads % difficulty);
for (uint64_t i (0); true; ++i)
{
block.hashables.previous.qwords[0] += 1;
auto begin1 (std::chrono::high_resolution_clock::now ());
block.block_work_set (*work_pool.generate (block.root (), difficulty));
block.block_work_set (*work_pool.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 ());
}
@ -738,7 +738,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 (genesis_latest))
.work (*work.generate (nano::work_version::work_1, genesis_latest))
.build ();
genesis_latest = send->hash ();
@ -751,7 +751,7 @@ int main (int argc, char * const * argv)
.balance (balances[i])
.link (genesis_latest)
.sign (keys[i].prv, keys[i].pub)
.work (*work.generate (keys[i].pub))
.work (*work.generate (nano::work_version::work_1, keys[i].pub))
.build ();
frontiers[i] = open->hash ();
@ -772,7 +772,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 (frontiers[j]))
.work (*work.generate (nano::work_version::work_1, frontiers[j]))
.build ();
frontiers[j] = send->hash ();
@ -787,7 +787,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 (frontiers[other]))
.work (*work.generate (nano::work_version::work_1, frontiers[other]))
.build ();
frontiers[other] = receive->hash ();
@ -859,7 +859,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 (genesis_latest))
.work (*work.generate (nano::work_version::work_1, genesis_latest))
.build ();
genesis_latest = send->hash ();
@ -872,7 +872,7 @@ int main (int argc, char * const * argv)
.balance (balance)
.link (genesis_latest)
.sign (keys[i].prv, keys[i].pub)
.work (*work.generate (keys[i].pub))
.work (*work.generate (nano::work_version::work_1, keys[i].pub))
.build ();
node->ledger.process (transaction, *open);
@ -891,7 +891,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 (genesis_latest))
.work (*work.generate (nano::work_version::work_1, genesis_latest))
.build ();
genesis_latest = send->hash ();
@ -1104,7 +1104,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.get ()))
if (nano::work_validate (nano::work_version::work_1, *block))
{
std::cerr << boost::str (boost::format ("Invalid work for block %1% value: %2%\n") % hash.to_string () % nano::to_string_hex (block->block_work ()));
}
@ -1416,4 +1416,4 @@ bool address_library_pair::operator== (const address_library_pair & other) const
{
return address == other.address;
}
}
}

View file

@ -108,10 +108,10 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
std::shared_ptr<nano_qt::wallet> gui;
nano::set_application_icon (application);
auto opencl (nano::opencl_work::create (config.opencl_enable, config.opencl, logger));
nano::work_pool work (config.node.work_threads, config.node.pow_sleep_interval, opencl ? [&opencl](nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> &) {
return opencl->generate_work (root_a, difficulty_a);
nano::work_pool work (config.node.work_threads, config.node.pow_sleep_interval, opencl ? [&opencl](nano::work_version const version_a, nano::root const & root_a, uint64_t difficulty_a, std::atomic<int> &) {
return opencl->generate_work (version_a, root_a, difficulty_a);
}
: std::function<boost::optional<uint64_t> (nano::root const &, uint64_t, std::atomic<int> &)> (nullptr));
: std::function<boost::optional<uint64_t> (nano::work_version const, nano::root const &, uint64_t, std::atomic<int> &)> (nullptr));
nano::alarm alarm (io_ctx);
node = std::make_shared<nano::node> (io_ctx, data_path, alarm, config.node, work, flags);
if (!node->init_error ())

View file

@ -582,7 +582,7 @@ std::pair<std::shared_ptr<nano::election>, bool> nano::active_transactions::inse
auto hash (block_a->hash ());
result.first = nano::make_shared<nano::election> (node, block_a, skip_delay_a, confirmation_action_a);
uint64_t difficulty (0);
release_assert (!nano::work_validate (*block_a, &difficulty));
release_assert (!nano::work_validate (nano::work_version::work_1, *block_a, &difficulty));
roots.get<tag_root> ().emplace (nano::conflict_info{ root, difficulty, difficulty, result.first });
blocks.emplace (hash, result.first);
adjust_difficulty (hash);
@ -680,7 +680,7 @@ void nano::active_transactions::update_difficulty (std::shared_ptr<nano::block>
if (existing_election != roots.get<tag_root> ().end ())
{
uint64_t difficulty;
auto error (nano::work_validate (*block_a, &difficulty));
auto error (nano::work_validate (nano::work_version::work_1, *block_a, &difficulty));
(void)error;
assert (!error);
if (difficulty > existing_election->difficulty)
@ -714,7 +714,7 @@ void nano::active_transactions::update_difficulty (std::shared_ptr<nano::block>
{
uint64_t existing_difficulty;
uint64_t new_difficulty;
if (!nano::work_validate (*block_a, &new_difficulty) && !nano::work_validate (*existing_block, &existing_difficulty))
if (!nano::work_validate (nano::work_version::work_1, *block_a, &new_difficulty) && !nano::work_validate (nano::work_version::work_1, *existing_block, &existing_difficulty))
{
if (new_difficulty > existing_difficulty)
{

View file

@ -69,7 +69,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)
{
if (!nano::work_validate (info_a.block->root (), info_a.block->block_work ()))
if (!nano::work_validate (nano::work_version::work_1, info_a.block->root (), info_a.block->block_work ()))
{
{
auto hash (info_a.block->hash ());
@ -375,7 +375,7 @@ void nano::block_processor::process_live (nano::block_hash const & hash_a, std::
// Add to work watcher to prevent dropping the election
if (watch_work_a)
{
node.wallets.watcher->add (block_a);
node.wallets.watcher->add (block_a, nano::work_version::work_1);
}
// Start collecting quorum on block

View file

@ -214,7 +214,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 (nano::work_version::work_1, *block))
{
auto hash (block->hash ());
if (connection->node->config.logging.bulk_pull_logging ())

View file

@ -240,7 +240,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 (nano::work_version::work_1, *block))
{
connection->node->process_active (std::move (block));
throttled_receive ();

View file

@ -418,7 +418,7 @@ void nano::message_parser::deserialize_publish (nano::stream & stream_a, nano::m
nano::publish incoming (error, stream_a, header_a, &block_uniquer);
if (!error && at_end (stream_a))
{
if (!nano::work_validate (*incoming.block))
if (!nano::work_validate (nano::work_version::work_1, *incoming.block))
{
visitor.publish (incoming);
}
@ -439,7 +439,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 (nano::work_version::work_1, *incoming.block))
{
visitor.confirm_req (incoming);
}
@ -465,7 +465,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 (nano::work_version::work_1, *block))
{
status = parse_status::insufficient_work;
break;

View file

@ -43,15 +43,15 @@ nano::distributed_work::~distributed_work ()
nano::websocket::message_builder builder;
if (status == work_generation_status::success)
{
node_l->websocket_server->broadcast (builder.work_generation (request.root, work_result, request.difficulty, node_l->network_params.network.publish_threshold, elapsed.value (), winner, bad_peers));
node_l->websocket_server->broadcast (builder.work_generation (request.version, request.root, work_result, request.difficulty, node_l->network_params.network.publish_threshold, elapsed.value (), winner, bad_peers));
}
else if (status == work_generation_status::cancelled)
{
node_l->websocket_server->broadcast (builder.work_cancelled (request.root, request.difficulty, node_l->network_params.network.publish_threshold, elapsed.value (), bad_peers));
node_l->websocket_server->broadcast (builder.work_cancelled (request.version, request.root, request.difficulty, node_l->network_params.network.publish_threshold, elapsed.value (), bad_peers));
}
else if (status == work_generation_status::failure_local || status == work_generation_status::failure_peers)
{
node_l->websocket_server->broadcast (builder.work_failed (request.root, request.difficulty, node_l->network_params.network.publish_threshold, elapsed.value (), bad_peers));
node_l->websocket_server->broadcast (builder.work_failed (request.version, request.root, request.difficulty, node_l->network_params.network.publish_threshold, elapsed.value (), bad_peers));
}
}
stop_once (true);
@ -108,7 +108,7 @@ void nano::distributed_work::start_local ()
auto this_l (shared_from_this ());
local_generation_started = true;
node.work.generate (
request.root, [this_l](boost::optional<uint64_t> const & work_a) {
request.version, request.root, [this_l](boost::optional<uint64_t> const & work_a) {
if (work_a.is_initialized ())
{
this_l->set_once (*work_a);
@ -242,7 +242,7 @@ void nano::distributed_work::success (std::string const & body_a, nano::tcp_endp
if (!nano::from_string_hex (work_text, work))
{
uint64_t result_difficulty (0);
if (!nano::work_validate (request.root, work, &result_difficulty) && result_difficulty >= request.difficulty)
if (!nano::work_validate (request.version, request.root, work, &result_difficulty) && result_difficulty >= request.difficulty)
{
error = false;
node.unresponsive_work_peers = false;

View file

@ -6,6 +6,7 @@
#include <nano/boost/beast/http/string_body.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/timer.hpp>
#include <nano/lib/work.hpp>
#include <nano/node/common.hpp>
#include <boost/optional.hpp>
@ -28,6 +29,7 @@ class node;
struct work_request final
{
nano::work_version version;
nano::root root;
uint64_t difficulty;
boost::optional<nano::account> const account;

View file

@ -11,9 +11,9 @@ nano::distributed_work_factory::~distributed_work_factory ()
stop ();
}
bool nano::distributed_work_factory::make (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, std::function<void(boost::optional<uint64_t>)> const & callback_a, uint64_t difficulty_a, boost::optional<nano::account> const & account_a)
{
return make (std::chrono::seconds (1), nano::work_request{ root_a, difficulty_a, account_a, callback_a, peers_a });
return make (std::chrono::seconds (1), nano::work_request{ version_a, root_a, difficulty_a, account_a, callback_a, peers_a });
}
bool nano::distributed_work_factory::make (std::chrono::seconds const & backoff_a, nano::work_request const & request_a)

View file

@ -22,7 +22,7 @@ class distributed_work_factory final
public:
distributed_work_factory (nano::node &);
~distributed_work_factory ();
bool make (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 &, std::function<void(boost::optional<uint64_t>)> const &, uint64_t, 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 ();
@ -36,4 +36,4 @@ public:
class container_info_component;
std::unique_ptr<container_info_component> collect_container_info (distributed_work_factory & distributed_work, const std::string & name);
}
}

View file

@ -375,6 +375,24 @@ double nano::json_handler::multiplier_optional_impl (uint64_t & difficulty)
return multiplier;
}
nano::work_version nano::json_handler::work_version_optional_impl (nano::work_version const default_a)
{
nano::work_version result = default_a;
boost::optional<std::string> version_text (request.get_optional<std::string> ("version"));
if (!ec && version_text.is_initialized ())
{
if (*version_text == nano::to_string (nano::work_version::work_1))
{
result = nano::work_version::work_1;
}
else
{
ec = nano::error_rpc::bad_work_version;
}
}
return result;
}
namespace
{
bool decode_unsigned (std::string const & text, uint64_t & number)
@ -697,7 +715,7 @@ 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 (info.head, work))
if (nano::work_validate (nano::work_version::work_1, info.head, work))
{
rpc_l->ec = nano::error_common::invalid_work;
}
@ -1277,6 +1295,8 @@ void nano::json_handler::block_create ()
}
}
auto work (work_optional_impl ());
// Default to work_1 if not specified
auto work_version (work_version_optional_impl (nano::work_version::work_1));
nano::raw_key prv;
prv.data.clear ();
nano::block_hash previous (0);
@ -1542,7 +1562,7 @@ void nano::json_handler::block_create ()
{
if (work == 0)
{
node.work_generate (root_l, get_callback_l (block_l), nano::account (pub));
node.work_generate (work_version, root_l, get_callback_l (block_l), nano::account (pub));
}
else
{
@ -2103,10 +2123,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 (info.head).value_or (0))
.work (node_a->work_generate_blocking (nano::work_version::work_1, info.head).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 (!nano::work_validate (nano::work_version::work_1, *epoch.get ()));
nano::process_result result (nano::process_result::old);
if (valid_signature && valid_work)
{
@ -2162,10 +2182,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 (key.account).value_or (0))
.work (node_a->work_generate_blocking (nano::work_version::work_1, key.account).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 (!nano::work_validate (nano::work_version::work_1, *epoch.get ()));
nano::process_result result (nano::process_result::old);
if (valid_signature && valid_work)
{
@ -3232,7 +3252,7 @@ void nano::json_handler::process ()
}
if (!rpc_l->ec)
{
if (!nano::work_validate (*block))
if (!nano::work_validate (nano::work_version::work_1, *block))
{
auto result (rpc_l->node.process_local (block, watch_work_l));
switch (result.code)
@ -3345,7 +3365,7 @@ void nano::json_handler::receive ()
{
head = account;
}
if (nano::work_validate (head, work))
if (nano::work_validate (nano::work_version::work_1, head, work))
{
ec = nano::error_common::invalid_work;
}
@ -3688,7 +3708,7 @@ void nano::json_handler::send ()
}
if (!ec && work)
{
if (nano::work_validate (info.head, work))
if (nano::work_validate (nano::work_version::work_1, info.head, work))
{
ec = nano::error_common::invalid_work;
}
@ -4895,7 +4915,9 @@ void nano::json_handler::work_generate ()
{
boost::optional<nano::account> account;
auto account_opt (request.get_optional<std::string> ("account"));
if (account_opt.is_initialized ())
// Default to work_1 if not specified
auto work_version (work_version_optional_impl (nano::work_version::work_1));
if (!ec && account_opt.is_initialized ())
{
account = account_impl (account_opt.get ());
}
@ -4912,7 +4934,7 @@ void nano::json_handler::work_generate ()
{
auto use_peers (request.get<bool> ("use_peers", false));
auto rpc_l (shared_from_this ());
auto callback = [rpc_l, hash, this](boost::optional<uint64_t> const & work_a) {
auto callback = [rpc_l, hash, work_version, this](boost::optional<uint64_t> const & work_a) {
if (work_a)
{
boost::property_tree::ptree response_l;
@ -4921,7 +4943,7 @@ void nano::json_handler::work_generate ()
response_l.put ("work", nano::to_string_hex (work));
std::stringstream ostream;
uint64_t result_difficulty;
nano::work_validate (hash, work, &result_difficulty);
nano::work_validate (work_version, hash, work, &result_difficulty);
response_l.put ("difficulty", nano::to_string_hex (result_difficulty));
auto result_multiplier = nano::difficulty::to_multiplier (result_difficulty, this->node.network_params.network.publish_threshold);
response_l.put ("multiplier", nano::to_string (result_multiplier));
@ -4937,7 +4959,7 @@ void nano::json_handler::work_generate ()
{
if (node.local_work_generation_enabled ())
{
node.work.generate (hash, callback, difficulty);
node.work.generate (work_version, hash, callback, difficulty);
}
else
{
@ -4959,7 +4981,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 (hash, callback, difficulty, account, secondary_work_peers_l);
node.work_generate (work_version, hash, callback, difficulty, account, secondary_work_peers_l);
}
else
{
@ -5031,10 +5053,12 @@ void nano::json_handler::work_validate ()
auto work (work_optional_impl ());
auto difficulty (difficulty_optional_impl ());
multiplier_optional_impl (difficulty);
// Default to work_1 if not specified
auto work_version (work_version_optional_impl (nano::work_version::work_1));
if (!ec)
{
uint64_t result_difficulty (0);
nano::work_validate (hash, work, &result_difficulty);
nano::work_validate (work_version, hash, work, &result_difficulty);
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, node.network_params.network.publish_threshold);

View file

@ -158,6 +158,7 @@ public:
uint64_t offset_optional_impl (uint64_t = 0);
uint64_t difficulty_optional_impl ();
double multiplier_optional_impl (uint64_t &);
nano::work_version work_version_optional_impl (nano::work_version const default_a);
bool enable_sign_hash{ false };
std::function<void()> stop_callback;
nano::node_rpc_config const & node_rpc_config;

View file

@ -1006,14 +1006,14 @@ 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)
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::work_version const version_a, nano::block & block_a)
{
return work_generate_blocking (block_a, network_params.network.publish_threshold);
return work_generate_blocking (version_a, block_a, network_params.network.publish_threshold);
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a, uint64_t difficulty_a)
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::work_version const version_a, nano::block & block_a, uint64_t difficulty_a)
{
auto opt_work_l (work_generate_blocking (block_a.root (), difficulty_a, block_a.account ()));
auto opt_work_l (work_generate_blocking (version_a, block_a.root (), difficulty_a, block_a.account ()));
if (opt_work_l.is_initialized ())
{
block_a.block_work_set (*opt_work_l);
@ -1021,37 +1021,61 @@ boost::optional<uint64_t> nano::node::work_generate_blocking (nano::block & bloc
return opt_work_l;
}
void nano::node::work_generate (nano::root const & root_a, std::function<void(boost::optional<uint64_t>)> callback_a, boost::optional<nano::account> const & 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, boost::optional<nano::account> const & account_a)
{
work_generate (root_a, callback_a, network_params.network.publish_threshold, account_a);
work_generate (version_a, root_a, callback_a, network_params.network.publish_threshold, account_a);
}
void nano::node::work_generate (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, 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)
{
auto const & peers_l (secondary_work_peers_a ? config.secondary_work_peers : config.work_peers);
if (distributed_work.make (root_a, peers_l, callback_a, difficulty_a, account_a))
if (distributed_work.make (version_a, root_a, peers_l, callback_a, difficulty_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::root const & root_a, boost::optional<nano::account> const & account_a)
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 (root_a, network_params.network.publish_threshold, account_a);
return work_generate_blocking (version_a, root_a, network_params.network.publish_threshold, account_a);
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a, uint64_t difficulty_a, boost::optional<nano::account> const & 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 (
root_a, [&promise](boost::optional<uint64_t> opt_work_a) {
version_a, root_a, [&promise](boost::optional<uint64_t> opt_work_a) {
promise.set_value (opt_work_a);
},
difficulty_a, account_a);
return promise.get_future ().get ();
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a)
{
assert (network_params.network.is_test_network ());
return work_generate_blocking (block_a, network_params.network.publish_threshold);
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a, uint64_t difficulty_a)
{
assert (network_params.network.is_test_network ());
return work_generate_blocking (nano::work_version::work_1, block_a, difficulty_a);
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a)
{
assert (network_params.network.is_test_network ());
return work_generate_blocking (root_a, network_params.network.publish_threshold);
}
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a, uint64_t difficulty_a)
{
assert (network_params.network.is_test_network ());
return work_generate_blocking (nano::work_version::work_1, root_a, difficulty_a);
}
void nano::node::add_initial_peers ()
{
auto transaction (store.tx_begin_read ());

View file

@ -129,12 +129,12 @@ public:
bool local_work_generation_enabled () const;
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::root const &, uint64_t, boost::optional<nano::account> const & = boost::none);
boost::optional<uint64_t> work_generate_blocking (nano::root const &, boost::optional<nano::account> const & = boost::none);
void work_generate (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::root const &, std::function<void(boost::optional<uint64_t>)>, boost::optional<nano::account> const & = boost::none);
boost::optional<uint64_t> work_generate_blocking (nano::work_version const, nano::block &, uint64_t);
boost::optional<uint64_t> work_generate_blocking (nano::work_version const, 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 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 +195,15 @@ 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 &, uint64_t);
// 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 &);
private:
void long_inactivity_cleanup ();
};

View file

@ -687,13 +687,13 @@ nano::opencl_work::~opencl_work ()
}
}
boost::optional<uint64_t> nano::opencl_work::generate_work (nano::root const & root_a, uint64_t const difficulty_a)
boost::optional<uint64_t> nano::opencl_work::generate_work (nano::work_version const version_a, nano::root const & root_a, uint64_t const difficulty_a)
{
std::atomic<int> ticket_l{ 0 };
return generate_work (root_a, difficulty_a, ticket_l);
return generate_work (version_a, root_a, difficulty_a, ticket_l);
}
boost::optional<uint64_t> nano::opencl_work::generate_work (nano::root const & root_a, uint64_t const difficulty_a, std::atomic<int> & ticket_a)
boost::optional<uint64_t> nano::opencl_work::generate_work (nano::work_version const version_a, nano::root const & root_a, uint64_t const difficulty_a, std::atomic<int> & ticket_a)
{
nano::lock_guard<std::mutex> lock (mutex);
bool error (false);
@ -702,7 +702,7 @@ boost::optional<uint64_t> nano::opencl_work::generate_work (nano::root const & r
uint64_t computed_difficulty (0);
unsigned thread_count (config.threads);
size_t work_size[] = { thread_count, 0, 0 };
while ((nano::work_validate (root_a, result, &computed_difficulty) || computed_difficulty < difficulty_a) && !error && ticket_a == ticket_l)
while ((nano::work_validate (version_a, root_a, result, &computed_difficulty) || computed_difficulty < difficulty_a) && !error && ticket_a == ticket_l)
{
result = rand.next ();
cl_int write_error1 = clEnqueueWriteBuffer (queue, attempt_buffer, false, 0, sizeof (uint64_t), &result, 0, nullptr, nullptr);

View file

@ -1,5 +1,6 @@
#pragma once
#include <nano/lib/work.hpp>
#include <nano/node/openclconfig.hpp>
#include <nano/node/xorshift.hpp>
@ -40,8 +41,8 @@ class opencl_work
public:
opencl_work (bool &, nano::opencl_config const &, nano::opencl_environment &, nano::logger_mt &);
~opencl_work ();
boost::optional<uint64_t> generate_work (nano::root const &, uint64_t const);
boost::optional<uint64_t> generate_work (nano::root const &, uint64_t const, std::atomic<int> &);
boost::optional<uint64_t> generate_work (nano::work_version const, nano::root const &, uint64_t const);
boost::optional<uint64_t> generate_work (nano::work_version const, nano::root const &, uint64_t const, std::atomic<int> &);
static std::unique_ptr<opencl_work> create (bool, nano::opencl_config const &, nano::logger_mt &);
nano::opencl_config const & config;
std::mutex mutex;

View file

@ -1134,10 +1134,10 @@ bool nano::wallet::action_complete (std::shared_ptr<nano::block> const & block_a
bool error{ false };
if (block_a != nullptr)
{
if (nano::work_validate (*block_a))
if (nano::work_validate (nano::work_version::work_1, *block_a))
{
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 ();
error = !wallets.node.work_generate_blocking (nano::work_version::work_1, *block_a, wallets.node.active.limited_active_difficulty ()).is_initialized ();
}
if (!error)
{
@ -1217,7 +1217,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)
{
assert (!nano::work_validate (root_a, work_a));
assert (!nano::work_validate (nano::work_version::work_1, root_a, work_a));
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));
@ -1364,7 +1364,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 (root_a, account_a));
auto opt_work_l (wallets.node.work_generate_blocking (nano::work_version::work_1, root_a, account_a));
if (opt_work_l.is_initialized ())
{
auto transaction_l (wallets.tx_begin_write ());
@ -1401,7 +1401,7 @@ void nano::work_watcher::stop ()
stopped = true;
}
void nano::work_watcher::add (std::shared_ptr<nano::block> block_a)
void nano::work_watcher::add (std::shared_ptr<nano::block> block_a, nano::work_version const work_version_a)
{
auto block_l (std::dynamic_pointer_cast<nano::state_block> (block_a));
if (!stopped && block_l != nullptr)
@ -1410,7 +1410,7 @@ void nano::work_watcher::add (std::shared_ptr<nano::block> block_a)
nano::unique_lock<std::mutex> lock (mutex);
watched[root_l] = block_l;
lock.unlock ();
watching (root_l, block_l);
watching (work_version_a, root_l, block_l);
}
}
@ -1420,10 +1420,10 @@ void nano::work_watcher::update (nano::qualified_root const & root_a, std::share
watched[root_a] = block_a;
}
void nano::work_watcher::watching (nano::qualified_root const & root_a, std::shared_ptr<nano::state_block> block_a)
void nano::work_watcher::watching (nano::work_version const version_a, nano::qualified_root const & root_a, std::shared_ptr<nano::state_block> block_a)
{
std::weak_ptr<nano::work_watcher> watcher_w (shared_from_this ());
node.alarm.add (std::chrono::steady_clock::now () + node.config.work_watcher_period, [block_a, root_a, watcher_w]() {
node.alarm.add (std::chrono::steady_clock::now () + node.config.work_watcher_period, [block_a, version_a, root_a, watcher_w]() {
auto watcher_l = watcher_w.lock ();
if (watcher_l && !watcher_l->stopped && block_a != nullptr)
{
@ -1433,7 +1433,7 @@ void nano::work_watcher::watching (nano::qualified_root const & root_a, std::sha
lock.unlock ();
uint64_t difficulty (0);
auto root_l (block_a->root ());
nano::work_validate (root_l, block_a->block_work (), &difficulty);
nano::work_validate (version_a, root_l, block_a->block_work (), &difficulty);
auto active_difficulty (watcher_l->node.active.limited_active_difficulty ());
/*
* Work watcher should still watch blocks even without work generation, although no rework is done
@ -1442,7 +1442,7 @@ void nano::work_watcher::watching (nano::qualified_root const & root_a, std::sha
if (active_difficulty > difficulty && watcher_l->node.work_generation_enabled ())
{
watcher_l->node.work_generate (
root_l, [watcher_l, block_a, root_a](boost::optional<uint64_t> work_a) {
version_a, root_l, [watcher_l, block_a, version_a, root_a](boost::optional<uint64_t> work_a) {
if (block_a != nullptr && watcher_l != nullptr && !watcher_l->stopped)
{
bool updated_l{ false };
@ -1457,12 +1457,12 @@ void nano::work_watcher::watching (nano::qualified_root const & root_a, std::sha
watcher_l->node.active.update_difficulty (block);
watcher_l->update (root_a, block);
updated_l = true;
watcher_l->watching (root_a, block);
watcher_l->watching (version_a, root_a, block);
}
}
if (!updated_l)
{
watcher_l->watching (root_a, block_a);
watcher_l->watching (version_a, root_a, block_a);
}
}
},
@ -1470,7 +1470,7 @@ void nano::work_watcher::watching (nano::qualified_root const & root_a, std::sha
}
else
{
watcher_l->watching (root_a, block_a);
watcher_l->watching (version_a, root_a, block_a);
}
}
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <nano/lib/work.hpp>
#include <nano/node/lmdb/lmdb.hpp>
#include <nano/node/lmdb/wallet_value.hpp>
#include <nano/node/openclwork.hpp>
@ -10,7 +11,6 @@
#include <mutex>
#include <thread>
#include <unordered_set>
namespace nano
{
class node;
@ -168,9 +168,9 @@ public:
work_watcher (nano::node &);
~work_watcher ();
void stop ();
void add (std::shared_ptr<nano::block>);
void add (std::shared_ptr<nano::block>, nano::work_version const);
void update (nano::qualified_root const &, std::shared_ptr<nano::state_block>);
void watching (nano::qualified_root const &, std::shared_ptr<nano::state_block>);
void watching (work_version const, nano::qualified_root const &, std::shared_ptr<nano::state_block>);
void remove (std::shared_ptr<nano::block>);
bool is_watched (nano::qualified_root const &);
size_t size ();

View file

@ -773,7 +773,7 @@ nano::websocket::message nano::websocket::message_builder::difficulty_changed (u
return message_l;
}
nano::websocket::message nano::websocket::message_builder::work_generation (nano::block_hash const & root_a, uint64_t work_a, uint64_t difficulty_a, uint64_t publish_threshold_a, std::chrono::milliseconds const & duration_a, std::string const & peer_a, std::vector<std::string> const & bad_peers_a, bool completed_a, bool cancelled_a)
nano::websocket::message nano::websocket::message_builder::work_generation (nano::work_version const version_a, nano::block_hash const & root_a, uint64_t work_a, uint64_t difficulty_a, uint64_t publish_threshold_a, std::chrono::milliseconds const & duration_a, std::string const & peer_a, std::vector<std::string> const & bad_peers_a, bool completed_a, bool cancelled_a)
{
nano::websocket::message message_l (nano::websocket::topic::work);
set_common_fields (message_l);
@ -785,6 +785,7 @@ nano::websocket::message nano::websocket::message_builder::work_generation (nano
work_l.put ("duration", duration_a.count ());
boost::property_tree::ptree request_l;
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, publish_threshold_a));
@ -797,7 +798,7 @@ nano::websocket::message nano::websocket::message_builder::work_generation (nano
result_l.put ("source", peer_a);
result_l.put ("work", nano::to_string_hex (work_a));
uint64_t result_difficulty_l;
nano::work_validate (root_a, work_a, &result_difficulty_l);
nano::work_validate (version_a, root_a, work_a, &result_difficulty_l);
result_l.put ("difficulty", nano::to_string_hex (result_difficulty_l));
auto result_multiplier_l (nano::difficulty::to_multiplier (result_difficulty_l, publish_threshold_a));
result_l.put ("multiplier", nano::to_string (result_multiplier_l));
@ -817,14 +818,14 @@ nano::websocket::message nano::websocket::message_builder::work_generation (nano
return message_l;
}
nano::websocket::message nano::websocket::message_builder::work_cancelled (nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a)
nano::websocket::message nano::websocket::message_builder::work_cancelled (nano::work_version const version_a, nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a)
{
return work_generation (root_a, 0, difficulty_a, publish_threshold_a, duration_a, "", bad_peers_a, false, true);
return work_generation (version_a, root_a, 0, difficulty_a, publish_threshold_a, duration_a, "", bad_peers_a, false, true);
}
nano::websocket::message nano::websocket::message_builder::work_failed (nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a)
nano::websocket::message nano::websocket::message_builder::work_failed (nano::work_version const version_a, nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a)
{
return work_generation (root_a, 0, difficulty_a, publish_threshold_a, duration_a, "", bad_peers_a, false, false);
return work_generation (version_a, root_a, 0, difficulty_a, publish_threshold_a, duration_a, "", bad_peers_a, false, false);
}
nano::websocket::message nano::websocket::message_builder::bootstrap_started (std::string const & id_a, std::string const & mode_a)

View file

@ -5,6 +5,7 @@
#include <nano/boost/beast/websocket.hpp>
#include <nano/lib/blocks.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/work.hpp>
#include <nano/secure/common.hpp>
#include <boost/property_tree/json_parser.hpp>
@ -88,9 +89,10 @@ namespace websocket
message stopped_election (nano::block_hash const & hash_a);
message vote_received (std::shared_ptr<nano::vote> vote_a, nano::vote_code code_a);
message difficulty_changed (uint64_t publish_threshold_a, uint64_t difficulty_active_a);
message work_generation (nano::block_hash const & root_a, uint64_t const work_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::string const & peer_a, std::vector<std::string> const & bad_peers_a, bool const completed_a = true, bool const cancelled_a = false);
message work_cancelled (nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a);
message work_failed (nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a);
message work_generation (nano::work_version const version_a, nano::block_hash const & root_a, uint64_t const work_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::string const & peer_a, std::vector<std::string> const & bad_peers_a, bool const completed_a = true, bool const cancelled_a = false);
message work_cancelled (nano::work_version const version_a, nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a);
message work_failed (nano::work_version const version_a, nano::block_hash const & root_a, uint64_t const difficulty_a, uint64_t const publish_threshold_a, std::chrono::milliseconds const & duration_a, std::vector<std::string> const & bad_peers_a);
message bootstrap_started (std::string const & id_a, std::string const & mode_a);
message bootstrap_exited (std::string const & id_a, std::string const & mode_a, std::chrono::steady_clock::time_point const start_time_a, uint64_t const total_blocks_a);

View file

@ -2229,7 +2229,7 @@ void nano_qt::block_creation::create_send ()
(void)error;
assert (!error);
nano::state_block send (account_l, info.head, info.representative, balance - amount_l.number (), destination_l, key, account_l, 0);
if (wallet.node.work_generate_blocking (send).is_initialized ())
if (wallet.node.work_generate_blocking (nano::work_version::work_1, send).is_initialized ())
{
std::string block_l;
send.serialize_json (block_l);
@ -2308,7 +2308,7 @@ void nano_qt::block_creation::create_receive ()
if (!error)
{
nano::state_block receive (pending_key.account, info.head, info.representative, info.balance.number () + pending.amount.number (), source_l, key, pending_key.account, 0);
if (wallet.node.work_generate_blocking (receive).is_initialized ())
if (wallet.node.work_generate_blocking (nano::work_version::work_1, receive).is_initialized ())
{
std::string block_l;
receive.serialize_json (block_l);
@ -2387,7 +2387,7 @@ void nano_qt::block_creation::create_change ()
if (!error)
{
nano::state_block change (account_l, info.head, representative_l, info.balance, 0, key, account_l, 0);
if (wallet.node.work_generate_blocking (change).is_initialized ())
if (wallet.node.work_generate_blocking (nano::work_version::work_1, change).is_initialized ())
{
std::string block_l;
change.serialize_json (block_l);
@ -2464,7 +2464,7 @@ void nano_qt::block_creation::create_open ()
if (!error)
{
nano::state_block open (pending_key.account, 0, representative_l, pending.amount, source_l, key, pending_key.account, 0);
if (wallet.node.work_generate_blocking (open).is_initialized ())
if (wallet.node.work_generate_blocking (nano::work_version::work_1, open).is_initialized ())
{
std::string block_l;
open.serialize_json (block_l);

View file

@ -3045,7 +3045,7 @@ 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 (hash1, [&work](boost::optional<uint64_t> work_a) {
node2.work_generate (nano::work_version::work_1, hash1, [&work](boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
work = *work_a;
});
@ -3075,7 +3075,7 @@ TEST (rpc, work_peer_one)
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 (key1.pub, [&work](boost::optional<uint64_t> work_a) {
node2.work_generate (nano::work_version::work_1, key1.pub, [&work](boost::optional<uint64_t> work_a) {
ASSERT_TRUE (work_a.is_initialized ());
work = *work_a;
});
@ -3122,7 +3122,7 @@ TEST (rpc, work_peer_many)
for (auto i (0); i < works.size (); ++i)
{
nano::keypair key1;
node1.work_generate (key1.pub, [& work = works[i]](boost::optional<uint64_t> work_a) {
node1.work_generate (nano::work_version::work_1, key1.pub, [& work = works[i]](boost::optional<uint64_t> work_a) {
work = *work_a;
});
while (nano::work_validate (key1.pub, works[i]))
@ -3136,6 +3136,48 @@ TEST (rpc, work_peer_many)
node1.stop ();
}
TEST (rpc, work_version_invalid)
{
nano::system system;
auto node = add_ipc_enabled_node (system);
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 ();
nano::block_hash hash (1);
boost::property_tree::ptree request;
request.put ("action", "work_generate");
request.put ("hash", hash.to_string ());
request.put ("version", "work_invalid");
{
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);
ASSERT_EQ (1, response.json.count ("error"));
ASSERT_EQ (std::error_code (nano::error_rpc::bad_work_version).message (), response.json.get<std::string> ("error"));
}
request.put ("action", "work_validate");
{
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);
ASSERT_EQ (1, response.json.count ("error"));
ASSERT_EQ (std::error_code (nano::error_rpc::bad_work_version).message (), response.json.get<std::string> ("error"));
}
}
TEST (rpc, block_count)
{
{

View file

@ -754,7 +754,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 const & block_a, nano::signature_verification verification)
{
assert (!nano::work_validate (block_a));
assert (!nano::work_validate (nano::work_version::work_1, block_a));
ledger_processor processor (*this, transaction_a, verification);
block_a.visit (processor);
if (processor.result.code == nano::process_result::progress)