Skip to content

API: Revert application boxes pagination #572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 6 additions & 26 deletions algosdk/v2client/algod.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,41 +194,21 @@ def application_box_by_name(
return self.algod_request("GET", req, params=params, **kwargs)

def application_boxes(
self,
application_id: int,
limit: int = 0,
prefix: Optional[str] = None,
next: Optional[str] = None,
values: Optional[bool] = False,
**kwargs: Any,
self, application_id: int, limit: int = 0, **kwargs: Any
) -> AlgodResponseType:
"""
Given an application ID, return boxes in lexographical order by name. If the results must be truncated, a next-token is supplied to continue the request.
Given an application ID, return all Box names. No particular ordering is guaranteed. Request fails when client or server-side configured limits prevent returning all Box names.

NOTE: box names are returned as base64-encoded strings.

Args:
application_id (int): The ID of the application to look up.
limit (int, optional): Max number of box names to return.
If max is not set, or max == 0, returns all box-names up to the maximum configured by the algod server being queried.
prefix (str, optional): A box name prefix, in the goal app
call arg form 'encoding:value'. For ints, use the form 'int:1234'. For raw bytes, use the form 'b64:A=='.
For printable strings, use the form 'str:hello'. For addresses, use the form 'addr:XYZ...'.
next (str, optional): A box name, in the goal app call arg
form 'encoding:value'. When provided, the returned boxes begin (lexographically) with the supplied name. Callers may
implement pagination by reinvoking the endpoint with the token from a previous call's next-token.
values (bool, optional): If true, box values will be returned.
"""
query: Dict[str, Union[int, str]] = {}
if limit:
query["max"] = limit
if prefix:
query["prefix"] = prefix
if next:
query["next"] = next
if values:
query["values"] = "true"
"""
req = "/applications/" + str(application_id) + "/boxes"
return self.algod_request("GET", req, params=query, **kwargs)
params = {"max": limit} if limit else {}
return self.algod_request("GET", req, params=params, **kwargs)

def account_asset_info(
self, address: str, asset_id: int, **kwargs: Any
Expand Down
77 changes: 3 additions & 74 deletions tests/steps/application_v2_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,11 @@ def application_box_by_name(context, app_id, box_name):


@when(
'we make a GetApplicationBoxes call for applicationID {app_id} with max {max_results} prefix "{prefix:MaybeString}" next "{next:MaybeString}" values "{values:MaybeBool}"'
"we make a GetApplicationBoxes call for applicationID {app_id} with max {max_results}"
)
def application_boxes(context, app_id, max_results, prefix, next, values):
def application_boxes(context, app_id, max_results):
context.response = context.acl.application_boxes(
app_id, limit=int(max_results), prefix=prefix, next=next, values=values
app_id, limit=int(max_results)
)


Expand Down Expand Up @@ -1243,10 +1243,7 @@ def check_all_boxes(context, from_client: str, box_names: str = None):
base64.b64decode(box_encoded)
for box_encoded in box_names.split(":")
]
expected_num_boxes = len(expected_box_names)

if from_client == "algod":
advance_chain_wait_for_box(context, expected_num_boxes)
box_response = context.app_acl.application_boxes(
context.current_application_id
)
Expand All @@ -1270,74 +1267,6 @@ def check_all_boxes(context, from_client: str, box_names: str = None):
), f"Expected box names array does not match actual array {expected_box_names} != {actual_box_names}"


def advance_chain_wait_for_box(
context,
expected_num_boxes: int,
wait_rounds: int = 5,
max_attempts: int = 50,
):
"""
Advance the blockchain and wait for application boxes to persist in Algod.

Args:
context: Behave context containing necessary information like app_acl, current_application_id, sender, etc.
wait_rounds (int): The number of rounds to wait for persistence.
max_attempts (int): Maximum number of attempts to check for persistence.

Raises:
Exception: If the boxes do not persist within the specified number of attempts.
"""
algod_client = context.app_acl
app_id = context.current_application_id
sender = context.transient_pk
sender_private_key = context.transient_sk

# Get the current round
status = algod_client.status()
current_round = status.get("last-round", 0)
target_round = current_round + wait_rounds

attempts = 0
while attempts < max_attempts:
# Submit a 0-pay transaction to advance the blockchain state
params = algod_client.suggested_params()
txn = transaction.PaymentTxn(
sender,
params,
sender,
0,
note=b"Advance round for box persistence",
)
signed_txn = txn.sign(sender_private_key)
tx_id = algod_client.send_transaction(signed_txn)

# Wait for transaction confirmation
transaction.wait_for_confirmation(algod_client, tx_id, 1)

# Wait for a second before checking again
time.sleep(1)
attempts += 1

# Get the latest status
status = algod_client.status()
current_round = status.get("last-round", 0)

if current_round >= target_round:
# Check if boxes are available
try:
box_response = algod_client.application_boxes(app_id)
actual_num_boxes = len(box_response.get("boxes", []))

if actual_num_boxes == expected_num_boxes:
return # Exit once the condition is met
except Exception as e:
print(f"Error retrieving boxes: {e}")

raise Exception(
f"Timeout waiting for boxes to persist for application {app_id}. Current round: {current_round}"
)


@then(
'according to indexer, with {limit} being the parameter that limits results, and "{next_page:MaybeString}" being the parameter that sets the next result, the current application should have the following boxes "{box_names:MaybeString}".'
)
Expand Down
Loading