Skip to content

Paginate get_cashtoken_addresses, get_unspent, and get_transactions#165

Open
yashasvi-ranawat wants to merge 2 commits into
pybitcash:masterfrom
yashasvi-ranawat:paginate-cashtoken-addresses-query
Open

Paginate get_cashtoken_addresses, get_unspent, and get_transactions#165
yashasvi-ranawat wants to merge 2 commits into
pybitcash:masterfrom
yashasvi-ranawat:paginate-cashtoken-addresses-query

Conversation

@yashasvi-ranawat

@yashasvi-ranawat yashasvi-ranawat commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Problem

Unbounded search_output queries against the Chaingraph endpoint fail with a database error for addresses or tokens with many UTXOs/transactions. The previous implementation fetched all results in a single request with DEFAULT_TIMEOUT=5s.

Changes

  • ChaingraphAPI.py: Added CASHTOKEN_ADDRESSES_PAGE_SIZE, UNSPENT_PAGE_SIZE, and TRANSACTIONS_PAGE_SIZE (all 1000). get_cashtoken_addresses, get_unspent, and get_transactions now use $limit/$offset GraphQL variables and loop with limit/offset pagination until a page returns fewer than PAGE_SIZE rows.
  • services.py: Added LONG_DEFAULT_TIMEOUT = 60 and switched all three methods to use it (was DEFAULT_TIMEOUT = 5).

Real-world tests

Moria USD (b38a33f750f84c5c169a6f23cb873e6e79605021585d4f3408789689ed87f366): fetched 4,710 UTXOs → 595 unique addresses in ~57 seconds — 5 pages of 1,000 at 2–19 seconds per page.

bitcoincash:qpnezxnfun4vgwypja3j4f4fzjdyf7ddgvfwvza6f5: fetched 18,823 UTXOs in ~35 seconds via Chaingraph (19 pages) vs 4 seconds via Fulcrum; 41,274 transactions in 3 seconds via Fulcrum.

Note: For plain get_unspent and get_transactions on high-activity addresses, the Fulcrum endpoint is significantly faster — it is purpose-built for address/scripthash lookups. Chaingraph's value is in richer queries such as get_cashtoken_addresses, where token filtering and cross-referencing on-chain data aren't expressible in the Electrum protocol.

Unbounded queries against the Chaingraph endpoint fail with a DB error
for tokens with many holders (e.g. 4710 UTXOs). Add limit/offset
pagination with CASHTOKEN_ADDRESSES_PAGE_SIZE=1000 and use
LONG_DEFAULT_TIMEOUT=60 per page in NetworkAPI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@yashasvi-ranawat
yashasvi-ranawat marked this pull request as ready for review April 29, 2026 20:06
Unbounded search_output queries fail or time out for addresses with many
UTXOs or transactions. Add limit/offset pagination (PAGE_SIZE=1000) and
use LONG_DEFAULT_TIMEOUT=60 per page, matching the pattern introduced for
get_cashtoken_addresses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@yashasvi-ranawat yashasvi-ranawat changed the title Paginate get_cashtoken_addresses to handle large token holders Paginate get_cashtoken_addresses, get_unspent, and get_transactions May 2, 2026
@merc1er
merc1er requested a review from Copilot May 17, 2026 13:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses Chaingraph query failures for high-activity addresses/tokens by adding limit/offset pagination to previously unbounded GraphQL queries, and increasing timeouts to accommodate multi-page fetches.

Changes:

  • Added page-size constants and implemented limit/offset pagination loops for Chaingraph get_transactions, get_unspent, and get_cashtoken_addresses.
  • Introduced a longer default timeout for potentially large result sets and applied it to the above calls via NetworkAPI.
  • Added a docstring note warning that cashtoken holder queries may take over a minute due to pagination.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
bitcash/network/services.py Adds a long timeout constant and uses it for large-result calls; updates docstring note for cashtoken address lookup.
bitcash/network/APIs/ChaingraphAPI.py Adds pagination parameters and loops to split large Chaingraph result sets into pages.
Comments suppressed due to low confidence (4)

bitcash/network/APIs/ChaingraphAPI.py:239

  • The current de-duplication strategy sorted(set(transactions), key=lambda x: transactions.index(x)) is O(n^2) due to repeated index() calls. With pagination enabling tens of thousands of transactions, this can become a major bottleneck; consider an order-preserving linear-time approach (e.g., tracking a seen set while building the list).
        transactions = [_[0] for _ in transactions][::-1]
        # remove duplicates, when address pays itself, spending tx and locking
        # tx are same transactions
        transactions = sorted(set(transactions), key=lambda x: transactions.index(x))
        return transactions

bitcash/network/APIs/ChaingraphAPI.py:336

  • This paginated loop queries block(limit: 1, order_by: {height: desc}, ...) on every page even though the block height is only used as a constant reference. Consider fetching the latest block height once (separate query) and then paginating only the search_output query to reduce per-page load and latency.
query GetUTXO($lb: _text!, $node: String!, $limit: Int!, $offset: Int!) {
  block(
    limit: 1
    order_by: { height: desc }
    where: { accepted_by: { node: { name: { _like: $node } } } }
  ) {

bitcash/network/APIs/ChaingraphAPI.py:360

  • Offset/limit pagination is used here, but the search_output query does not specify an order_by. Without deterministic ordering, offset pagination can return overlapping pages or skip rows, causing missing UTXOs. Add a stable order_by (e.g., transaction_hash asc, output_index asc) when paginating.
      ]
    }
    limit: $limit
    offset: $offset
  ) {

bitcash/network/APIs/ChaingraphAPI.py:547

  • Offset/limit pagination is used for output holders, but the output query does not specify an order_by. Without deterministic ordering, offset pagination can skip/duplicate rows between pages and produce incomplete address sets. Add a stable order_by (e.g., by transaction_hash/output_index or another unique ordering) when paginating.
      ]
    }
    limit: $limit
    offset: $offset
  ) {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 31 to +36
# Default API call total time timeout
DEFAULT_TIMEOUT = 5

# Timeout for queries that may fetch large result sets
LONG_DEFAULT_TIMEOUT = 60

@@ -364,6 +367,8 @@ def get_cashtoken_addresses(
:param has_token: If True, only return addresses holding fungible tokens of this category.
:returns: A set of addresses holding the cashtoken.
:raises ConnectionError: If all API services fail.
@@ -161,6 +164,8 @@ def get_transactions(self, address: str, *args, **kwargs) -> list[str]:
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants