From d6dfeaf190e47a90c1bd6fddbd37234f8fc06f0c Mon Sep 17 00:00:00 2001 From: Shryder Date: Tue, 15 Jun 2021 18:15:40 +0100 Subject: [PATCH] Improve delegators rpc by adding "count", "head", and "threshold" parameters (#3330) (#3333) This commit allows the 'delegators' RPC to limit the response by a 'count' and start the search at a 'start'. The response from 'delegators' can be potentially large and this lets the requestor stream the response in smaller chunks. --- nano/node/json_handler.cpp | 29 +++++++--- nano/rpc_test/rpc.cpp | 111 +++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 8 deletions(-) diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index f9fc65752e..0b858ea6ce 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -2068,20 +2068,33 @@ void nano::json_handler::database_txn_tracker () void nano::json_handler::delegators () { - auto account (account_impl ()); + auto representative (account_impl ()); + auto count (count_optional_impl (1024)); + auto threshold (threshold_optional_impl ()); + auto start_account_text (request.get_optional ("start")); + + nano::account start_account (0); + if (!ec && start_account_text.is_initialized ()) + { + start_account = account_impl (start_account_text.get ()); + } + if (!ec) { - boost::property_tree::ptree delegators; auto transaction (node.store.tx_begin_read ()); - for (auto i (node.store.account.begin (transaction)), n (node.store.account.end ()); i != n; ++i) + boost::property_tree::ptree delegators; + for (auto i (node.store.account.begin (transaction, start_account.number () + 1)), n (node.store.account.end ()); i != n && delegators.size () < count; ++i) { nano::account_info const & info (i->second); - if (info.representative == account) + if (info.representative == representative) { - std::string balance; - nano::uint128_union (info.balance).encode_dec (balance); - nano::account const & account (i->first); - delegators.put (account.to_account (), balance); + if (info.balance.number () >= threshold.number ()) + { + std::string balance; + nano::uint128_union (info.balance).encode_dec (balance); + nano::account const & delegator (i->first); + delegators.put (delegator.to_account (), balance); + } } } response_l.add_child ("delegators", delegators); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 9a9ad71eac..1e056d7308 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -4633,6 +4633,117 @@ TEST (rpc, delegators) ASSERT_EQ ("340282366920938463463374607431768211355", delegators.get (key.pub.to_account ())); } +TEST (rpc, delegators_parameters) +{ + nano::system system; + auto & node1 = *add_ipc_enabled_node (system); + nano::keypair key; + auto latest (node1.latest (nano::dev_genesis_key.pub)); + nano::send_block send (latest, key.pub, 100, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *node1.work_generate_blocking (latest)); + node1.process (send); + nano::open_block open (send.hash (), nano::dev_genesis_key.pub, key.pub, key.prv, key.pub, *node1.work_generate_blocking (key.pub)); + ASSERT_EQ (nano::process_result::progress, node1.process (open).code); + + 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 (); + + // Test with "count" = 2 + boost::property_tree::ptree request; + request.put ("action", "delegators"); + request.put ("account", nano::dev_genesis_key.pub.to_account ()); + request.put ("count", 2); + test_response response (request, rpc.config.port, system.io_ctx); + ASSERT_TIMELY (5s, response.status != 0); + ASSERT_EQ (200, response.status); + auto & delegators_node (response.json.get_child ("delegators")); + boost::property_tree::ptree delegators; + for (auto i (delegators_node.begin ()), n (delegators_node.end ()); i != n; ++i) + { + delegators.put ((i->first), (i->second.get (""))); + } + ASSERT_EQ (2, delegators.size ()); + ASSERT_EQ ("100", delegators.get (nano::dev_genesis_key.pub.to_account ())); + ASSERT_EQ ("340282366920938463463374607431768211355", delegators.get (key.pub.to_account ())); + + // Test with "count" = 1 + request.put ("count", 1); + test_response response2 (request, rpc.config.port, system.io_ctx); + ASSERT_TIMELY (5s, response2.status != 0); + ASSERT_EQ (200, response2.status); + auto & delegators_node2 (response2.json.get_child ("delegators")); + boost::property_tree::ptree delegators2; + for (auto i (delegators_node2.begin ()), n (delegators_node2.end ()); i != n; ++i) + { + delegators2.put ((i->first), (i->second.get (""))); + } + ASSERT_EQ (1, delegators2.size ()); + // What is first in ledger by public key? + if (nano::dev_genesis_key.pub.number () < key.pub.number ()) + { + ASSERT_EQ ("100", delegators2.get (nano::dev_genesis_key.pub.to_account ())); + } + else + { + ASSERT_EQ ("340282366920938463463374607431768211355", delegators2.get (key.pub.to_account ())); + } + + // Test with "threshold" + request.put ("count", 1024); + request.put ("threshold", 101); // higher than remaining genesis balance + test_response response3 (request, rpc.config.port, system.io_ctx); + ASSERT_TIMELY (5s, response3.status != 0); + ASSERT_EQ (200, response3.status); + auto & delegators_node3 (response3.json.get_child ("delegators")); + boost::property_tree::ptree delegators3; + for (auto i (delegators_node3.begin ()), n (delegators_node3.end ()); i != n; ++i) + { + delegators3.put ((i->first), (i->second.get (""))); + } + ASSERT_EQ (1, delegators3.size ()); + ASSERT_EQ ("340282366920938463463374607431768211355", delegators3.get (key.pub.to_account ())); + + // Test with "start" before last account + request.put ("threshold", 0); + auto last_account (key.pub); + if (nano::dev_genesis_key.pub.number () > key.pub.number ()) + { + last_account = nano::dev_genesis_key.pub; + } + request.put ("start", nano::account (last_account.number () - 1).to_account ()); + + test_response response4 (request, rpc.config.port, system.io_ctx); + ASSERT_TIMELY (5s, response4.status != 0); + ASSERT_EQ (200, response4.status); + auto & delegators_node4 (response4.json.get_child ("delegators")); + boost::property_tree::ptree delegators4; + for (auto i (delegators_node4.begin ()), n (delegators_node4.end ()); i != n; ++i) + { + delegators4.put ((i->first), (i->second.get (""))); + } + ASSERT_EQ (1, delegators4.size ()); + boost::optional balance (delegators4.get_optional (last_account.to_account ())); + ASSERT_TRUE (balance.is_initialized ()); + + // Test with "start" equal to last account + request.put ("start", last_account.to_account ()); + test_response response5 (request, rpc.config.port, system.io_ctx); + ASSERT_TIMELY (5s, response5.status != 0); + ASSERT_EQ (200, response5.status); + auto & delegators_node5 (response5.json.get_child ("delegators")); + boost::property_tree::ptree delegators5; + for (auto i (delegators_node5.begin ()), n (delegators_node5.end ()); i != n; ++i) + { + delegators5.put ((i->first), (i->second.get (""))); + } + ASSERT_EQ (0, delegators5.size ()); +} + TEST (rpc, delegators_count) { nano::system system;