Limit and paginate get_accounts_by_authorizers results - #1894
Conversation
|
Bounding this endpoint is worth doing — the WAX key in #1893 makes the amplification concrete, and a caller-supplied A hard cap with no continuation makes the complete answer unobtainable. The natural consumers of this endpoint are asking "which account permissions does this key satisfy" — key-compromise auditing, or exactly the typo-squat investigation described in #1893. Those are the queries where the tail matters most. With an unconditional 1000-row cap and no way to resume, any authority set past the cap becomes permanently unqueryable through the API (no other RPC enumerates permissions by key). The truncated set is not deterministic. The bimap left keys order rows by Suggestion: cursor pagination in the existing
With a cursor in place, Alternative (or complement): honor the deadline instead of clamping silently. Two smaller notes if a server-side max stays: consider capping only the default while honoring a larger explicit caller limit (or making the max operator-configurable) — this endpoint is opt-in via |
@heifner thanks for the detailed feedback. I’ve updated the PR to address these points while preserving backward compatibility:
Follow-up commits: |
Summary
limittoget_accounts_by_authorizerslimitis omitted, bounded by the existing server-side HTTP deadlineWhy
A public key can be assigned to an unusually large number of account permissions. The endpoint previously traversed and serialized every match, so a pathological or attacker-influenced authority set could consume significant CPU, memory, HTTP worker time, and bandwidth.
The read-only API already receives the deadline derived from
http-max-response-time-ms, butread_only::get_accounts_by_authorizerspreviously discarded it. This change threads that deadline through input deduplication and authority-index traversal. If an unpaginated legacy request cannot finish its query work before the deadline, it fails with a timeout rather than returning a partial response that looks complete.For backward compatibility, callers that omit both
limitandcursorretain complete-result behavior. Callers opt into pagination by supplyinglimit; the effective page size is capped at 1,000, and all matches remain obtainable throughnext_cursor.Addresses the node-side result-limit portion of #1893 and incorporates the pagination, deterministic ordering, and deadline recommendations from review.
API behavior
limit, nocursorlimitsuppliedmin(limit, 1000)rows.cursorsuppliedlimitand the sameaccounts/keysinputs are required.limit: 0moreif a match exists, and leavenext_cursorempty because the query did not advance.Successful unpaginated and final-page responses return
more: falseand an emptynext_cursor.Pagination and ordering
The input accounts and keys are deduplicated into ordered sets. Accounts are processed first, followed by keys. Authority-index rows are ordered by:
Adding
(owner, permission_name)makes rows tied on authorizer and weight deterministic across node histories.next_cursoris an opaque, versioned Base64URL encoding of:(weight, owner, permission_name)coordinateOn the next request, the server decodes the cursor and resumes with
upper_boundimmediately after that coordinate, preventing the last row of the previous page from being repeated.As with
get_table_rows, changes to chain state between pages can affect a multi-page result. The cursor is continuation state, not a snapshot identifier.Curl examples
Legacy complete-or-timeout request:
First paginated request:
An incomplete page returns:
{ "accounts": [ { "account_name": "exampleacct", "permission_name": "active", "authorizing_account": null, "authorizing_key": "PUB_K1_REPLACE_WITH_PUBLIC_KEY", "weight": 1, "threshold": 1 } ], "more": true, "next_cursor": "AQEBA..." }Pass that value back with the same inputs and an explicit limit:
Continue until
moreisfalseandnext_cursoris empty.Deadline scope
The deadline is checked while deduplicating supplied accounts/keys, before processing each input range, and while emitting matching rows. A breach throws
fc::timeout_exception; it does not produce a partial success response.This change specifically bounds account-query preparation and index traversal. It does not add a separate response-byte limit or extend the deadline into JSON serialization and network transmission after the query returns.
Validation
git diff --checkTests not run locally; CI is awaiting maintainer approval.