From 02414831ca556d35c3b79051675530454e557b2d Mon Sep 17 00:00:00 2001 From: Hang Su <87964331+ahangsu@users.noreply.github.com> Date: Wed, 11 Jan 2023 16:27:22 -0500 Subject: [PATCH] Upgrade to py-sdk v2.0 (#46) * upgrade to v2 sdk, trying to do stuffs * remember py3.9 * mypy you happy * oof my bad * questionable refactoring --- Makefile | 2 +- graviton/abi_strategy.py | 95 ++++++++++++++--------------------- graviton/blackbox.py | 19 ++++--- graviton/dryrun.py | 2 +- graviton/models.py | 9 ++-- setup.py | 2 +- tests/integration/abi_test.py | 2 +- 7 files changed, 58 insertions(+), 73 deletions(-) diff --git a/Makefile b/Makefile index 8b915585..cfc186fe 100644 --- a/Makefile +++ b/Makefile @@ -59,7 +59,7 @@ local-blackbox: local-blackbox-smoke integration-test NOTEBOOK = notebooks/quadratic_factoring_game.ipynb # assumes already ran `make pip-notebooks` -local-notebook: +local-notebook: jupyter retro $(NOTEBOOK) # assumes act is installed, e.g. via `brew install act`: diff --git a/graviton/abi_strategy.py b/graviton/abi_strategy.py index a6893f2f..f287e10b 100644 --- a/graviton/abi_strategy.py +++ b/graviton/abi_strategy.py @@ -4,10 +4,9 @@ TODO: Leverage Hypothesis! """ from abc import ABC, abstractmethod -from collections import OrderedDict import random import string -from typing import Callable, Dict, List, Optional, cast +from typing import List, Optional, Sequence, cast from algosdk import abi, encoding @@ -112,17 +111,6 @@ def get(self) -> PyTypes: raise ValueError(f"Unexpected abi_type {self.abi_type}") - def map( - self, - waterfall: Dict[abi.ABIType, Callable[..., PyTypes]], - *args, - **kwargs, - ) -> PyTypes: - for abi_type, call in waterfall.items(): - if isinstance(self.abi_type, abi_type): - return call(*args, **kwargs) - return waterfall["DEFAULT"](*args, **kwargs) - def mutate_for_roundtrip(self, py_abi_instance: PyTypes) -> PyTypes: def not_implemented(_): raise NotImplementedError(f"Currently cannot handle type {self.abi_type}") @@ -140,49 +128,42 @@ def address_logic(x): ) ) - waterfall = OrderedDict( - [ - (abi.UfixedType, not_implemented), - (abi.BoolType, lambda x: not x), - (abi.UintType, lambda x: (1 << self.abi_type.bit_size) - 1 - x), - ( - abi.ByteType, - lambda x: RandomABIStrategy(abi.UintType(8)).mutate_for_roundtrip( - x - ), - ), - ( - abi.TupleType, - lambda x: [ - RandomABIStrategy(child_type).mutate_for_roundtrip(x[i]) - for i, child_type in enumerate(self.abi_type.child_types) - ], - ), - ( - abi.ArrayStaticType, - lambda x: [ - RandomABIStrategy( - self.abi_type.child_type - ).mutate_for_roundtrip(y) - for y in x - ], - ), - (abi.AddressType, address_logic), - ( - abi.ArrayDynamicType, - lambda x: [ - RandomABIStrategy( - self.abi_type.child_type - ).mutate_for_roundtrip(y) - for y in x - ], - ), - (abi.StringType, lambda x: "".join(reversed(x))), - ("DEFAULT", unexpected_type), + if isinstance(self.abi_type, abi.UfixedType): + return not_implemented(py_abi_instance) + elif isinstance(self.abi_type, abi.BoolType): + return not py_abi_instance + elif isinstance(self.abi_type, abi.UintType): + assert isinstance(py_abi_instance, int) + return (1 << self.abi_type.bit_size) - 1 - py_abi_instance + elif isinstance(self.abi_type, abi.ByteType): + return RandomABIStrategy(abi.UintType(8)).mutate_for_roundtrip( + py_abi_instance + ) + elif isinstance(self.abi_type, abi.TupleType): + assert isinstance(py_abi_instance, Sequence) + return [ + RandomABIStrategy(child_type).mutate_for_roundtrip(py_abi_instance[i]) + for i, child_type in enumerate(self.abi_type.child_types) ] - ) - - return self.map(waterfall, py_abi_instance) + elif isinstance(self.abi_type, abi.ArrayStaticType): + assert isinstance(py_abi_instance, Sequence) + return [ + RandomABIStrategy(self.abi_type.child_type).mutate_for_roundtrip(y) + for y in py_abi_instance + ] + elif isinstance(self.abi_type, abi.AddressType): + return address_logic(py_abi_instance) + elif isinstance(self.abi_type, abi.ArrayDynamicType): + assert isinstance(py_abi_instance, Sequence) + return [ + RandomABIStrategy(self.abi_type.child_type).mutate_for_roundtrip(y) + for y in py_abi_instance + ] + elif isinstance(self.abi_type, abi.StringType): + assert isinstance(py_abi_instance, str) + return "".join(reversed(py_abi_instance)) + else: + return unexpected_type(py_abi_instance) class RandomABIStrategyHalfSized(RandomABIStrategy): @@ -199,4 +180,6 @@ def get(self) -> PyTypes: if not isinstance(self.abi_type, abi.UintType): return full_random - return full_random % (1 << (self.abi_type.bit_size // 2)) + return cast(int, full_random) % ( + 1 << (cast(abi.UintType, self.abi_type).bit_size // 2) + ) diff --git a/graviton/blackbox.py b/graviton/blackbox.py index c1869652..9a6aaaea 100644 --- a/graviton/blackbox.py +++ b/graviton/blackbox.py @@ -23,7 +23,7 @@ from algosdk import abi from algosdk.v2client.algod import AlgodClient from algosdk.v2client.models import DryrunRequest -from algosdk.future.transaction import ( +from algosdk.transaction import ( OnComplete, StateSchema, SuggestedParams, @@ -725,7 +725,7 @@ def execute_one_dryrun( args = tuple(args) if method.returns.type != abi.Returns.VOID: - abi_return_type = method.returns.type + abi_return_type = cast(abi.ABIType, method.returns.type) encoded_args = DryRunEncoder.encode_args( args, abi_types=abi_argument_types, validation=validation @@ -746,7 +746,7 @@ def execute_one_dryrun( if verbose: print(f"{cls}::execute_one_dryrun(): {dryrun_resp=}") return DryRunInspector.from_single_response( - dryrun_resp, args, encoded_args, abi_type=abi_return_type + dryrun_resp, args, encoded_args, abi_type=cast(abi.ABIType, abi_return_type) ) @classmethod @@ -851,7 +851,10 @@ def argument_types(self, method: Optional[str] = None) -> List[abi.ABIType]: if not method: return [] - return [arg.type for arg in self.contract.get_method_by_name(method).args] + return [ + cast(abi.ABIType, arg.type) + for arg in self.contract.get_method_by_name(method).args + ] def generate_inputs(self, method: Optional[str]) -> List[Sequence[PyTypes]]: """ @@ -890,7 +893,7 @@ def validate_inputs(self, method: Optional[str], inputs: List[Sequence[PyTypes]] return arg_types = self.argument_types(method) - selector_if_needed: Optional[str] = None + selector_if_needed: Optional[bytes] = None if self.handle_selector: selector_if_needed = self.contract.get_method_by_name(method).get_selector() @@ -904,7 +907,7 @@ def validate_inputs(self, method: Optional[str], inputs: List[Sequence[PyTypes]] break if targs[0] != selector_if_needed: - error = f"{pfx}expected selector={selector_if_needed} at arg 0 but got {targs[0]!r}" + error = f"{pfx}expected selector={selector_if_needed!r} at arg 0 but got {targs[0]!r}" break assert not error, error @@ -1018,7 +1021,7 @@ def __init__( txn_index: int, args: Sequence[PyTypes], encoded_args: List[ArgType], - abi_type: abi.ABIType = None, + abi_type: Optional[abi.ABIType] = None, ): txns = dryrun_resp.get("txns", []) assert txns, "Dry Run response is missing transactions" @@ -1091,7 +1094,7 @@ def from_single_response( dryrun_resp: dict, args: Sequence[PyTypes], encoded_args: List[ArgType], - abi_type: abi.ABIType = None, + abi_type: Optional[abi.ABIType] = None, ) -> "DryRunInspector": error = dryrun_resp.get("error") assert not error, f"dryrun response included the following error: [{error}]" diff --git a/graviton/dryrun.py b/graviton/dryrun.py index c9642c19..3bbc0ec3 100644 --- a/graviton/dryrun.py +++ b/graviton/dryrun.py @@ -1,7 +1,7 @@ import string from typing import Any, Dict, List -from algosdk.future import transaction +from algosdk import transaction from algosdk.encoding import encode_address from algosdk.v2client.models import ( DryrunRequest, diff --git a/graviton/models.py b/graviton/models.py index 55581ca3..eea80cb9 100644 --- a/graviton/models.py +++ b/graviton/models.py @@ -3,8 +3,9 @@ from typing import List, Optional, Sequence, Union from algosdk.encoding import encode_address -from algosdk.future import transaction -from algosdk.v2client.models import Account, TealKeyValue +from algosdk.transaction import OnComplete +from algosdk.v2client.models.account import Account +from algosdk.v2client.models.teal_key_value import TealKeyValue ZERO_ADDRESS = encode_address(bytes(32)) @@ -24,9 +25,7 @@ def get_run_mode(app): on_complete = ( app.get("on_complete") if isinstance(app, dict) else app.on_complete ) - run_mode = ( - "clearp" if on_complete == transaction.OnComplete.ClearStateOC else "approv" - ) + run_mode = "clearp" if on_complete == OnComplete.ClearStateOC else "approv" return run_mode diff --git a/setup.py b/setup.py index d7e42534..664ce0e5 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ author="Algorand", author_email="pypiservice@algorand.com", python_requires=">=3.9", - install_requires=["py-algorand-sdk>=1.16.1", "tabulate==0.9.0"], + install_requires=["py-algorand-sdk>=2.0.0", "tabulate==0.9.0"], extras_require={ "development": [ "black==22.10.0", diff --git a/tests/integration/abi_test.py b/tests/integration/abi_test.py index 10a8976a..517bf82a 100644 --- a/tests/integration/abi_test.py +++ b/tests/integration/abi_test.py @@ -22,7 +22,7 @@ from typing import Any, Dict, List, Optional, Tuple from algosdk import abi -from algosdk.future.transaction import OnComplete +from algosdk.transaction import OnComplete from graviton.blackbox import ( ABIContractExecutor,