RPC unopened (#1727)

* Add RPC unopened to get unopened accounts with pending blocks

* Format

* Faster approach using pending tables

* Avoid using unordered map and exclude the burn account

* Add test for burn account

* Check for account number overflow

* Change RPC response format

* Add edge case with no accounts

* Only add if pending>0

* Require RPC control

* Check for errors

* Add starting 'account' and total 'count' options.
This commit is contained in:
Guilherme Lawless 2019-02-22 21:13:47 +00:00 committed by Sergey Kroshnin
commit 36b46547bb
3 changed files with 187 additions and 0 deletions

View file

@ -4361,6 +4361,125 @@ TEST (rpc, stats_clear)
ASSERT_LE (system.nodes[0]->stats.last_reset ().count (), 5);
}
TEST (rpc, unopened)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto transaction (system.wallet (0)->wallets.tx_begin_write ());
nano::account account1 (1), account2 (account1.number () + 1);
auto genesis (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_FALSE (genesis.is_zero ());
auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, account1, 1));
ASSERT_NE (nullptr, send);
auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, account2, 2));
ASSERT_NE (nullptr, send2);
nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true));
rpc.start ();
{
boost::property_tree::ptree request;
request.put ("action", "unopened");
test_response response (request, rpc, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
auto & accounts (response.json.get_child ("accounts"));
ASSERT_EQ (2, accounts.size ());
ASSERT_EQ ("1", accounts.get<std::string> (account1.to_account ()));
ASSERT_EQ ("2", accounts.get<std::string> (account2.to_account ()));
}
{
// starting at second account should get a single result
boost::property_tree::ptree request;
request.put ("action", "unopened");
request.put ("account", account2.to_account ());
test_response response (request, rpc, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
auto & accounts (response.json.get_child ("accounts"));
ASSERT_EQ (1, accounts.size ());
ASSERT_EQ ("2", accounts.get<std::string> (account2.to_account ()));
}
{
// starting at third account should get no results
boost::property_tree::ptree request;
request.put ("action", "unopened");
request.put ("account", nano::account (account2.number () + 1).to_account ());
test_response response (request, rpc, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
auto & accounts (response.json.get_child ("accounts"));
ASSERT_EQ (0, accounts.size ());
}
{
// using count=1 should get a single result
boost::property_tree::ptree request;
request.put ("action", "unopened");
request.put ("count", "1");
test_response response (request, rpc, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
auto & accounts (response.json.get_child ("accounts"));
ASSERT_EQ (1, accounts.size ());
ASSERT_EQ ("1", accounts.get<std::string> (account1.to_account ()));
}
}
TEST (rpc, unopened_burn)
{
nano::system system (24000, 1);
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
auto genesis (system.nodes[0]->latest (nano::test_genesis_key.pub));
ASSERT_FALSE (genesis.is_zero ());
auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::burn_account, 1));
ASSERT_NE (nullptr, send);
nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true));
rpc.start ();
boost::property_tree::ptree request;
request.put ("action", "unopened");
test_response response (request, rpc, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
auto & accounts (response.json.get_child ("accounts"));
ASSERT_EQ (0, accounts.size ());
}
TEST (rpc, unopened_no_accounts)
{
nano::system system (24000, 1);
nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true));
rpc.start ();
boost::property_tree::ptree request;
request.put ("action", "unopened");
test_response response (request, rpc, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
auto & accounts (response.json.get_child ("accounts"));
ASSERT_EQ (0, accounts.size ());
}
TEST (rpc, uptime)
{
nano::system system (24000, 1);

View file

@ -3243,6 +3243,69 @@ void nano::rpc_handler::unchecked_keys ()
response_errors ();
}
void nano::rpc_handler::unopened ()
{
rpc_control_impl ();
if (!ec)
{
auto count (count_optional_impl ());
nano::account start (1); // exclude burn account by default
boost::optional<std::string> account_text (request.get_optional<std::string> ("account"));
if (account_text.is_initialized ())
{
if (start.decode_account (account_text.get ()))
{
ec = nano::error_common::bad_account_number;
}
}
if (!ec)
{
auto transaction (node.store.tx_begin_read ());
auto iterator (node.store.pending_begin (transaction, nano::pending_key (start, 0)));
auto end (node.store.pending_end ());
nano::account current_account (start);
nano::uint128_t current_account_sum{ 0 };
boost::property_tree::ptree accounts;
while (iterator != end && accounts.size () < count)
{
nano::pending_key key (iterator->first);
nano::account account (key.account);
nano::pending_info info (iterator->second);
if (node.store.account_exists (transaction, account))
{
if (account.number () == std::numeric_limits<nano::uint256_t>::max ())
{
break;
}
// Skip existing accounts
iterator = node.store.pending_begin (transaction, nano::pending_key (account.number () + 1, 0));
}
else
{
if (account != current_account)
{
if (current_account_sum > 0)
{
accounts.put (current_account.to_account (), current_account_sum.convert_to<std::string> ());
current_account_sum = 0;
}
current_account = account;
}
current_account_sum += info.amount.number ();
++iterator;
}
}
// last one after iterator reaches end
if (current_account_sum > 0 && accounts.size () < count)
{
accounts.put (current_account.to_account (), current_account_sum.convert_to<std::string> ());
}
response_l.add_child ("accounts", accounts);
}
}
response_errors ();
}
void nano::rpc_handler::uptime ()
{
response_l.put ("seconds", std::chrono::duration_cast<std::chrono::seconds> (std::chrono::steady_clock::now () - node.startup_time).count ());
@ -4536,6 +4599,10 @@ void nano::rpc_handler::process_request ()
{
unchecked_keys ();
}
else if (action == "unopened")
{
unopened ();
}
else if (action == "uptime")
{
uptime ();

View file

@ -166,6 +166,7 @@ public:
void unchecked_clear ();
void unchecked_get ();
void unchecked_keys ();
void unopened ();
void uptime ();
void validate_account_number ();
void version ();