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

RPC epoch_upgrade #2304

Merged
merged 27 commits into from
Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5ec3059
RPC epoch_upgrade
SergiySW Sep 14, 2019
28cd7b2
More log messages during upgrade
SergiySW Sep 15, 2019
0171be8
Formatting
SergiySW Sep 15, 2019
ce769e0
Support epoch 2
wezrule Sep 23, 2019
50d628c
Simplify return statements in validate_epoch_block()
wezrule Sep 23, 2019
b9dcbb2
Merge with master
wezrule Sep 24, 2019
782363a
Merge branch 'master' into epoch_2
wezrule Sep 24, 2019
16fe942
Formatting
wezrule Sep 24, 2019
e07c300
Serg review comments
wezrule Sep 26, 2019
f3a6429
Enforce sequential check for all epoch upgrades
wezrule Sep 26, 2019
eb2ae96
Add a test for the new is_sequential function
wezrule Sep 26, 2019
51f65a9
Merge remote-tracking branch 'upstream/master' into rpc/epoch_upgrade
SergiySW Sep 26, 2019
4b27f78
Merge remote-tracking branch 'wezrule/epoch_2' into rpc/epoch_upgrade
SergiySW Sep 26, 2019
a88a020
Update RPC epoch_upgrade & test
SergiySW Sep 26, 2019
0114645
Test for epoch_2 upgrade
SergiySW Sep 27, 2019
d2d1d64
Be able to open an unopened account to epoch 2
wezrule Sep 27, 2019
ada5de3
Merge remote-tracking branch 'wezrule/epoch_2' into rpc/epoch_upgrade
SergiySW Sep 27, 2019
9dbb1a3
Formatting
SergiySW Sep 27, 2019
4f1c057
Better naming of ledger epoch link & signer
SergiySW Sep 28, 2019
44dbe8c
Merge remote-tracking branch 'upstream/master' into rpc/epoch_upgrade
SergiySW Sep 29, 2019
c0e0968
Update confirmation height tests
SergiySW Sep 29, 2019
57c2069
Merge remote-tracking branch 'upstream/master' into rpc/epoch_upgrade
SergiySW Sep 30, 2019
2d1bf7b
More detailed epoch upgrader log message for failures
SergiySW Sep 30, 2019
0304c2b
Allow count limit for epoch_upgrade command
SergiySW Sep 30, 2019
58fb22c
Merge remote-tracking branch 'upstream/master' into rpc/epoch_upgrade
SergiySW Oct 6, 2019
85efade
Fix updated tests
SergiySW Oct 6, 2019
db367f8
Merge remote-tracking branch 'upstream/master' into rpc/epoch_upgrade
SergiySW Oct 9, 2019
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
4 changes: 4 additions & 0 deletions nano/lib/errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,10 @@ std::string nano::error_rpc_messages::message (int ev) const
return "Invalid balance number";
case nano::error_rpc::invalid_destinations:
return "Invalid destinations number";
case nano::error_rpc::invalid_epoch:
return "Invalid epoch number";
case nano::error_rpc::invalid_epoch_signer:
return "Incorrect epoch signer";
case nano::error_rpc::invalid_offset:
return "Invalid offset";
case nano::error_rpc::invalid_missing_type:
Expand Down
2 changes: 2 additions & 0 deletions nano/lib/errors.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ enum class error_rpc
disabled_bootstrap_legacy,
invalid_balance,
invalid_destinations,
invalid_epoch,
invalid_epoch_signer,
invalid_offset,
invalid_missing_type,
invalid_root,
Expand Down
180 changes: 180 additions & 0 deletions nano/node/json_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2011,6 +2011,185 @@ void nano::json_handler::deterministic_key ()
response_errors ();
}

void epoch_upgrader (std::shared_ptr<nano::node> node_a, nano::uint256_union const & prv_a, nano::epoch epoch_a)
{
nano::block_builder builder;
auto link (node_a->ledger.link (epoch_a));
nano::raw_key raw_key;
raw_key.data = prv_a;
auto signer (nano::pub_key (prv_a));
assert (signer == node_a->ledger.signer (link));

auto previous_begin = [node_a, epoch_a](nano::account const & account_a) {
auto transaction (node_a->store.tx_begin_read ());
if (epoch_a == nano::epoch::epoch_1)
{
return node_a->store.latest_v0_begin (transaction, account_a);
}
else
{
assert (false);
return node_a->store.latest_v0_begin (transaction, account_a);
}
};
auto previous_end = [node_a, epoch_a]() {
if (epoch_a == nano::epoch::epoch_1)
{
return node_a->store.latest_v0_end ();
}
else
{
assert (false);
return node_a->store.latest_v0_end ();
}
};
auto previous_pending_begin = [node_a, epoch_a](nano::account const & account_a) {
nano::pending_key pending_key (account_a, 0);
auto transaction (node_a->store.tx_begin_read ());
if (epoch_a == nano::epoch::epoch_1)
{
return node_a->store.pending_v0_begin (transaction, pending_key);
}
else
{
assert (false);
return node_a->store.pending_v0_begin (transaction, pending_key);
}
};
auto previous_pending_end = [node_a, epoch_a]() {
if (epoch_a == nano::epoch::epoch_1)
{
return node_a->store.pending_v0_end ();
}
else
{
assert (false);
return node_a->store.pending_v0_end ();
}
};

// Old existing accounts upgrade
// Repeat until latest table for previous accounts is empty
nano::account account (0);
uint64_t count (0);
while (previous_begin (account) != previous_end () && !node_a->stopped)
{
nano::account account (0);
auto iterator (previous_begin (account));
while (iterator != previous_end () && !node_a->stopped)
{
account = iterator->first;
nano::account_info const & info (iterator->second);
auto epoch = builder.state ()
.account (account)
.previous (info.head)
.representative (info.representative)
.balance (info.balance)
.link (link)
.sign (raw_key, signer)
.work (node_a->work_generate_blocking (info.head).value_or (0))
.build ();
if (!nano::validate_message (signer, epoch->hash (), epoch->block_signature ()) && !nano::work_validate (*epoch.get ()))
{
++count;
node_a->process_active (std::move (epoch));
}
if (count % 1000 == 0)
{
node_a->logger.always_log (boost::str (boost::format ("%1% accounts were upgraded to new epoch...") % count));
}
iterator = previous_begin (account.number () + 1);
}
account = 0;
node_a->block_processor.flush ();
}
node_a->logger.always_log (boost::str (boost::format ("%1% accounts were upgraded to new epoch") % count));

// Pending blocks upgrade
account = 1; // Ignore 0 account (burn)
count = 0;
auto iterator (previous_pending_begin (account));
while (iterator != previous_pending_end () && !node_a->stopped)
{
account = iterator->first.account;
auto transaction (node_a->store.tx_begin_read ());
if (!node_a->store.account_exists (transaction, account))
{
auto epoch = builder.state ()
.account (account)
.previous (0)
.representative (0)
.balance (0)
.link (link)
.sign (raw_key, signer)
.work (node_a->work_generate_blocking (account).value_or (0))
.build ();
if (!nano::validate_message (signer, epoch->hash (), epoch->block_signature ()) && !nano::work_validate (*epoch.get ()))
{
++count;
node_a->process_active (std::move (epoch));
}
if (count % 1000 == 0)
{
node_a->logger.always_log (boost::str (boost::format ("%1% unopened accounts with pending blocks were upgraded to new epoch...") % count));
}
}
iterator = account.number () != std::numeric_limits<nano::uint256_t>::max () ? previous_pending_begin (account.number () + 1) : previous_pending_end ();
// Repeat if some pending accounts were upgraded
if (iterator == previous_pending_end () && count != 0)
{
node_a->block_processor.flush ();
account = 1;
iterator = previous_pending_begin (account);
node_a->logger.always_log (boost::str (boost::format ("%1% unopened accounts with pending blocks were upgraded to new epoch") % count));
count = 0;
}
}
node_a->logger.always_log ("Epoch upgrade is completed");
}

/*
* @warning This is an internal/diagnostic RPC, do not rely on its interface being stable
*/
void nano::json_handler::epoch_upgrade ()
{
nano::epoch epoch (nano::epoch::invalid);
uint8_t epoch_int (request.get<uint8_t> ("epoch"));
if (epoch_int == 1)
{
epoch = nano::epoch::epoch_1;
}
if (epoch != nano::epoch::invalid)
{
std::string key_text (request.get<std::string> ("key"));
nano::uint256_union prv;
if (!prv.decode_hex (key_text))
{
if (nano::pub_key (prv) == node.ledger.signer (node.ledger.link (epoch)))
{
auto node_l (node.shared ());
node.worker.push_task ([node_l, prv, epoch]() {
epoch_upgrader (node_l, prv, epoch);
});
response_l.put ("started", "1");
}
else
{
ec = nano::error_rpc::invalid_epoch_signer;
}
}
else
{
ec = nano::error_common::bad_private_key;
}
}
else
{
ec = nano::error_rpc::invalid_epoch;
}
response_errors ();
}

void nano::json_handler::frontiers ()
{
auto start (account_impl ());
Expand Down Expand Up @@ -4707,6 +4886,7 @@ ipc_json_handler_no_arg_func_map create_ipc_json_handler_no_arg_func_map ()
no_arg_funcs.emplace ("delegators", &nano::json_handler::delegators);
no_arg_funcs.emplace ("delegators_count", &nano::json_handler::delegators_count);
no_arg_funcs.emplace ("deterministic_key", &nano::json_handler::deterministic_key);
no_arg_funcs.emplace ("epoch_upgrade", &nano::json_handler::epoch_upgrade);
no_arg_funcs.emplace ("frontiers", &nano::json_handler::frontiers);
no_arg_funcs.emplace ("frontier_count", &nano::json_handler::account_count);
no_arg_funcs.emplace ("keepalive", &nano::json_handler::keepalive);
Expand Down
1 change: 1 addition & 0 deletions nano/node/json_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class json_handler : public std::enable_shared_from_this<nano::json_handler>
void delegators ();
void delegators_count ();
void deterministic_key ();
void epoch_upgrade ();
void frontiers ();
void keepalive ();
void key_create ();
Expand Down
1 change: 1 addition & 0 deletions nano/rpc/rpc_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ std::unordered_set<std::string> create_rpc_control_impls ()
set.emplace ("bootstrap_lazy");
set.emplace ("confirmation_height_currently_processing");
set.emplace ("database_txn_tracker");
set.emplace ("epoch_upgrade");
set.emplace ("keepalive");
set.emplace ("ledger");
set.emplace ("node_id");
Expand Down
64 changes: 64 additions & 0 deletions nano/rpc_test/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7070,3 +7070,67 @@ TEST (rpc, deprecated_account_format)
boost::optional<std::string> deprecated_account_format2 (response2.json.get_optional<std::string> ("deprecated_account_format"));
ASSERT_TRUE (deprecated_account_format2.is_initialized ());
}

TEST (rpc, epoch_upgrade)
{
nano::system system (24000, 1);
auto node = system.nodes.front ();
nano::keypair key1, key2;
nano::genesis genesis;
nano::keypair epoch_signer (nano::test_genesis_key);
auto send1 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, genesis.hash (), nano::test_genesis_key.pub, nano::genesis_amount - 1, key1.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (genesis.hash ()))); // to opened account
ASSERT_EQ (nano::process_result::progress, node->process (*send1).code);
auto send2 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send1->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 2, key2.pub, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send1->hash ()))); // to unopened account (pending)
ASSERT_EQ (nano::process_result::progress, node->process (*send2).code);
auto send3 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send2->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 3, 0, nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send2->hash ()))); // to burn (0)
ASSERT_EQ (nano::process_result::progress, node->process (*send3).code);
auto send4 (std::make_shared<nano::state_block> (nano::test_genesis_key.pub, send3->hash (), nano::test_genesis_key.pub, nano::genesis_amount - 4, std::numeric_limits<nano::uint256_t>::max (), nano::test_genesis_key.prv, nano::test_genesis_key.pub, system.work.generate (send3->hash ()))); // to max account
ASSERT_EQ (nano::process_result::progress, node->process (*send4).code);
auto open (std::make_shared<nano::state_block> (key1.pub, 0, key1.pub, 1, send1->hash (), key1.prv, key1.pub, system.work.generate (key1.pub)));
ASSERT_EQ (nano::process_result::progress, node->process (*open).code);
// Check account count
{
auto transaction (node->store.tx_begin_read ());
ASSERT_EQ (2, node->store.account_count (transaction));
ASSERT_NE (node->store.latest_v0_begin (transaction), node->store.latest_v0_end ());
ASSERT_EQ (node->store.latest_v1_begin (transaction), node->store.latest_v1_end ());
}
enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp);
nano::node_rpc_config node_rpc_config;
nano::ipc::ipc_server ipc_server (*node, node_rpc_config);
nano::rpc_config rpc_config (true);
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", "epoch_upgrade");
request.put ("epoch", 1);
request.put ("key", epoch_signer.prv.data.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);
ASSERT_EQ ("1", response.json.get<std::string> ("started"));
system.deadline_set (5s);
bool done (false);
while (!done)
{
auto transaction (node->store.tx_begin_read ());
done = (4 == node->store.account_count (transaction));
ASSERT_NO_ERROR (system.poll ());
}
// Check upgrade
{
auto transaction (node->store.tx_begin_read ());
ASSERT_EQ (4, node->store.account_count (transaction));
ASSERT_EQ (node->store.latest_v0_begin (transaction), node->store.latest_v0_end ());
ASSERT_NE (node->store.latest_v1_begin (transaction), node->store.latest_v1_end ());
ASSERT_TRUE (node->store.account_exists (transaction, key1.pub));
ASSERT_TRUE (node->store.account_exists (transaction, key2.pub));
ASSERT_TRUE (node->store.account_exists (transaction, std::numeric_limits<nano::uint256_t>::max ()));
ASSERT_FALSE (node->store.account_exists (transaction, 0));
}
}