diff --git a/plugins/chain_api_plugin/chain.swagger.yaml b/plugins/chain_api_plugin/chain.swagger.yaml index 08d831fad0..10747b7503 100644 --- a/plugins/chain_api_plugin/chain.swagger.yaml +++ b/plugins/chain_api_plugin/chain.swagger.yaml @@ -675,6 +675,11 @@ paths: description: List of authorizing keys items: $ref: "https://docs.eosnetwork.com/openapi/v2.0/PublicKey.yaml" + limit: + type: integer + default: 1000 + maximum: 1000 + description: Maximum number of account permission authorities to return. The server enforces an upper bound of 1000. responses: "200": description: OK @@ -685,6 +690,7 @@ paths: description: Result containing a list of accounts which are authorized, in whole or part, by the provided accounts and keys required: - accounts + - more properties: accounts: type: array @@ -713,6 +719,9 @@ paths: threshold: type: "integer" description: the sum of weights that must be met or exceeded to satisfy the permission + more: + type: boolean + description: True when additional matching account permission authorities were omitted because the result limit was reached. /get_transaction_status: post: description: Attempts to get current blockchain state and, if available, transaction information given the transaction id. For query to work, the transaction finality status feature must be enabled by configuring the chain plugin with the config option '--transaction-finality-status-max-storage-size-gb' in nodeos. @@ -927,4 +936,4 @@ paths: content: application/json: schema: - description: Returns Nothing \ No newline at end of file + description: Returns Nothing diff --git a/plugins/chain_plugin/account_query_db.cpp b/plugins/chain_plugin/account_query_db.cpp index f5a6cf6a32..66fc6f4e42 100644 --- a/plugins/chain_plugin/account_query_db.cpp +++ b/plugins/chain_plugin/account_query_db.cpp @@ -13,6 +13,7 @@ #include #include +#include #include using namespace eosio; @@ -423,6 +424,7 @@ namespace eosio::chain_apis { using result_t = account_query_db::get_accounts_by_authorizers_result; result_t result; + const auto limit = std::min(args.limit, account_query_db::max_results); // deduplicate inputs auto account_set = std::set(args.accounts.begin(), args.accounts.end()); @@ -431,8 +433,9 @@ namespace eosio::chain_apis { /** * Add a range of results */ - auto push_results = [&result](const auto& begin, const auto& end) { - for (auto itr = begin; itr != end; ++itr) { + auto push_results = [&result, limit](const auto& begin, const auto& end) { + auto itr = begin; + for (; itr != end && result.accounts.size() < limit; ++itr) { const auto& pi = itr->second.get(); const auto& authorizer = itr->first.value; auto weight = itr->first.weight; @@ -446,6 +449,11 @@ namespace eosio::chain_apis { pi.threshold }); } + if (itr != end) { + result.more = true; + return false; + } + return true; }; @@ -457,13 +465,15 @@ namespace eosio::chain_apis { const auto begin = name_bimap.left.lower_bound(weighted::lower_bound_for({a.actor, a.permission})); const auto next_account_name = chain::name(a.actor.to_uint64_t() + 1); const auto end = name_bimap.left.lower_bound(weighted::lower_bound_for({next_account_name, a.permission})); - push_results(begin, end); + if (!push_results(begin, end)) + return result; } else { // construct a range of all possible weights for an account/permission pair const auto p = chain::permission_level{a.actor, a.permission}; const auto begin = name_bimap.left.lower_bound(weighted::lower_bound_for(p)); const auto end = name_bimap.left.upper_bound(weighted::upper_bound_for(p)); - push_results(begin, end); + if (!push_results(begin, end)) + return result; } } @@ -471,7 +481,8 @@ namespace eosio::chain_apis { // construct a range of all possible weights for a key const auto begin = key_bimap.left.lower_bound(weighted::lower_bound_for(k)); const auto end = key_bimap.left.upper_bound(weighted::upper_bound_for(k)); - push_results(begin, end); + if (!push_results(begin, end)) + return result; } return result; diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/account_query_db.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/account_query_db.hpp index 2b360e28db..60d32d6f53 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/account_query_db.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/account_query_db.hpp @@ -10,6 +10,8 @@ namespace eosio::chain_apis { */ class account_query_db { public: + static constexpr uint32_t max_results = 1000; + /** * Instantiate a new account query DB from the given chain controller @@ -54,6 +56,7 @@ namespace eosio::chain_apis { std::vector accounts; std::vector keys; + uint32_t limit = max_results; }; /** @@ -70,6 +73,7 @@ namespace eosio::chain_apis { }; std::vector accounts; + bool more = false; }; /** * Given a set of account names and public keys, find all account permission authorities that are, in part or whole, @@ -130,6 +134,6 @@ namespace fc { } } -FC_REFLECT( eosio::chain_apis::account_query_db::get_accounts_by_authorizers_params, (accounts)(keys)) +FC_REFLECT( eosio::chain_apis::account_query_db::get_accounts_by_authorizers_params, (accounts)(keys)(limit)) FC_REFLECT( eosio::chain_apis::account_query_db::get_accounts_by_authorizers_result::account_result, (account_name)(permission_name)(authorizing_account)(authorizing_key)(weight)(threshold)) -FC_REFLECT( eosio::chain_apis::account_query_db::get_accounts_by_authorizers_result, (accounts)) +FC_REFLECT( eosio::chain_apis::account_query_db::get_accounts_by_authorizers_result, (accounts)(more)) diff --git a/plugins/chain_plugin/test/test_account_query_db.cpp b/plugins/chain_plugin/test/test_account_query_db.cpp index cd02c1627a..6bedfdf8d6 100644 --- a/plugins/chain_plugin/test/test_account_query_db.cpp +++ b/plugins/chain_plugin/test/test_account_query_db.cpp @@ -92,6 +92,47 @@ BOOST_FIXTURE_TEST_CASE(updateauth_test, validating_tester) { try { } FC_LOG_AND_RETHROW() } +BOOST_FIXTURE_TEST_CASE(get_accounts_by_authorizers_limit_test, validating_tester) { try { + + auto aq_db = account_query_db(*control); + + auto c = control->accepted_block.connect([&](const block_signal_params& t) { + const auto& [ block, id ] = t; + aq_db.commit_block( block ); + }); + + produce_blocks(10); + + const auto tester_account = "tester"_n; + const auto shared_key = get_public_key(tester_account, "shared"); + create_account(tester_account); + + for (const auto permission : {"first"_n, "second"_n, "third"_n}) { + const auto trace_ptr = push_action(config::system_account_name, updateauth::get_name(), tester_account, + fc::mutable_variant_object() + ("account", tester_account) + ("permission", permission) + ("parent", "active") + ("auth", authority(shared_key, 1))); + aq_db.cache_transaction_trace(trace_ptr); + produce_block(); + } + + params pars; + pars.keys.emplace_back(shared_key); + pars.limit = 2; + + const auto limited_results = aq_db.get_accounts_by_authorizers(pars); + BOOST_TEST_REQUIRE(limited_results.accounts.size() == 2u); + BOOST_TEST_REQUIRE(limited_results.more == true); + + pars.limit = 3; + const auto complete_results = aq_db.get_accounts_by_authorizers(pars); + BOOST_TEST_REQUIRE(complete_results.accounts.size() == 3u); + BOOST_TEST_REQUIRE(complete_results.more == false); + +} FC_LOG_AND_RETHROW() } + BOOST_FIXTURE_TEST_CASE(updateauth_test_multi_threaded, validating_tester) { try { // instantiate an account_query_db @@ -286,4 +327,3 @@ BOOST_AUTO_TEST_CASE(fork_test) { try { } FC_LOG_AND_RETHROW() } BOOST_AUTO_TEST_SUITE_END() -