Skip to content

Commit

Permalink
Merge branch 'develop' into lift-log-limits
Browse files Browse the repository at this point in the history
  • Loading branch information
ahangsu committed Apr 27, 2023
2 parents acdad32 + e668c2c commit 491727f
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 4 deletions.
2 changes: 1 addition & 1 deletion algosdk/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"""str: header key for algod requests"""
INDEXER_AUTH_HEADER = "X-Indexer-API-Token"
"""str: header key for indexer requests"""
UNVERSIONED_PATHS = ["/health", "/versions", "/metrics", "/genesis"]
UNVERSIONED_PATHS = ["/health", "/versions", "/metrics", "/genesis", "/ready"]
"""str[]: paths that don't use the version path prefix"""
NO_AUTH: List[str] = []
"""str[]: requests that don't require authentication"""
Expand Down
77 changes: 77 additions & 0 deletions algosdk/v2client/algod.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ def algod_request(
try:
return json.load(resp)
except Exception as e:
# Some algod responses currently return a 200 OK
# but have an empty response.
# Do not return an error, and just return an empty response.
if resp.status == 200 and resp.length == 0:
return {}
raise error.AlgodResponseError(
"Failed to parse JSON response from algod"
) from e
Expand Down Expand Up @@ -641,6 +646,78 @@ def simulate_raw_transactions(
)
return self.simulate_transactions(request, **kwargs)

def get_sync_round(self, **kwargs: Any) -> AlgodResponseType:
"""
Get the minimum sync round for the ledger.
Returns:
Dict[str, Any]: Response from algod
"""
req = "/ledger/sync"
return self.algod_request("GET", req, **kwargs)

def set_sync_round(self, round: int, **kwargs: Any) -> AlgodResponseType:
"""
Set the minimum sync round for the ledger.
Args:
round (int): Sync round
Returns:
Dict[str, Any]: Response from algod
"""
req = f"/ledger/sync/{round}"
return self.algod_request("POST", req, **kwargs)

def unset_sync_round(self, **kwargs: Any) -> AlgodResponseType:
"""
Unset the minimum sync round for the ledger.
Returns:
Dict[str, Any]: Response from algod
"""
req = "/ledger/sync"
return self.algod_request("DELETE", req, **kwargs)

def ready(self, **kwargs: Any) -> AlgodResponseType:
"""
Returns OK if the node is healthy and fully caught up.
Returns:
Dict[str, Any]: Response from algod
"""
req = "/ready"
return self.algod_request("GET", req, **kwargs)

def get_timestamp_offset(self, **kwargs: Any) -> AlgodResponseType:
"""
Get the timestamp offset in block headers.
This feature is only available in dev mode networks.
Returns:
Dict[str, Any]: Response from algod
"""
req = "/devmode/blocks/offset"
return self.algod_request("GET", req, **kwargs)

def set_timestamp_offset(
self,
offset: int,
**kwargs: Any,
) -> AlgodResponseType:
"""
Set the timestamp offset in block headers.
This feature is only available in dev mode networks.
Args:
offset (int): Block timestamp offset
Returns:
Dict[str, Any]: Response from algod
"""
req = f"/devmode/blocks/offset/{offset}"
return self.algod_request("POST", req, **kwargs)


def _specify_round_string(
block: Union[int, None], round_num: Union[int, None]
Expand Down
8 changes: 8 additions & 0 deletions tests/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ def do_POST(self):
m = bytes(m, "ascii")
self.wfile.write(m)

def do_DELETE(self):
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
m = json.dumps({"path": self.path})
m = bytes(m, "ascii")
self.wfile.write(m)


def get_status_to_use():
f = open("tests/features/resources/mock_response_status", "r")
Expand Down
50 changes: 47 additions & 3 deletions tests/steps/other_v2_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,11 @@ def expect_path(context, path):
assert exp_query == actual_query, f"{exp_query} != {actual_query}"


@then('expect the request to be "{method}" "{path}"')
def expect_request(context, method, path):
return expect_path(context, path)


@then('expect error string to contain "{err:MaybeString}"')
def expect_error(context, err):
# TODO: this should actually do the claimed action
Expand Down Expand Up @@ -1484,7 +1489,7 @@ def make_simulate_request(context):

@then("I allow more logs on that simulate request.")
def allow_more_logs_in_request(context):
context.simulate_request.allow_more_logs = True
context.atomic_transaction_composer_return.allow_more_logs = True


@then("I attach the simulate request to simulate the transaction group.")
Expand All @@ -1498,8 +1503,12 @@ def attach_sim_request_to_txn_group_simulation(context):

@then("I check the simulation result has power packs allow-more-logging.")
def power_pack_simulation_should_pass(context):
assert context.simulate_response.eval_overrides.max_log_calls
assert context.simulate_response.eval_overrides.max_log_size
assert (
context.atomic_transaction_composer_return.eval_overrides.max_log_calls
)
assert (
context.atomic_transaction_composer_return.eval_overrides.max_log_size
)


@when("I prepare the transaction without signatures for simulation")
Expand All @@ -1524,3 +1533,38 @@ def check_missing_signatures(context, group, path):
"failure-message"
]
assert missing_sig is True


@when("we make a GetLedgerStateDelta call against round {round}")
def get_ledger_state_delta_call(context, round):
context.response = context.acl.get_ledger_state_delta(round)


@when("we make a SetSyncRound call against round {round}")
def set_sync_round_call(context, round):
context.response = context.acl.set_sync_round(round)


@when("we make a GetSyncRound call")
def get_sync_round_call(context):
context.response = context.acl.get_sync_round()


@when("we make a UnsetSyncRound call")
def unset_sync_round_call(context):
context.response = context.acl.unset_sync_round()


@when("we make a Ready call")
def ready_call(context):
context.response = context.acl.ready()


@when("we make a SetBlockTimeStampOffset call against offset {offset}")
def set_block_timestamp_offset(context, offset):
context.response = context.acl.set_timestamp_offset(offset)


@when("we make a GetBlockTimeStampOffset call")
def get_block_timestamp_offset(context):
context.response = context.acl.get_timestamp_offset()
5 changes: 5 additions & 0 deletions tests/unit.tags
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
@unit.indexer.logs
@unit.offline
@unit.program_sanity_check
@unit.ready
@unit.rekey
@unit.responses
@unit.responses.231
@unit.responses.blocksummary
@unit.responses.participationupdates
@unit.responses.sync
@unit.responses.timestamp
@unit.responses.unlimited_assets
@unit.sourcemap
@unit.sync
@unit.tealsign
@unit.timestamp
@unit.transactions
@unit.transactions.keyreg
@unit.transactions.payment
Expand Down

0 comments on commit 491727f

Please sign in to comment.