Epoch 2 started flag in ledger cache (#2684)
* Epoch 2 started flag in ledger cache This flag is flipped when the first epoch 2 block is successfully processed. Differences in behavior after the flag is flipped: - RPC `work_generate` uses the new epoch 2 threshold as default - RPC `work_validate` validates for the new threshold as default (breaking behavior in previous PR) - RPC `block_create` uses the new threshold as default - Node wallet pre-caches work at the new threshold * Adjust tests for node default difficulty * Also affect work websocket multiplier, and change default test work generate difficulty * Fix rpc.work_validate test * Ensure state is kept on ledger initialization (node restart)
This commit is contained in:
parent
d1d2a1d51c
commit
ed10a6fb6b
11 changed files with 182 additions and 21 deletions
|
@ -3056,3 +3056,32 @@ TEST (ledger, work_validation)
|
|||
process_block (open, {});
|
||||
process_block (epoch, nano::block_details (nano::epoch::epoch_1, false, false, true));
|
||||
}
|
||||
|
||||
TEST (ledger, epoch_2_started_flag)
|
||||
{
|
||||
nano::system system (2);
|
||||
|
||||
auto & node1 = *system.nodes[0];
|
||||
ASSERT_FALSE (node1.ledger.cache.epoch_2_started.load ());
|
||||
ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node1, nano::epoch::epoch_1));
|
||||
ASSERT_FALSE (node1.ledger.cache.epoch_2_started.load ());
|
||||
ASSERT_NE (nullptr, system.upgrade_genesis_epoch (node1, nano::epoch::epoch_2));
|
||||
ASSERT_TRUE (node1.ledger.cache.epoch_2_started.load ());
|
||||
|
||||
auto & node2 = *system.nodes[1];
|
||||
nano::keypair key;
|
||||
auto epoch1 = system.upgrade_genesis_epoch (node2, nano::epoch::epoch_1);
|
||||
ASSERT_NE (nullptr, epoch1);
|
||||
ASSERT_FALSE (node2.ledger.cache.epoch_2_started.load ());
|
||||
nano::state_block send (nano::test_genesis_key.pub, epoch1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 1, key.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (epoch1->hash ()));
|
||||
ASSERT_EQ (nano::process_result::progress, node2.process (send).code);
|
||||
ASSERT_FALSE (node2.ledger.cache.epoch_2_started.load ());
|
||||
nano::state_block epoch2 (key.pub, 0, 0, 0, node2.ledger.epoch_link (nano::epoch::epoch_2), nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (key.pub));
|
||||
ASSERT_EQ (nano::process_result::progress, node2.process (epoch2).code);
|
||||
ASSERT_TRUE (node2.ledger.cache.epoch_2_started.load ());
|
||||
|
||||
// Ensure state is kept on ledger initialization
|
||||
nano::stat stats;
|
||||
nano::ledger ledger (node1.store, stats);
|
||||
ASSERT_TRUE (ledger.cache.epoch_2_started.load ());
|
||||
}
|
||||
|
|
|
@ -649,7 +649,7 @@ TEST (wallet, work)
|
|||
uint64_t work (0);
|
||||
if (!wallet->store.work_get (transaction, nano::test_genesis_key.pub, work))
|
||||
{
|
||||
done = nano::work_difficulty (genesis.open->work_version (), genesis.hash (), work) >= nano::work_threshold_base (genesis.open->work_version ());
|
||||
done = nano::work_difficulty (genesis.open->work_version (), genesis.hash (), work) >= system.nodes[0]->default_difficulty ();
|
||||
}
|
||||
ASSERT_NO_ERROR (system.poll ());
|
||||
}
|
||||
|
@ -683,7 +683,7 @@ TEST (wallet, work_generate)
|
|||
ASSERT_NO_ERROR (system.poll ());
|
||||
auto block_transaction (node1.store.tx_begin_read ());
|
||||
auto transaction (system.wallet (0)->wallets.tx_begin_read ());
|
||||
again = wallet->store.work_get (transaction, account1, work1) || nano::work_difficulty (block->work_version (), node1.ledger.latest_root (block_transaction, account1), work1) < nano::work_threshold_base (block->work_version ());
|
||||
again = wallet->store.work_get (transaction, account1, work1) || nano::work_difficulty (block->work_version (), node1.ledger.latest_root (block_transaction, account1), work1) < node1.default_difficulty ();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -680,15 +680,15 @@ TEST (websocket, work)
|
|||
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_thresholds.base));
|
||||
ASSERT_EQ (request.get<std::string> ("difficulty"), nano::to_string_hex (node1->default_difficulty ()));
|
||||
ASSERT_EQ (request.get<double> ("multiplier"), 1.0);
|
||||
|
||||
ASSERT_EQ (1, contents.count ("result"));
|
||||
auto & result = contents.get_child ("result");
|
||||
uint64_t result_difficulty;
|
||||
nano::from_string_hex (result.get<std::string> ("difficulty"), result_difficulty);
|
||||
ASSERT_GE (result_difficulty, node1->network_params.network.publish_thresholds.base);
|
||||
ASSERT_NEAR (result.get<double> ("multiplier"), nano::difficulty::to_multiplier (result_difficulty, node1->network_params.network.publish_thresholds.base), 1e-6);
|
||||
ASSERT_GE (result_difficulty, node1->default_difficulty ());
|
||||
ASSERT_NEAR (result.get<double> ("multiplier"), nano::difficulty::to_multiplier (result_difficulty, node1->default_difficulty ()), 1e-6);
|
||||
ASSERT_EQ (result.get<std::string> ("work"), nano::to_string_hex (work.get ()));
|
||||
|
||||
ASSERT_EQ (1, contents.count ("bad_peers"));
|
||||
|
|
|
@ -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.version, request.root, work_result, request.difficulty, node_l->network_params.network.publish_thresholds.base, elapsed.value (), winner, bad_peers));
|
||||
node_l->websocket_server->broadcast (builder.work_generation (request.version, request.root, work_result, request.difficulty, node_l->default_difficulty (), elapsed.value (), winner, bad_peers));
|
||||
}
|
||||
else if (status == work_generation_status::cancelled)
|
||||
{
|
||||
node_l->websocket_server->broadcast (builder.work_cancelled (request.version, request.root, request.difficulty, node_l->network_params.network.publish_thresholds.base, elapsed.value (), bad_peers));
|
||||
node_l->websocket_server->broadcast (builder.work_cancelled (request.version, request.root, request.difficulty, node_l->default_difficulty (), 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.version, request.root, request.difficulty, node_l->network_params.network.publish_thresholds.base, elapsed.value (), bad_peers));
|
||||
node_l->websocket_server->broadcast (builder.work_failed (request.version, request.root, request.difficulty, node_l->default_difficulty (), elapsed.value (), bad_peers));
|
||||
}
|
||||
}
|
||||
stop_once (true);
|
||||
|
|
|
@ -345,7 +345,7 @@ uint64_t nano::json_handler::work_optional_impl ()
|
|||
|
||||
uint64_t nano::json_handler::difficulty_optional_impl ()
|
||||
{
|
||||
uint64_t difficulty (node.network_params.network.publish_thresholds.base);
|
||||
auto difficulty (node.default_difficulty ());
|
||||
boost::optional<std::string> difficulty_text (request.get_optional<std::string> ("difficulty"));
|
||||
if (!ec && difficulty_text.is_initialized ())
|
||||
{
|
||||
|
@ -5085,7 +5085,7 @@ void nano::json_handler::work_validate ()
|
|||
auto result_difficulty (nano::work_difficulty (work_version, hash, work));
|
||||
response_l.put ("valid", (result_difficulty >= difficulty) ? "1" : "0");
|
||||
response_l.put ("difficulty", nano::to_string_hex (result_difficulty));
|
||||
auto result_multiplier = nano::difficulty::to_multiplier (result_difficulty, node.network_params.network.publish_thresholds.base);
|
||||
auto result_multiplier = nano::difficulty::to_multiplier (result_difficulty, node.default_difficulty ());
|
||||
response_l.put ("multiplier", nano::to_string (result_multiplier));
|
||||
}
|
||||
response_errors ();
|
||||
|
|
|
@ -1004,6 +1004,11 @@ int nano::node::price (nano::uint128_t const & balance_a, int amount_a)
|
|||
return static_cast<int> (result * 100.0);
|
||||
}
|
||||
|
||||
uint64_t nano::node::default_difficulty () const
|
||||
{
|
||||
return ledger.cache.epoch_2_started ? network_params.network.publish_thresholds.base : network_params.network.publish_thresholds.epoch_1;
|
||||
}
|
||||
|
||||
bool nano::node::local_work_generation_enabled () const
|
||||
{
|
||||
return config.work_threads > 0 || work.opencl;
|
||||
|
@ -1053,13 +1058,13 @@ boost::optional<uint64_t> nano::node::work_generate_blocking (nano::work_version
|
|||
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a)
|
||||
{
|
||||
debug_assert (network_params.network.is_test_network ());
|
||||
return work_generate_blocking (block_a, network_params.network.publish_thresholds.base);
|
||||
return work_generate_blocking (block_a, default_difficulty ());
|
||||
}
|
||||
|
||||
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a)
|
||||
{
|
||||
debug_assert (network_params.network.is_test_network ());
|
||||
return work_generate_blocking (root_a, network_params.network.publish_thresholds.base);
|
||||
return work_generate_blocking (root_a, default_difficulty ());
|
||||
}
|
||||
|
||||
boost::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a, uint64_t difficulty_a)
|
||||
|
|
|
@ -126,6 +126,8 @@ public:
|
|||
void bootstrap_wallet ();
|
||||
void unchecked_cleanup ();
|
||||
int price (nano::uint128_t const &, int);
|
||||
// The default difficulty updates to base only when the first epoch_2 block is processed
|
||||
uint64_t default_difficulty () const;
|
||||
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;
|
||||
|
|
|
@ -1383,7 +1383,8 @@ void nano::wallet::work_cache_blocking (nano::account const & account_a, nano::r
|
|||
{
|
||||
if (wallets.node.work_generation_enabled ())
|
||||
{
|
||||
auto opt_work_l (wallets.node.work_generate_blocking (nano::work_version::work_1, root_a, wallets.node.network_params.network.publish_thresholds.base, account_a));
|
||||
auto difficulty (wallets.node.default_difficulty ());
|
||||
auto opt_work_l (wallets.node.work_generate_blocking (nano::work_version::work_1, root_a, difficulty, account_a));
|
||||
if (opt_work_l.is_initialized ())
|
||||
{
|
||||
auto transaction_l (wallets.tx_begin_write ());
|
||||
|
|
|
@ -2310,7 +2310,7 @@ TEST (rpc, payment_begin_end)
|
|||
ASSERT_LT (work, 50);
|
||||
}
|
||||
system.deadline_set (10s);
|
||||
while (nano::work_difficulty (nano::work_version::work_1, root1, work) < nano::work_threshold_base (nano::work_version::work_1))
|
||||
while (nano::work_difficulty (nano::work_version::work_1, root1, work) < node1->default_difficulty ())
|
||||
{
|
||||
auto ec = system.poll ();
|
||||
auto transaction (wallet->wallets.tx_begin_read ());
|
||||
|
@ -3073,6 +3073,67 @@ TEST (rpc, work_generate_multiplier)
|
|||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_generate_epoch_2)
|
||||
{
|
||||
nano::system system;
|
||||
auto node = add_ipc_enabled_node (system);
|
||||
auto epoch1 = system.upgrade_genesis_epoch (*node, nano::epoch::epoch_1);
|
||||
ASSERT_NE (nullptr, epoch1);
|
||||
scoped_io_thread_name_change scoped_thread_name_io;
|
||||
nano::node_rpc_config node_rpc_config;
|
||||
nano::ipc::ipc_server ipc_server (*node, node_rpc_config);
|
||||
nano::rpc_config rpc_config (nano::get_available_port (), true);
|
||||
rpc_config.rpc_process.ipc_port = node->config.ipc_config.transport_tcp.port;
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config);
|
||||
nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor);
|
||||
rpc.start ();
|
||||
boost::property_tree::ptree request;
|
||||
auto verify_response = [node, &rpc, &system](auto & request, nano::block_hash const & hash, uint64_t & out_difficulty) {
|
||||
request.put ("hash", hash.to_string ());
|
||||
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);
|
||||
auto work_text (response.json.get<std::string> ("work"));
|
||||
uint64_t work{ 0 };
|
||||
ASSERT_FALSE (nano::from_string_hex (work_text, work));
|
||||
out_difficulty = nano::work_difficulty (nano::work_version::work_1, hash, work);
|
||||
};
|
||||
request.put ("action", "work_generate");
|
||||
// Before upgrading to epoch 2 should use epoch_1 difficulty as default
|
||||
{
|
||||
unsigned const max_tries = 30;
|
||||
uint64_t difficulty{ 0 };
|
||||
unsigned tries = 0;
|
||||
while (++tries < max_tries)
|
||||
{
|
||||
verify_response (request, epoch1->hash (), difficulty);
|
||||
if (difficulty < node->network_params.network.publish_thresholds.base)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSERT_LT (tries, max_tries);
|
||||
}
|
||||
// After upgrading, should always use the higher difficulty by default
|
||||
ASSERT_EQ (node->network_params.network.publish_thresholds.epoch_2, node->network_params.network.publish_thresholds.base);
|
||||
scoped_thread_name_io.reset ();
|
||||
auto epoch2 = system.upgrade_genesis_epoch (*node, nano::epoch::epoch_2);
|
||||
ASSERT_NE (nullptr, epoch2);
|
||||
scoped_thread_name_io.renew ();
|
||||
{
|
||||
for (auto i = 0; i < 5; ++i)
|
||||
{
|
||||
uint64_t difficulty{ 0 };
|
||||
verify_response (request, epoch1->hash (), difficulty);
|
||||
ASSERT_GE (difficulty, node->network_params.network.publish_thresholds.base);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_cancel)
|
||||
{
|
||||
nano::system system;
|
||||
|
@ -3917,7 +3978,6 @@ TEST (rpc, wallet_frontiers)
|
|||
|
||||
TEST (rpc, work_validate)
|
||||
{
|
||||
nano::network_params params;
|
||||
nano::system system;
|
||||
auto & node1 = *add_ipc_enabled_node (system);
|
||||
nano::keypair key;
|
||||
|
@ -3950,9 +4010,9 @@ TEST (rpc, work_validate)
|
|||
std::string difficulty_text (response.json.get<std::string> ("difficulty"));
|
||||
uint64_t difficulty;
|
||||
ASSERT_FALSE (nano::from_string_hex (difficulty_text, difficulty));
|
||||
ASSERT_GE (difficulty, params.network.publish_thresholds.base);
|
||||
ASSERT_GE (difficulty, node1.default_difficulty ());
|
||||
double multiplier (response.json.get<double> ("multiplier"));
|
||||
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (difficulty, params.network.publish_thresholds.base), 1e-6);
|
||||
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (difficulty, node1.default_difficulty ()), 1e-6);
|
||||
}
|
||||
uint64_t work2 (0);
|
||||
request.put ("work", nano::to_string_hex (work2));
|
||||
|
@ -3969,12 +4029,12 @@ TEST (rpc, work_validate)
|
|||
std::string difficulty_text (response.json.get<std::string> ("difficulty"));
|
||||
uint64_t difficulty;
|
||||
ASSERT_FALSE (nano::from_string_hex (difficulty_text, difficulty));
|
||||
ASSERT_GE (params.network.publish_thresholds.base, difficulty);
|
||||
ASSERT_GE (node1.default_difficulty (), difficulty);
|
||||
double multiplier (response.json.get<double> ("multiplier"));
|
||||
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (difficulty, params.network.publish_thresholds.base), 1e-6);
|
||||
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (difficulty, node1.default_difficulty ()), 1e-6);
|
||||
}
|
||||
auto result_difficulty (nano::work_difficulty (nano::work_version::work_1, hash, work1));
|
||||
ASSERT_GE (result_difficulty, params.network.publish_thresholds.base);
|
||||
ASSERT_GE (result_difficulty, node1.default_difficulty ());
|
||||
request.put ("work", nano::to_string_hex (work1));
|
||||
request.put ("difficulty", nano::to_string_hex (result_difficulty));
|
||||
{
|
||||
|
@ -4017,6 +4077,62 @@ TEST (rpc, work_validate)
|
|||
}
|
||||
}
|
||||
|
||||
TEST (rpc, work_validate_epoch_2)
|
||||
{
|
||||
nano::system system;
|
||||
auto node = add_ipc_enabled_node (system);
|
||||
auto epoch1 = system.upgrade_genesis_epoch (*node, nano::epoch::epoch_1);
|
||||
ASSERT_NE (nullptr, epoch1);
|
||||
ASSERT_EQ (node->network_params.network.publish_thresholds.epoch_2, node->network_params.network.publish_thresholds.base);
|
||||
auto work = system.work_generate_limited (epoch1->hash (), node->network_params.network.publish_thresholds.epoch_1, node->network_params.network.publish_thresholds.base);
|
||||
scoped_io_thread_name_change scoped_thread_name_io;
|
||||
nano::node_rpc_config node_rpc_config;
|
||||
nano::ipc::ipc_server ipc_server (*node, node_rpc_config);
|
||||
nano::rpc_config rpc_config (nano::get_available_port (), true);
|
||||
rpc_config.rpc_process.ipc_port = node->config.ipc_config.transport_tcp.port;
|
||||
nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config);
|
||||
nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor);
|
||||
rpc.start ();
|
||||
boost::property_tree::ptree request;
|
||||
request.put ("action", "work_validate");
|
||||
request.put ("hash", epoch1->hash ().to_string ());
|
||||
request.put ("work", nano::to_string_hex (work));
|
||||
{
|
||||
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_TRUE (response.json.get<bool> ("valid"));
|
||||
std::string difficulty_text (response.json.get<std::string> ("difficulty"));
|
||||
uint64_t difficulty{ 0 };
|
||||
ASSERT_FALSE (nano::from_string_hex (difficulty_text, difficulty));
|
||||
double multiplier (response.json.get<double> ("multiplier"));
|
||||
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (difficulty, node->network_params.network.publish_thresholds.epoch_1), 1e-6);
|
||||
};
|
||||
// After upgrading, the higher difficulty is used to validate and calculate the multiplier
|
||||
scoped_thread_name_io.reset ();
|
||||
ASSERT_NE (nullptr, system.upgrade_genesis_epoch (*node, nano::epoch::epoch_2));
|
||||
scoped_thread_name_io.renew ();
|
||||
{
|
||||
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_FALSE (response.json.get<bool> ("valid"));
|
||||
std::string difficulty_text (response.json.get<std::string> ("difficulty"));
|
||||
uint64_t difficulty{ 0 };
|
||||
ASSERT_FALSE (nano::from_string_hex (difficulty_text, difficulty));
|
||||
double multiplier (response.json.get<double> ("multiplier"));
|
||||
ASSERT_NEAR (multiplier, nano::difficulty::to_multiplier (difficulty, node->network_params.network.publish_thresholds.base), 1e-6);
|
||||
};
|
||||
}
|
||||
|
||||
TEST (rpc, successors)
|
||||
{
|
||||
nano::system system;
|
||||
|
@ -5911,7 +6027,7 @@ TEST (rpc, block_create_state_request_work)
|
|||
boost::property_tree::read_json (block_stream, block_l);
|
||||
auto block (nano::deserialize_block_json (block_l));
|
||||
ASSERT_NE (nullptr, block);
|
||||
ASSERT_GE (block->difficulty (), nano::work_threshold_base (nano::work_version::work_1));
|
||||
ASSERT_GE (block->difficulty (), node->default_difficulty ());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -508,6 +508,7 @@ public:
|
|||
std::atomic<uint64_t> block_count{ 0 };
|
||||
std::atomic<uint64_t> unchecked_count{ 0 };
|
||||
std::atomic<uint64_t> account_count{ 0 };
|
||||
std::atomic<bool> epoch_2_started{ 0 };
|
||||
};
|
||||
|
||||
/* Defines the possible states for an election to stop in */
|
||||
|
|
|
@ -439,6 +439,10 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a)
|
|||
{
|
||||
ledger.store.frontier_del (transaction, info.head);
|
||||
}
|
||||
if (epoch == nano::epoch::epoch_2)
|
||||
{
|
||||
ledger.cache.epoch_2_started.store (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -725,12 +729,15 @@ check_bootstrap_weights (true)
|
|||
auto transaction = store.tx_begin_read ();
|
||||
if (generate_cache_a.reps || generate_cache_a.account_count)
|
||||
{
|
||||
bool epoch_2_started_l{ false };
|
||||
for (auto i (store.latest_begin (transaction)), n (store.latest_end ()); i != n; ++i)
|
||||
{
|
||||
nano::account_info const & info (i->second);
|
||||
cache.rep_weights.representation_add (info.representative, info.balance.number ());
|
||||
++cache.account_count;
|
||||
epoch_2_started_l = epoch_2_started_l || info.epoch () == nano::epoch::epoch_2;
|
||||
}
|
||||
cache.epoch_2_started.store (epoch_2_started_l);
|
||||
}
|
||||
|
||||
if (generate_cache_a.cemented_count)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue