From e822707d90d30642502c7a21d819f482019fa28a Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Wed, 15 May 2019 11:57:20 +0100 Subject: [PATCH] Work multiplier format and RPC work_validate fix for lower difficulties (#1984) * Format multipliers using fixed and max precision rather than scientific * Fix RPC work_validate returning invalid for difficulty lower than threshold * Move nano::to/from_string_hex to lib/numbers.hpp * Parameter can be const in nano::to_string_hex * Extract nano::to_string method for converting double with fixed and custom precision. Use std streams rather than boost * Fix intermitent failure of rpc.work_generate test * Prevent crash in debug if given difficulty is 0 --- nano/lib/blocks.cpp | 37 ------------------------------- nano/lib/blocks.hpp | 2 -- nano/lib/numbers.cpp | 45 ++++++++++++++++++++++++++++++++++++++ nano/lib/numbers.hpp | 10 +++++++++ nano/lib/work.cpp | 2 +- nano/node/json_handler.cpp | 11 +++++----- nano/rpc_test/rpc.cpp | 2 +- 7 files changed, 62 insertions(+), 47 deletions(-) diff --git a/nano/lib/blocks.cpp b/nano/lib/blocks.cpp index e424ca94c..58462021a 100644 --- a/nano/lib/blocks.cpp +++ b/nano/lib/blocks.cpp @@ -16,43 +16,6 @@ bool blocks_equal (T const & first, nano::block const & second) } } -std::string nano::to_string_hex (uint64_t value_a) -{ - std::stringstream stream; - stream << std::hex << std::noshowbase << std::setw (16) << std::setfill ('0'); - stream << value_a; - return stream.str (); -} - -bool nano::from_string_hex (std::string const & value_a, uint64_t & target_a) -{ - auto error (value_a.empty ()); - if (!error) - { - error = value_a.size () > 16; - if (!error) - { - std::stringstream stream (value_a); - stream << std::hex << std::noshowbase; - try - { - uint64_t number_l; - stream >> number_l; - target_a = number_l; - if (!stream.eof ()) - { - error = true; - } - } - catch (std::runtime_error &) - { - error = true; - } - } - } - return error; -} - std::string nano::block::to_json () const { std::string result; diff --git a/nano/lib/blocks.hpp b/nano/lib/blocks.hpp index c56137785..b05b147c6 100644 --- a/nano/lib/blocks.hpp +++ b/nano/lib/blocks.hpp @@ -12,8 +12,6 @@ namespace nano { -std::string to_string_hex (uint64_t); -bool from_string_hex (std::string const &, uint64_t &); // We operate on streams of uint8_t by convention using stream = std::basic_streambuf; // Read a raw byte stream the size of `T' and fill value. diff --git a/nano/lib/numbers.cpp b/nano/lib/numbers.cpp index b49b7f094..a632df57e 100644 --- a/nano/lib/numbers.cpp +++ b/nano/lib/numbers.cpp @@ -774,6 +774,51 @@ std::string nano::uint128_union::to_string_dec () const return result; } +std::string nano::to_string_hex (uint64_t const value_a) +{ + std::stringstream stream; + stream << std::hex << std::noshowbase << std::setw (16) << std::setfill ('0'); + stream << value_a; + return stream.str (); +} + +bool nano::from_string_hex (std::string const & value_a, uint64_t & target_a) +{ + auto error (value_a.empty ()); + if (!error) + { + error = value_a.size () > 16; + if (!error) + { + std::stringstream stream (value_a); + stream << std::hex << std::noshowbase; + try + { + uint64_t number_l; + stream >> number_l; + target_a = number_l; + if (!stream.eof ()) + { + error = true; + } + } + catch (std::runtime_error &) + { + error = true; + } + } + } + return error; +} + +std::string nano::to_string (double const value_a, int const precision_a) +{ + std::stringstream stream; + stream << std::setprecision (precision_a) << std::fixed; + stream << value_a; + return stream.str (); +} + uint64_t nano::difficulty::from_multiplier (double const multiplier_a, uint64_t const base_difficulty_a) { assert (multiplier_a > 0.); diff --git a/nano/lib/numbers.hpp b/nano/lib/numbers.hpp index 1b583548c..f67a98d13 100644 --- a/nano/lib/numbers.hpp +++ b/nano/lib/numbers.hpp @@ -127,6 +127,16 @@ bool validate_message_batch (const unsigned char **, size_t *, const unsigned ch void deterministic_key (nano::uint256_union const &, uint32_t, nano::uint256_union &); nano::public_key pub_key (nano::private_key const &); +/* Conversion methods */ +std::string to_string_hex (uint64_t const); +bool from_string_hex (std::string const &, uint64_t &); + +/** + * Convert a double to string in fixed format + * @param precision_a (optional) use a specific precision (default is the maximum) + */ +std::string to_string (double const, int const precision_a = std::numeric_limits::digits10); + namespace difficulty { uint64_t from_multiplier (double const, uint64_t const); diff --git a/nano/lib/work.cpp b/nano/lib/work.cpp index 5c7b5f1d1..f501ec86b 100644 --- a/nano/lib/work.cpp +++ b/nano/lib/work.cpp @@ -115,7 +115,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 (work_value (current_l.item, work) == output); + assert (current_l.difficulty == 0 || work_value (current_l.item, work) == output); // Signal other threads to stop their work next time they check ticket ++ticket; pending.pop_front (); diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index 189f821c3..21305f443 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -839,7 +839,7 @@ void nano::json_handler::active_difficulty () auto difficulty_active = node.active.active_difficulty (); response_l.put ("difficulty_active", nano::to_string_hex (difficulty_active)); auto multiplier = nano::difficulty::to_multiplier (difficulty_active, node.network_params.network.publish_threshold); - response_l.put ("multiplier", std::to_string (multiplier)); + response_l.put ("multiplier", nano::to_string (multiplier)); response_errors (); } @@ -4363,7 +4363,7 @@ void nano::json_handler::work_generate () nano::work_validate (hash, work_a.value (), &result_difficulty); response_l.put ("difficulty", nano::to_string_hex (result_difficulty)); auto multiplier = nano::difficulty::to_multiplier (result_difficulty, this->node.network_params.network.publish_threshold); - response_l.put ("multiplier", multiplier); + response_l.put ("multiplier", nano::to_string (multiplier)); boost::property_tree::write_json (ostream, response_l); rpc_l->response (ostream.str ()); } @@ -4451,12 +4451,11 @@ void nano::json_handler::work_validate () if (!ec) { uint64_t result_difficulty (0); - bool invalid (nano::work_validate (hash, work, &result_difficulty)); - bool valid (!invalid && result_difficulty >= difficulty); - response_l.put ("valid", valid ? "1" : "0"); + nano::work_validate (hash, work, &result_difficulty); + response_l.put ("valid", (result_difficulty >= difficulty) ? "1" : "0"); response_l.put ("difficulty", nano::to_string_hex (result_difficulty)); auto multiplier = nano::difficulty::to_multiplier (result_difficulty, node.network_params.network.publish_threshold); - response_l.put ("multiplier", multiplier); + response_l.put ("multiplier", nano::to_string (multiplier)); } response_errors (); } diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 7ca2bfdf6..305239a2e 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -2462,7 +2462,7 @@ TEST (rpc, work_generate) ASSERT_FALSE (nano::from_string_hex (response_difficulty_text, response_difficulty)); ASSERT_EQ (result_difficulty, response_difficulty); auto multiplier = response.json.get ("multiplier"); - ASSERT_EQ (nano::difficulty::to_multiplier (result_difficulty, node->network_params.network.publish_threshold), multiplier); + ASSERT_NEAR (nano::difficulty::to_multiplier (result_difficulty, node->network_params.network.publish_threshold), multiplier, 1e-6); } TEST (rpc, work_generate_difficulty)