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:
Guilherme Lawless 2020-03-26 19:42:06 +00:00 committed by GitHub
commit ed10a6fb6b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 182 additions and 21 deletions

View file

@ -3056,3 +3056,32 @@ TEST (ledger, work_validation)
process_block (open, {}); process_block (open, {});
process_block (epoch, nano::block_details (nano::epoch::epoch_1, false, false, true)); 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 ());
}

View file

@ -649,7 +649,7 @@ TEST (wallet, work)
uint64_t work (0); uint64_t work (0);
if (!wallet->store.work_get (transaction, nano::test_genesis_key.pub, work)) 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 ()); ASSERT_NO_ERROR (system.poll ());
} }
@ -683,7 +683,7 @@ TEST (wallet, work_generate)
ASSERT_NO_ERROR (system.poll ()); ASSERT_NO_ERROR (system.poll ());
auto block_transaction (node1.store.tx_begin_read ()); auto block_transaction (node1.store.tx_begin_read ());
auto transaction (system.wallet (0)->wallets.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 ();
} }
} }

View file

@ -680,15 +680,15 @@ TEST (websocket, work)
auto & request = contents.get_child ("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> ("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> ("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 (request.get<double> ("multiplier"), 1.0);
ASSERT_EQ (1, contents.count ("result")); ASSERT_EQ (1, contents.count ("result"));
auto & result = contents.get_child ("result"); auto & result = contents.get_child ("result");
uint64_t result_difficulty; uint64_t result_difficulty;
nano::from_string_hex (result.get<std::string> ("difficulty"), 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_GE (result_difficulty, node1->default_difficulty ());
ASSERT_NEAR (result.get<double> ("multiplier"), nano::difficulty::to_multiplier (result_difficulty, node1->network_params.network.publish_thresholds.base), 1e-6); 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 (result.get<std::string> ("work"), nano::to_string_hex (work.get ()));
ASSERT_EQ (1, contents.count ("bad_peers")); ASSERT_EQ (1, contents.count ("bad_peers"));

View file

@ -43,15 +43,15 @@ nano::distributed_work::~distributed_work ()
nano::websocket::message_builder builder; nano::websocket::message_builder builder;
if (status == work_generation_status::success) 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) 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) 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); stop_once (true);

View file

@ -345,7 +345,7 @@ uint64_t nano::json_handler::work_optional_impl ()
uint64_t nano::json_handler::difficulty_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")); boost::optional<std::string> difficulty_text (request.get_optional<std::string> ("difficulty"));
if (!ec && difficulty_text.is_initialized ()) 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)); auto result_difficulty (nano::work_difficulty (work_version, hash, work));
response_l.put ("valid", (result_difficulty >= difficulty) ? "1" : "0"); response_l.put ("valid", (result_difficulty >= difficulty) ? "1" : "0");
response_l.put ("difficulty", nano::to_string_hex (result_difficulty)); 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_l.put ("multiplier", nano::to_string (result_multiplier));
} }
response_errors (); response_errors ();

View file

@ -1004,6 +1004,11 @@ int nano::node::price (nano::uint128_t const & balance_a, int amount_a)
return static_cast<int> (result * 100.0); 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 bool nano::node::local_work_generation_enabled () const
{ {
return config.work_threads > 0 || work.opencl; 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) boost::optional<uint64_t> nano::node::work_generate_blocking (nano::block & block_a)
{ {
debug_assert (network_params.network.is_test_network ()); 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) boost::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a)
{ {
debug_assert (network_params.network.is_test_network ()); 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) boost::optional<uint64_t> nano::node::work_generate_blocking (nano::root const & root_a, uint64_t difficulty_a)

View file

@ -126,6 +126,8 @@ public:
void bootstrap_wallet (); void bootstrap_wallet ();
void unchecked_cleanup (); void unchecked_cleanup ();
int price (nano::uint128_t const &, int); 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 local_work_generation_enabled () const;
bool work_generation_enabled () const; bool work_generation_enabled () const;
bool work_generation_enabled (std::vector<std::pair<std::string, uint16_t>> const &) const; bool work_generation_enabled (std::vector<std::pair<std::string, uint16_t>> const &) const;

View file

@ -1383,7 +1383,8 @@ void nano::wallet::work_cache_blocking (nano::account const & account_a, nano::r
{ {
if (wallets.node.work_generation_enabled ()) 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 ()) if (opt_work_l.is_initialized ())
{ {
auto transaction_l (wallets.tx_begin_write ()); auto transaction_l (wallets.tx_begin_write ());

View file

@ -2310,7 +2310,7 @@ TEST (rpc, payment_begin_end)
ASSERT_LT (work, 50); ASSERT_LT (work, 50);
} }
system.deadline_set (10s); 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 ec = system.poll ();
auto transaction (wallet->wallets.tx_begin_read ()); 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) TEST (rpc, work_cancel)
{ {
nano::system system; nano::system system;
@ -3917,7 +3978,6 @@ TEST (rpc, wallet_frontiers)
TEST (rpc, work_validate) TEST (rpc, work_validate)
{ {
nano::network_params params;
nano::system system; nano::system system;
auto & node1 = *add_ipc_enabled_node (system); auto & node1 = *add_ipc_enabled_node (system);
nano::keypair key; nano::keypair key;
@ -3950,9 +4010,9 @@ TEST (rpc, work_validate)
std::string difficulty_text (response.json.get<std::string> ("difficulty")); std::string difficulty_text (response.json.get<std::string> ("difficulty"));
uint64_t difficulty; uint64_t difficulty;
ASSERT_FALSE (nano::from_string_hex (difficulty_text, 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")); 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); uint64_t work2 (0);
request.put ("work", nano::to_string_hex (work2)); 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")); std::string difficulty_text (response.json.get<std::string> ("difficulty"));
uint64_t difficulty; uint64_t difficulty;
ASSERT_FALSE (nano::from_string_hex (difficulty_text, 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")); 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)); 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 ("work", nano::to_string_hex (work1));
request.put ("difficulty", nano::to_string_hex (result_difficulty)); 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) TEST (rpc, successors)
{ {
nano::system system; nano::system system;
@ -5911,7 +6027,7 @@ TEST (rpc, block_create_state_request_work)
boost::property_tree::read_json (block_stream, block_l); boost::property_tree::read_json (block_stream, block_l);
auto block (nano::deserialize_block_json (block_l)); auto block (nano::deserialize_block_json (block_l));
ASSERT_NE (nullptr, block); ASSERT_NE (nullptr, block);
ASSERT_GE (block->difficulty (), nano::work_threshold_base (nano::work_version::work_1)); ASSERT_GE (block->difficulty (), node->default_difficulty ());
} }
} }

View file

@ -508,6 +508,7 @@ public:
std::atomic<uint64_t> block_count{ 0 }; std::atomic<uint64_t> block_count{ 0 };
std::atomic<uint64_t> unchecked_count{ 0 }; std::atomic<uint64_t> unchecked_count{ 0 };
std::atomic<uint64_t> account_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 */ /* Defines the possible states for an election to stop in */

View file

@ -439,6 +439,10 @@ void ledger_processor::epoch_block_impl (nano::state_block & block_a)
{ {
ledger.store.frontier_del (transaction, info.head); 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 (); auto transaction = store.tx_begin_read ();
if (generate_cache_a.reps || generate_cache_a.account_count) 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) for (auto i (store.latest_begin (transaction)), n (store.latest_end ()); i != n; ++i)
{ {
nano::account_info const & info (i->second); nano::account_info const & info (i->second);
cache.rep_weights.representation_add (info.representative, info.balance.number ()); cache.rep_weights.representation_add (info.representative, info.balance.number ());
++cache.account_count; ++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) if (generate_cache_a.cemented_count)