Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing dynamic re-work and trend from elections #1968

Merged
merged 24 commits into from
May 13, 2019
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nano/core_test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_executable (core_test
block.cpp
block_store.cpp
conflicts.cpp
difficulty.cpp
entry.cpp
gap_cache.cpp
interface.cpp
Expand Down
35 changes: 35 additions & 0 deletions nano/core_test/difficulty.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#include <gtest/gtest.h>

#include <nano/lib/numbers.hpp>

TEST (difficulty, multipliers)
{
{
uint64_t base = 0xff00000000000000;
uint64_t difficulty = 0xfff27e7a57c285cd;
double expected_multiplier = 18.95461493377003;

ASSERT_NEAR (expected_multiplier, nano::difficulty::to_multiplier (difficulty, base), 1e-10);
ASSERT_EQ (difficulty, nano::difficulty::from_multiplier (expected_multiplier, base));
}

{
uint64_t base = 0xffffffc000000000;
uint64_t difficulty = 0xfffffe0000000000;
double expected_multiplier = 0.125;

auto multiplier = nano::difficulty::to_multiplier (difficulty, base);
ASSERT_NEAR (expected_multiplier, nano::difficulty::to_multiplier (difficulty, base), 1e-10);
ASSERT_EQ (difficulty, nano::difficulty::from_multiplier (expected_multiplier, base));
}

{
uint64_t base = 0xffffffc000000000;
uint64_t difficulty_nil = 0;
double multiplier_nil = 0.;
#ifndef NDEBUG
ASSERT_DEATH_IF_SUPPORTED (nano::difficulty::to_multiplier (difficulty_nil, base), "");
ASSERT_DEATH_IF_SUPPORTED (nano::difficulty::from_multiplier (multiplier_nil, base), "");
#endif
}
}
13 changes: 7 additions & 6 deletions nano/core_test/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2462,29 +2462,30 @@ TEST (active_difficulty, recalculate_work)
node1.work_generate_blocking (*send1);
uint64_t difficulty1;
nano::work_validate (*send1, &difficulty1);
auto multiplier1 = nano::difficulty::to_multiplier (difficulty1, node1.network_params.network.publish_threshold);
// Process as local block
node1.process_active (send1);
system.deadline_set (2s);
while (node1.active.empty ())
{
ASSERT_NO_ERROR (system.poll ());
}
auto sum (std::accumulate (node1.active.difficulty_cb.begin (), node1.active.difficulty_cb.end (), nano::uint128_t (0)));
ASSERT_EQ (node1.active.active_difficulty (), static_cast<uint64_t> (sum / node1.active.difficulty_cb.size ()));
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_threshold));
std::unique_lock<std::mutex> lock (node1.active.mutex);
// Fake history records to force work recalculation
for (auto i (0); i < node1.active.difficulty_cb.size (); i++)
for (auto i (0); i < node1.active.multipliers_cb.size (); i++)
{
node1.active.difficulty_cb.push_back (difficulty1 + 10000);
node1.active.multipliers_cb.push_back (multiplier1 * (1 + i / 100.));
}
node1.work_generate_blocking (*send1);
uint64_t difficulty2;
nano::work_validate (*send1, &difficulty2);
node1.process_active (send1);
node1.active.update_active_difficulty (lock);
lock.unlock ();
sum = std::accumulate (node1.active.difficulty_cb.begin (), node1.active.difficulty_cb.end (), nano::uint128_t (0));
ASSERT_EQ (node1.active.active_difficulty (), static_cast<uint64_t> (sum / node1.active.difficulty_cb.size ()));
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_threshold));
}

namespace
Expand Down
7 changes: 4 additions & 3 deletions nano/core_test/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1051,16 +1051,17 @@ TEST (wallet, update_work_action)
auto const block (wallet.send_action (nano::test_genesis_key.pub, key.pub, nano::genesis_amount));
uint64_t difficulty1 (0);
nano::work_validate (*block, &difficulty1);
auto multiplier1 = nano::difficulty::to_multiplier (difficulty1, node.network_params.network.publish_threshold);
system.deadline_set (10s);
auto updated (false);
uint64_t updated_difficulty;
while (!updated)
{
std::unique_lock<std::mutex> lock (node.active.mutex);
//fill difficulty_cb and update active difficulty;
for (auto i (0); i < node.active.difficulty_cb.size (); i++)
//fill multipliers_cb and update active difficulty;
for (auto i (0); i < node.active.multipliers_cb.size (); i++)
{
node.active.difficulty_cb.push_back (difficulty1 + 10000);
node.active.multipliers_cb.push_back (multiplier1 * (1 + i / 100.));
}
node.active.update_active_difficulty (lock);
auto const existing (node.active.roots.find (block->qualified_root ()));
Expand Down
12 changes: 12 additions & 0 deletions nano/lib/numbers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -773,3 +773,15 @@ std::string nano::uint128_union::to_string_dec () const
encode_dec (result);
return result;
}

uint64_t nano::difficulty::from_multiplier (double const multiplier_a, uint64_t const base_difficulty_a)
{
assert (multiplier_a > 0.);
return (-static_cast<uint64_t> ((-base_difficulty_a) / multiplier_a));
}

double nano::difficulty::to_multiplier (uint64_t const difficulty_a, uint64_t const base_difficulty_a)
{
assert (difficulty_a > 0);
return static_cast<double> (-base_difficulty_a) / (-difficulty_a);
}
6 changes: 6 additions & 0 deletions nano/lib/numbers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ bool validate_message (nano::public_key const &, nano::uint256_union const &, na
bool validate_message_batch (const unsigned char **, size_t *, const unsigned char **, const unsigned char **, size_t, int *);
void deterministic_key (nano::uint256_union const &, uint32_t, nano::uint256_union &);
nano::public_key pub_key (nano::private_key const &);

namespace difficulty
{
uint64_t from_multiplier (double const, uint64_t const);
double to_multiplier (uint64_t const, uint64_t const);
}
}

namespace std
Expand Down
53 changes: 23 additions & 30 deletions nano/node/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ using namespace std::chrono;

nano::active_transactions::active_transactions (nano::node & node_a, bool delay_frontier_confirmation_height_updating) :
node (node_a),
difficulty_cb (20, node.network_params.network.publish_threshold),
multipliers_cb (20, 1.),
trended_active_difficulty (node.network_params.network.publish_threshold),
next_frontier_check (steady_clock::now () + (delay_frontier_confirmation_height_updating ? 60s : 0s)),
thread ([this]() {
Expand Down Expand Up @@ -447,7 +447,7 @@ void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash
remaining_blocks.emplace_back (hash_a, 0);
std::unordered_set<nano::block_hash> processed_blocks;
std::vector<std::pair<nano::qualified_root, int64_t>> elections_list;
uint128_t sum (0);
double sum (0.);
while (!remaining_blocks.empty ())
{
auto const & item (remaining_blocks.front ());
Expand Down Expand Up @@ -482,61 +482,54 @@ void nano::active_transactions::adjust_difficulty (nano::block_hash const & hash
auto existing_root (roots.find (root));
if (existing_root != roots.end ())
{
sum += existing_root->difficulty;
sum += nano::difficulty::to_multiplier (existing_root->difficulty, node.network_params.network.publish_threshold);
elections_list.emplace_back (root, level);
}
}
}
remaining_blocks.pop_front ();
}
if (elections_list.size () > 1)
if (!elections_list.empty ())
{
uint64_t average (static_cast<uint64_t> (sum / elections_list.size ()));
// Potential overflow check
uint64_t divider (1);
if (elections_list.size () > 1000000 && (average - node.network_params.network.publish_threshold) > elections_list.size ())
double multiplier = sum / elections_list.size ();
uint64_t average = nano::difficulty::from_multiplier (multiplier, node.network_params.network.publish_threshold);
auto highest_level = elections_list.back ().second;
uint64_t divider = 1;
// Possible overflow check, will not occur for negative levels
if ((multiplier + highest_level) > 10000000000)
{
divider = ((average - node.network_params.network.publish_threshold) / elections_list.size ()) + 1;
divider = ((multiplier + highest_level) / 10000000000) + 1;
}

// Set adjusted difficulty
for (auto & item : elections_list)
{
auto existing_root (roots.find (item.first));
uint64_t difficulty_a (average + (item.second / divider));
uint64_t difficulty_a = average + item.second / divider;
roots.modify (existing_root, [difficulty_a](nano::conflict_info & info_a) {
info_a.adjusted_difficulty = difficulty_a;
});
}
}
// Set adjusted difficulty equals to difficulty
else if (elections_list.size () == 1)
{
auto existing_root (roots.find (elections_list.begin ()->first));
if (existing_root->difficulty != existing_root->adjusted_difficulty)
{
roots.modify (existing_root, [](nano::conflict_info & info_a) {
info_a.adjusted_difficulty = info_a.difficulty;
});
}
}
}

void nano::active_transactions::update_active_difficulty (std::unique_lock<std::mutex> & lock_a)
{
assert (lock_a.mutex () == &mutex && lock_a.owns_lock ());
uint64_t difficulty (node.network_params.network.publish_threshold);
double multiplier (1.);
if (!roots.empty ())
{
uint128_t min = roots.get<1> ().begin ()->adjusted_difficulty;
assert (min >= node.network_params.network.publish_threshold);
uint128_t max = (--roots.get<1> ().end ())->adjusted_difficulty;
uint64_t max = roots.get<1> ().begin ()->adjusted_difficulty;
assert (max >= node.network_params.network.publish_threshold);
difficulty = static_cast<uint64_t> ((min + max) / 2);
uint64_t min = (--roots.get<1> ().end ())->adjusted_difficulty;
assert (min >= node.network_params.network.publish_threshold);

multiplier = 0.5 * (nano::difficulty::to_multiplier (min, node.network_params.network.publish_threshold) + nano::difficulty::to_multiplier (max, node.network_params.network.publish_threshold));
}
assert (difficulty >= node.network_params.network.publish_threshold);
difficulty_cb.push_front (difficulty);
auto sum (std::accumulate (node.active.difficulty_cb.begin (), node.active.difficulty_cb.end (), uint128_t (0)));
difficulty = static_cast<uint64_t> (sum / difficulty_cb.size ());
assert (multiplier >= 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_threshold);
assert (difficulty >= node.network_params.network.publish_threshold);
trended_active_difficulty = difficulty;
}
Expand Down
2 changes: 1 addition & 1 deletion nano/node/active_transactions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class active_transactions final
static unsigned constexpr announcement_long = 20;
static size_t constexpr election_history_size = 2048;
static size_t constexpr max_broadcast_queue = 1000;
boost::circular_buffer<uint64_t> difficulty_cb;
boost::circular_buffer<double> multipliers_cb;
uint64_t trended_active_difficulty;

private:
Expand Down
7 changes: 5 additions & 2 deletions nano/node/json_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,7 +836,10 @@ void nano::json_handler::accounts_pending ()
void nano::json_handler::active_difficulty ()
{
response_l.put ("difficulty_threshold", nano::to_string_hex (node.network_params.network.publish_threshold));
response_l.put ("difficulty_active", nano::to_string_hex (node.active.active_difficulty ()));
auto difficulty_active = node.active.active_difficulty ();
response_l.put ("difficulty_active", nano::to_string_hex (difficulty_active));
float multiplier = nano::difficulty::to_multiplier (difficulty_active, node.network_params.network.publish_threshold);
response_l.put ("multiplier", std::to_string (multiplier));
response_errors ();
}

Expand Down Expand Up @@ -4401,7 +4404,7 @@ void nano::json_handler::work_validate ()
bool valid (!invalid && result_difficulty >= difficulty);
response_l.put ("valid", valid ? "1" : "0");
response_l.put ("value", nano::to_string_hex (result_difficulty));
float multiplier = static_cast<float> (-node.network_params.network.publish_threshold) / (-result_difficulty);
float multiplier = nano::difficulty::to_multiplier (result_difficulty, node.network_params.network.publish_threshold);
response_l.put ("multiplier", std::to_string (multiplier));
}
response_errors ();
Expand Down
3 changes: 3 additions & 0 deletions nano/node/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,9 @@ void nano::work_watcher::run ()
}
node.network.flood_block (block);
node.active.update_difficulty (*block.get ());
lock.lock ();
i.second = block;
lock.unlock ();
}
lock.lock ();
}
Expand Down
26 changes: 4 additions & 22 deletions nano/rpc_test/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3216,6 +3216,8 @@ TEST (rpc, work_validate)
uint64_t value;
ASSERT_FALSE (nano::from_string_hex (value_text, value));
ASSERT_GE (value, params.network.publish_threshold);
double multiplier (response.json.get<double> ("multiplier"));
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (value, params.network.publish_threshold), 1e-6);
}
uint64_t work2 (0);
request.put ("work", nano::to_string_hex (work2));
Expand All @@ -3233,6 +3235,8 @@ TEST (rpc, work_validate)
uint64_t value;
ASSERT_FALSE (nano::from_string_hex (value_text, value));
ASSERT_GE (params.network.publish_threshold, value);
double multiplier (response.json.get<double> ("multiplier"));
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (value, params.network.publish_threshold), 1e-6);
}
uint64_t result_difficulty;
ASSERT_FALSE (nano::work_validate (hash, work1, &result_difficulty));
Expand Down Expand Up @@ -3277,28 +3281,6 @@ TEST (rpc, work_validate)
bool validate (response.json.get<bool> ("valid"));
ASSERT_TRUE (validate);
}
// Test the multiplier field in the response
// It's a multiplier from the base network threshold, so we make sure the test network threshold has not been changed
// The work and its value/multiplier were calculated beforehand
ASSERT_EQ (params.network.publish_threshold, 0xff00000000000000);
request.put ("work", nano::to_string_hex (0x4b52c90f538bbb60));
{
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);
bool validate (response.json.get<bool> ("valid"));
ASSERT_TRUE (validate);
std::string value_text (response.json.get<std::string> ("value"));
uint64_t value;
ASSERT_FALSE (nano::from_string_hex (value_text, value));
ASSERT_EQ (value, 0xfff27e7a57c285cd);
double multiplier (response.json.get<double> ("multiplier"));
ASSERT_NEAR (multiplier, 18.9546, 1e-4);
}
}

TEST (rpc, successors)
Expand Down
2 changes: 1 addition & 1 deletion nano/secure/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ char const * test_genesis_data = R"%%%({
"source": "B0311EA55708D6A53C75CDBF88300259C6D018522FE3D4D0A242E431F9E8B6D0",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo",
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo",
"work": "9680625b39d3363d",
"work": "7b42a00ee91d5810",
"signature": "ECDA914373A2F0CA1296475BAEE40500A7F0A7AD72A5A80C81D7FAB7F6C802B2CC7DB50F5DD0FB25B2EF11761FA7344A158DD5A700B21BD47DE5BD0F63153A02"
})%%%";

Expand Down