[Pruning] Support pruned chains in RPC & QT wallet (#2977)

* [Pruning] Support pruned chains in RPC & QT wallet
Splitting https://github.com/nanocurrency/nano-node/pull/2881

* Several RPC calls are modified to support pruned blocks related to main request block.
* RPC “history”: if previous block is pruned, “amount” field is not returned for all blocks, “account” field is not returned for state blocks, also state block type/subtype is “unknown” . If source is pruned, then “account” field is not returned for receive/open legacy & for receive/open state blocks.
* RPC “block_info”/“blocks_info”: if previous block is pruned, “amount” field is not returned. RPC “block_count”: for nodes with enabled pruning returns 2 new fields: “full” blocks & “pruned” blocks. “full” + “pruned” = total “blocks”.
* QT wallet history support for pruned chains

* Remove not yet possible function
* Update tests to cement blocks before pruning
This commit is contained in:
Sergey Kroshnin 2020-10-03 01:04:55 +03:00 committed by GitHub
commit 8462acbc8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 453 additions and 41 deletions

View file

@ -1003,8 +1003,12 @@ void nano::json_handler::block_info ()
{
nano::account account (block->account ().is_zero () ? block->sideband ().account : block->account ());
response_l.put ("block_account", account.to_account ());
auto amount (node.ledger.amount (transaction, hash));
response_l.put ("amount", amount.convert_to<std::string> ());
bool error_or_pruned (false);
auto amount (node.ledger.amount_safe (transaction, hash, error_or_pruned));
if (!error_or_pruned)
{
response_l.put ("amount", amount.convert_to<std::string> ());
}
auto balance (node.ledger.balance (transaction, hash));
response_l.put ("balance", balance.convert_to<std::string> ());
response_l.put ("height", std::to_string (block->sideband ().height));
@ -1067,11 +1071,15 @@ void nano::json_handler::block_confirm ()
// Trigger callback for confirmed block
node.block_arrival.add (hash);
auto account (node.ledger.account (transaction, hash));
auto amount (node.ledger.amount (transaction, hash));
bool error_or_pruned (false);
auto amount (node.ledger.amount_safe (transaction, hash, error_or_pruned));
bool is_state_send (false);
if (auto state = dynamic_cast<nano::state_block *> (block_l.get ()))
if (!error_or_pruned)
{
is_state_send = node.ledger.is_send (transaction, *state);
if (auto state = dynamic_cast<nano::state_block *> (block_l.get ()))
{
is_state_send = node.ledger.is_send (transaction, *state);
}
}
node.observers.blocks.notify (status, account, amount, is_state_send);
}
@ -1153,8 +1161,12 @@ void nano::json_handler::blocks_info ()
boost::property_tree::ptree entry;
nano::account account (block->account ().is_zero () ? block->sideband ().account : block->account ());
entry.put ("block_account", account.to_account ());
auto amount (node.ledger.amount (transaction, hash));
entry.put ("amount", amount.convert_to<std::string> ());
bool error_or_pruned (false);
auto amount (node.ledger.amount_safe (transaction, hash, error_or_pruned));
if (!error_or_pruned)
{
entry.put ("amount", amount.convert_to<std::string> ());
}
auto balance (node.ledger.balance (transaction, hash));
entry.put ("balance", balance.convert_to<std::string> ());
entry.put ("height", std::to_string (block->sideband ().height));
@ -1257,6 +1269,11 @@ void nano::json_handler::block_count ()
response_l.put ("count", std::to_string (node.ledger.cache.block_count));
response_l.put ("unchecked", std::to_string (node.store.unchecked_count (node.store.tx_begin_read ())));
response_l.put ("cemented", std::to_string (node.ledger.cache.cemented_count));
if (node.flags.enable_pruning)
{
response_l.put ("full", std::to_string (node.ledger.cache.block_count - node.ledger.cache.pruned_count));
response_l.put ("pruned", std::to_string (node.ledger.cache.pruned_count));
}
response_errors ();
}
@ -2180,8 +2197,12 @@ public:
tree.put ("type", "send");
auto account (block_a.hashables.destination.to_account ());
tree.put ("account", account);
auto amount (handler.node.ledger.amount (transaction, hash).convert_to<std::string> ());
tree.put ("amount", amount);
bool error_or_pruned (false);
auto amount (handler.node.ledger.amount_safe (transaction, hash, error_or_pruned).convert_to<std::string> ());
if (!error_or_pruned)
{
tree.put ("amount", amount);
}
if (raw)
{
tree.put ("destination", account);
@ -2192,10 +2213,17 @@ public:
void receive_block (nano::receive_block const & block_a)
{
tree.put ("type", "receive");
auto account (handler.node.ledger.account (transaction, block_a.hashables.source).to_account ());
tree.put ("account", account);
auto amount (handler.node.ledger.amount (transaction, hash).convert_to<std::string> ());
tree.put ("amount", amount);
bool error_or_pruned (false);
auto amount (handler.node.ledger.amount_safe (transaction, hash, error_or_pruned).convert_to<std::string> ());
if (!error_or_pruned)
{
auto source_account (handler.node.ledger.account_safe (transaction, block_a.hashables.source, error_or_pruned));
if (!error_or_pruned)
{
tree.put ("account", source_account.to_account ());
}
tree.put ("amount", amount);
}
if (raw)
{
tree.put ("source", block_a.hashables.source.to_string ());
@ -2218,8 +2246,17 @@ public:
}
if (block_a.hashables.source != network_params.ledger.genesis_account)
{
tree.put ("account", handler.node.ledger.account (transaction, block_a.hashables.source).to_account ());
tree.put ("amount", handler.node.ledger.amount (transaction, hash).convert_to<std::string> ());
bool error_or_pruned (false);
auto amount (handler.node.ledger.amount_safe (transaction, hash, error_or_pruned).convert_to<std::string> ());
if (!error_or_pruned)
{
auto source_account (handler.node.ledger.account_safe (transaction, block_a.hashables.source, error_or_pruned));
if (!error_or_pruned)
{
tree.put ("account", source_account.to_account ());
}
tree.put ("amount", amount);
}
}
else
{
@ -2247,8 +2284,20 @@ public:
tree.put ("previous", block_a.hashables.previous.to_string ());
}
auto balance (block_a.hashables.balance.number ());
auto previous_balance (handler.node.ledger.balance (transaction, block_a.hashables.previous));
if (balance < previous_balance)
bool error_or_pruned (false);
auto previous_balance (handler.node.ledger.balance_safe (transaction, block_a.hashables.previous, error_or_pruned));
if (error_or_pruned)
{
if (raw)
{
tree.put ("subtype", "unknown");
}
else
{
tree.put ("type", "unknown");
}
}
else if (balance < previous_balance)
{
if (should_ignore_account (block_a.hashables.link.as_account ()))
{
@ -2285,8 +2334,8 @@ public:
}
else
{
auto account (handler.node.ledger.account (transaction, block_a.hashables.link.as_block_hash ()));
if (should_ignore_account (account))
auto source_account (handler.node.ledger.account_safe (transaction, block_a.hashables.link.as_block_hash (), error_or_pruned));
if (!error_or_pruned && should_ignore_account (source_account))
{
tree.clear ();
return;
@ -2299,7 +2348,10 @@ public:
{
tree.put ("type", "receive");
}
tree.put ("account", account.to_account ());
if (!error_or_pruned)
{
tree.put ("account", source_account.to_account ());
}
tree.put ("amount", (balance - previous_balance).convert_to<std::string> ());
}
}
@ -3176,14 +3228,15 @@ void nano::json_handler::receive ()
auto block (node.store.block_get (block_transaction, hash));
if (block != nullptr)
{
if (node.store.pending_exists (block_transaction, nano::pending_key (account, hash)))
nano::pending_info pending_info;
if (!node.store.pending_get (block_transaction, nano::pending_key (account, hash), pending_info))
{
auto work (work_optional_impl ());
if (!ec && work)
{
nano::account_info info;
nano::root head;
nano::epoch epoch = block->sideband ().details.epoch;
nano::epoch epoch = pending_info.epoch;
if (!node.store.account_get (block_transaction, account, info))
{
head = info.head;
@ -4681,7 +4734,14 @@ void nano::json_handler::wallet_republish ()
{
hashes.push_back (latest);
block = node.store.block_get (block_transaction, latest);
latest = block->previous ();
if (block != nullptr)
{
latest = block->previous ();
}
else
{
latest.clear ();
}
}
std::reverse (hashes.begin (), hashes.end ());
for (auto & hash : hashes)

View file

@ -522,13 +522,23 @@ public:
{
type = "Send";
account = block_a.hashables.destination;
amount = ledger.amount (transaction, block_a.hash ());
bool error_or_pruned (false);
amount = ledger.amount_safe (transaction, block_a.hash (), error_or_pruned);
if (error_or_pruned)
{
type = "Send (pruned)";
}
}
void receive_block (nano::receive_block const & block_a)
{
type = "Receive";
account = ledger.account (transaction, block_a.source ());
amount = ledger.amount (transaction, block_a.source ());
bool error_or_pruned (false);
account = ledger.account_safe (transaction, block_a.hashables.source, error_or_pruned);
amount = ledger.amount_safe (transaction, block_a.hash (), error_or_pruned);
if (error_or_pruned)
{
type = "Receive (pruned)";
}
}
void open_block (nano::open_block const & block_a)
{
@ -536,8 +546,13 @@ public:
type = "Receive";
if (block_a.hashables.source != params.ledger.genesis_account)
{
account = ledger.account (transaction, block_a.hashables.source);
amount = ledger.amount (transaction, block_a.hash ());
bool error_or_pruned (false);
account = ledger.account_safe (transaction, block_a.hashables.source, error_or_pruned);
amount = ledger.amount_safe (transaction, block_a.hash (), error_or_pruned);
if (error_or_pruned)
{
type = "Receive (pruned)";
}
}
else
{
@ -554,8 +569,15 @@ public:
void state_block (nano::state_block const & block_a)
{
auto balance (block_a.hashables.balance.number ());
auto previous_balance (ledger.balance (transaction, block_a.hashables.previous));
if (balance < previous_balance)
bool error_or_pruned (false);
auto previous_balance (ledger.balance_safe (transaction, block_a.hashables.previous, error_or_pruned));
if (error_or_pruned)
{
type = "Unknown (pruned)";
amount = 0;
account = block_a.hashables.account;
}
else if (balance < previous_balance)
{
type = "Send";
amount = previous_balance - balance;
@ -576,7 +598,11 @@ public:
else
{
type = "Receive";
account = ledger.account (transaction, block_a.hashables.link.as_block_hash ());
account = ledger.account_safe (transaction, block_a.hashables.link.as_block_hash (), error_or_pruned);
if (error_or_pruned)
{
type = "Receive (pruned)";
}
}
amount = balance - previous_balance;
}
@ -599,16 +625,18 @@ void nano_qt::history::refresh ()
{
QList<QStandardItem *> items;
auto block (ledger.store.block_get (transaction, hash));
debug_assert (block != nullptr);
block->visit (visitor);
items.push_back (new QStandardItem (QString (visitor.type.c_str ())));
items.push_back (new QStandardItem (QString (visitor.account.to_account ().c_str ())));
auto balanceItem = new QStandardItem (QString (wallet.format_balance (visitor.amount).c_str ()));
balanceItem->setData (Qt::AlignRight, Qt::TextAlignmentRole);
items.push_back (balanceItem);
items.push_back (new QStandardItem (QString (hash.to_string ().c_str ())));
hash = block->previous ();
model->appendRow (items);
if (block != nullptr)
{
block->visit (visitor);
items.push_back (new QStandardItem (QString (visitor.type.c_str ())));
items.push_back (new QStandardItem (QString (visitor.account.to_account ().c_str ())));
auto balanceItem = new QStandardItem (QString (wallet.format_balance (visitor.amount).c_str ()));
balanceItem->setData (Qt::AlignRight, Qt::TextAlignmentRole);
items.push_back (balanceItem);
items.push_back (new QStandardItem (QString (hash.to_string ().c_str ())));
hash = block->previous ();
model->appendRow (items);
}
}
}

View file

@ -501,6 +501,78 @@ TEST (history, short_text)
ASSERT_EQ (4, history.model->rowCount ());
}
TEST (history, pruned_source)
{
if (nano::using_rocksdb_in_tests ())
{
// Don't test this in rocksdb mode
return;
}
nano_qt::eventloop_processor processor;
nano::keypair key;
nano::system system (1);
system.wallet (0)->insert_adhoc (key.prv);
nano::account account;
{
auto transaction (system.nodes[0]->wallets.tx_begin_read ());
account = system.account (transaction, 0);
}
auto wallet (std::make_shared<nano_qt::wallet> (*test_application, processor, *system.nodes[0], system.wallet (0), account));
auto store = nano::make_store (system.nodes[0]->logger, nano::unique_path ());
ASSERT_TRUE (!store->init_error ());
nano::genesis genesis;
nano::ledger ledger (*store, system.nodes[0]->stats);
ledger.pruning = true;
nano::block_hash next_pruning;
// Basic pruning for legacy blocks. Previous block is pruned, source is pruned
{
auto transaction (store->tx_begin_write ());
store->initialize (transaction, genesis, ledger.cache);
auto latest (ledger.latest (transaction, nano::dev_genesis_key.pub));
nano::send_block send1 (latest, nano::dev_genesis_key.pub, nano::genesis_amount - 100, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (latest));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send1).code);
nano::send_block send2 (send1.hash (), key.pub, nano::genesis_amount - 200, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send1.hash ()));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send2).code);
nano::receive_block receive (send2.hash (), send1.hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (send2.hash ()));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive).code);
nano::open_block open (send2.hash (), key.pub, key.pub, key.prv, key.pub, *system.work.generate (key.pub));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, open).code);
ASSERT_EQ (1, ledger.pruning_action (transaction, send1.hash (), 2));
next_pruning = send2.hash ();
}
nano_qt::history history1 (ledger, nano::dev_genesis_key.pub, *wallet);
history1.refresh ();
ASSERT_EQ (2, history1.model->rowCount ());
nano_qt::history history2 (ledger, key.pub, *wallet);
history2.refresh ();
ASSERT_EQ (1, history2.model->rowCount ());
// Additional legacy test
{
auto transaction (store->tx_begin_write ());
ASSERT_EQ (1, ledger.pruning_action (transaction, next_pruning, 2));
}
history1.refresh ();
ASSERT_EQ (1, history1.model->rowCount ());
history2.refresh ();
ASSERT_EQ (1, history2.model->rowCount ());
// Pruning for state blocks. Previous block is pruned, source is pruned
{
auto transaction (store->tx_begin_write ());
auto latest (ledger.latest (transaction, nano::dev_genesis_key.pub));
nano::state_block send (nano::dev_genesis_key.pub, latest, nano::dev_genesis_key.pub, nano::genesis_amount - 200, key.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (latest));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, send).code);
auto latest_key (ledger.latest (transaction, key.pub));
nano::state_block receive (key.pub, latest_key, key.pub, 200, send.hash (), key.prv, key.pub, *system.work.generate (latest_key));
ASSERT_EQ (nano::process_result::progress, ledger.process (transaction, receive).code);
ASSERT_EQ (1, ledger.pruning_action (transaction, latest, 2));
ASSERT_EQ (1, ledger.pruning_action (transaction, latest_key, 2));
}
history1.refresh ();
ASSERT_EQ (1, history1.model->rowCount ());
history2.refresh ();
ASSERT_EQ (1, history2.model->rowCount ());
}
TEST (wallet, startup_work)
{
nano_qt::eventloop_processor processor;

View file

@ -1562,6 +1562,155 @@ TEST (rpc, history_count)
ASSERT_EQ (1, history_node.size ());
}
TEST (rpc, history_pruning)
{
nano::system system;
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.enable_voting = false; // Remove after allowing pruned voting
nano::node_flags node_flags;
node_flags.enable_pruning = true;
auto node0 = add_ipc_enabled_node (system, node_config, node_flags);
nano::genesis genesis;
auto change (std::make_shared<nano::change_block> (genesis.hash (), nano::dev_genesis_key.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node0->work.generate (genesis.hash ())));
node0->process_active (change);
auto send (std::make_shared<nano::send_block> (change->hash (), nano::dev_genesis_key.pub, nano::genesis_amount - node0->config.receive_minimum.number (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node0->work.generate (change->hash ())));
node0->process_active (send);
auto receive (std::make_shared<nano::receive_block> (send->hash (), send->hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node0->work.generate (send->hash ())));
node0->process_active (receive);
auto usend (std::make_shared<nano::state_block> (nano::genesis_account, receive->hash (), nano::genesis_account, nano::genesis_amount - nano::Gxrb_ratio, nano::genesis_account, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node0->work_generate_blocking (receive->hash ())));
auto ureceive (std::make_shared<nano::state_block> (nano::genesis_account, usend->hash (), nano::genesis_account, nano::genesis_amount, usend->hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node0->work_generate_blocking (usend->hash ())));
auto uchange (std::make_shared<nano::state_block> (nano::genesis_account, ureceive->hash (), nano::keypair ().pub, nano::genesis_amount, 0, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node0->work_generate_blocking (ureceive->hash ())));
node0->process_active (usend);
node0->process_active (ureceive);
node0->process_active (uchange);
node0->block_processor.flush ();
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
// Confirm last block to prune previous
{
auto election = node0->active.election (change->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
}
ASSERT_TIMELY (2s, node0->block_confirmed (change->hash ()) && node0->active.active (send->qualified_root ()));
{
auto election = node0->active.election (send->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
}
ASSERT_TIMELY (2s, node0->block_confirmed (send->hash ()) && node0->active.active (receive->qualified_root ()));
{
auto election = node0->active.election (receive->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
}
ASSERT_TIMELY (2s, node0->block_confirmed (receive->hash ()) && node0->active.active (usend->qualified_root ()));
{
auto election = node0->active.election (usend->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
}
ASSERT_TIMELY (2s, node0->block_confirmed (usend->hash ()) && node0->active.active (ureceive->qualified_root ()));
{
auto election = node0->active.election (ureceive->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
}
ASSERT_TIMELY (2s, node0->block_confirmed (ureceive->hash ()) && node0->active.active (uchange->qualified_root ()));
{
auto election = node0->active.election (uchange->qualified_root ());
ASSERT_NE (nullptr, election);
election->force_confirm ();
}
ASSERT_TIMELY (2s, node0->active.empty () && node0->block_confirmed (uchange->hash ()));
ASSERT_TIMELY (2s, node0->ledger.cache.cemented_count == 7 && node0->confirmation_height_processor.current ().is_zero () && node0->confirmation_height_processor.awaiting_processing_size () == 0);
// Pruning action
{
auto transaction (node0->store.tx_begin_write ());
ASSERT_EQ (1, node0->ledger.pruning_action (transaction, change->hash (), 1));
}
nano::node_rpc_config node_rpc_config;
nano::ipc::ipc_server ipc_server (*node0, node_rpc_config);
nano::rpc_config rpc_config (nano::get_available_port (), true);
rpc_config.rpc_process.ipc_port = node0->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", "history");
request.put ("hash", send->hash ().to_string ());
request.put ("count", 100);
test_response response (request, rpc.config.port, system.io_ctx);
ASSERT_TIMELY (5s, response.status != 0);
ASSERT_EQ (200, response.status);
std::vector<std::tuple<std::string, std::string, std::string, std::string>> history_l;
auto & history_node (response.json.get_child ("history"));
for (auto i (history_node.begin ()), n (history_node.end ()); i != n; ++i)
{
history_l.push_back (std::make_tuple (i->second.get<std::string> ("type"), i->second.get<std::string> ("account", "-1"), i->second.get<std::string> ("amount", "-1"), i->second.get<std::string> ("hash")));
boost::optional<std::string> amount (i->second.get_optional<std::string> ("amount"));
ASSERT_FALSE (amount.is_initialized ()); // Cannot calculate amount
}
ASSERT_EQ (1, history_l.size ());
ASSERT_EQ ("send", std::get<0> (history_l[0]));
ASSERT_EQ (nano::dev_genesis_key.pub.to_account (), std::get<1> (history_l[0]));
ASSERT_EQ ("-1", std::get<2> (history_l[0]));
ASSERT_EQ (send->hash ().to_string (), std::get<3> (history_l[0]));
// Pruning action
{
auto transaction (node0->store.tx_begin_write ());
ASSERT_EQ (1, node0->ledger.pruning_action (transaction, send->hash (), 1));
}
boost::property_tree::ptree request2;
request2.put ("action", "history");
request2.put ("hash", receive->hash ().to_string ());
request2.put ("count", 100);
test_response response2 (request2, rpc.config.port, system.io_ctx);
ASSERT_TIMELY (5s, response2.status != 0);
ASSERT_EQ (200, response2.status);
history_l.clear ();
auto & history_node2 (response2.json.get_child ("history"));
for (auto i (history_node2.begin ()), n (history_node2.end ()); i != n; ++i)
{
history_l.push_back (std::make_tuple (i->second.get<std::string> ("type"), i->second.get<std::string> ("account", "-1"), i->second.get<std::string> ("amount", "-1"), i->second.get<std::string> ("hash")));
boost::optional<std::string> amount (i->second.get_optional<std::string> ("amount"));
ASSERT_FALSE (amount.is_initialized ()); // Cannot calculate amount
boost::optional<std::string> account (i->second.get_optional<std::string> ("account"));
ASSERT_FALSE (account.is_initialized ()); // Cannot find source account
}
ASSERT_EQ (1, history_l.size ());
ASSERT_EQ ("receive", std::get<0> (history_l[0]));
ASSERT_EQ ("-1", std::get<1> (history_l[0]));
ASSERT_EQ ("-1", std::get<2> (history_l[0]));
ASSERT_EQ (receive->hash ().to_string (), std::get<3> (history_l[0]));
// Pruning action
{
auto transaction (node0->store.tx_begin_write ());
ASSERT_EQ (1, node0->ledger.pruning_action (transaction, receive->hash (), 1));
}
boost::property_tree::ptree request3;
request3.put ("action", "history");
request3.put ("hash", uchange->hash ().to_string ());
request3.put ("count", 100);
test_response response3 (request3, rpc.config.port, system.io_ctx);
ASSERT_TIMELY (5s, response3.status != 0);
ASSERT_EQ (200, response3.status);
history_l.clear ();
auto & history_node3 (response3.json.get_child ("history"));
for (auto i (history_node3.begin ()), n (history_node3.end ()); i != n; ++i)
{
history_l.push_back (std::make_tuple (i->second.get<std::string> ("type"), i->second.get<std::string> ("account", "-1"), i->second.get<std::string> ("amount", "-1"), i->second.get<std::string> ("hash")));
}
ASSERT_EQ (2, history_l.size ());
ASSERT_EQ ("receive", std::get<0> (history_l[0]));
ASSERT_EQ (ureceive->hash ().to_string (), std::get<3> (history_l[0]));
ASSERT_EQ (nano::dev_genesis_key.pub.to_account (), std::get<1> (history_l[0]));
ASSERT_EQ (nano::Gxrb_ratio.convert_to<std::string> (), std::get<2> (history_l[0]));
ASSERT_EQ ("unknown", std::get<0> (history_l[1]));
ASSERT_EQ ("-1", std::get<1> (history_l[1]));
ASSERT_EQ ("-1", std::get<2> (history_l[1]));
ASSERT_EQ (usend->hash ().to_string (), std::get<3> (history_l[1]));
}
TEST (rpc, process_block)
{
nano::system system;
@ -3180,6 +3329,50 @@ TEST (rpc, block_count)
}
}
TEST (rpc, block_count_pruning)
{
nano::system system;
auto & node0 = *system.add_node ();
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.enable_voting = false; // Remove after allowing pruned voting
nano::node_flags node_flags;
node_flags.enable_pruning = true;
auto & node1 = *add_ipc_enabled_node (system, node_config, node_flags);
auto latest (node1.latest (nano::dev_genesis_key.pub));
auto send1 (std::make_shared<nano::send_block> (latest, nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (latest)));
node1.process_active (send1);
auto receive1 (std::make_shared<nano::receive_block> (send1->hash (), send1->hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (send1->hash ())));
node1.process_active (receive1);
node1.block_processor.flush ();
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
ASSERT_TIMELY (5s, node1.ledger.cache.cemented_count == 3 && node1.confirmation_height_processor.current ().is_zero () && node1.confirmation_height_processor.awaiting_processing_size () == 0);
// Pruning action
{
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (1, node1.ledger.pruning_action (transaction, send1->hash (), 1));
}
scoped_io_thread_name_change scoped_thread_name_io;
nano::node_rpc_config node_rpc_config;
nano::ipc::ipc_server ipc_server (node1, node_rpc_config);
nano::rpc_config rpc_config (nano::get_available_port (), true);
rpc_config.rpc_process.ipc_port = node1.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 request1;
request1.put ("action", "block_count");
{
test_response response1 (request1, rpc.config.port, system.io_ctx);
ASSERT_TIMELY (10s, response1.status != 0);
ASSERT_EQ (200, response1.status);
ASSERT_EQ ("3", response1.json.get<std::string> ("count"));
ASSERT_EQ ("0", response1.json.get<std::string> ("unchecked"));
ASSERT_EQ ("3", response1.json.get<std::string> ("cemented"));
ASSERT_EQ ("2", response1.json.get<std::string> ("full"));
ASSERT_EQ ("1", response1.json.get<std::string> ("pruned"));
}
}
TEST (rpc, frontier_count)
{
nano::system system;
@ -5059,6 +5252,65 @@ TEST (rpc, blocks_info_subtype)
ASSERT_EQ (change_subtype, "change");
}
TEST (rpc, block_info_pruning)
{
nano::system system;
auto & node0 = *system.add_node ();
nano::node_config node_config (nano::get_available_port (), system.logging);
node_config.enable_voting = false; // Remove after allowing pruned voting
nano::node_flags node_flags;
node_flags.enable_pruning = true;
auto & node1 = *add_ipc_enabled_node (system, node_config, node_flags);
auto latest (node1.latest (nano::dev_genesis_key.pub));
auto send1 (std::make_shared<nano::send_block> (latest, nano::dev_genesis_key.pub, nano::genesis_amount - nano::Gxrb_ratio, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (latest)));
node1.process_active (send1);
auto receive1 (std::make_shared<nano::receive_block> (send1->hash (), send1->hash (), nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (send1->hash ())));
node1.process_active (receive1);
node1.block_processor.flush ();
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
ASSERT_TIMELY (5s, node1.ledger.cache.cemented_count == 3 && node1.confirmation_height_processor.current ().is_zero () && node1.confirmation_height_processor.awaiting_processing_size () == 0);
// Pruning action
{
auto transaction (node1.store.tx_begin_write ());
ASSERT_EQ (1, node1.ledger.pruning_action (transaction, send1->hash (), 1));
}
scoped_io_thread_name_change scoped_thread_name_io;
nano::node_rpc_config node_rpc_config;
nano::ipc::ipc_server ipc_server (node1, node_rpc_config);
nano::rpc_config rpc_config (nano::get_available_port (), true);
rpc_config.rpc_process.ipc_port = node1.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 ();
// Pruned block
boost::property_tree::ptree request;
request.put ("action", "block_info");
request.put ("hash", send1->hash ().to_string ());
test_response response (request, rpc.config.port, system.io_ctx);
ASSERT_TIMELY (5s, response.status != 0);
ASSERT_EQ (200, response.status);
ASSERT_EQ (std::error_code (nano::error_blocks::not_found).message (), response.json.get<std::string> ("error"));
// Existing block with previous pruned
boost::property_tree::ptree request2;
request2.put ("action", "block_info");
request2.put ("json_block", "true");
request2.put ("hash", receive1->hash ().to_string ());
test_response response2 (request2, rpc.config.port, system.io_ctx);
ASSERT_TIMELY (5s, response2.status != 0);
ASSERT_EQ (200, response2.status);
std::string account_text (response2.json.get<std::string> ("block_account"));
ASSERT_EQ (nano::dev_genesis_key.pub.to_account (), account_text);
boost::optional<std::string> amount (response2.json.get_optional<std::string> ("amount"));
ASSERT_FALSE (amount.is_initialized ()); // Cannot calculate amount
bool json_error{ false };
nano::receive_block receive_from_json (json_error, response2.json.get_child ("contents"));
ASSERT_FALSE (json_error);
ASSERT_EQ (receive1->full_hash (), receive_from_json.full_hash ());
std::string balance_text (response2.json.get<std::string> ("balance"));
ASSERT_EQ (nano::genesis_amount.convert_to<std::string> (), balance_text);
ASSERT_TRUE (response2.json.get<bool> ("confirmed"));
}
TEST (rpc, work_peers_all)
{
nano::system system;