diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ddc7da01b..6b203bd94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,7 @@ jobs: echo $installed [[ $installed =~ "Python ${expected}" ]] && echo "Configured Python" || (echo "Failed to configure Python" && exit 1) - name: Start algod - run: make algod-start + run: make algod-start-report - name: Install python dependencies run: make setup-development - name: Integration Tests Only diff --git a/Makefile b/Makefile index 6a7708606..0439a6a4f 100644 --- a/Makefile +++ b/Makefile @@ -69,9 +69,13 @@ lint-and-test: check-generate-init lint test-unit algod-start: docker compose up -d algod --wait +algod-version: + docker compose exec algod goal --version + +algod-start-report: algod-start algod-version + algod-stop: docker compose stop algod - integration-run: pytest -n $(NUM_PROCS) --durations=10 -sv tests/integration -m "not serial" pytest --durations=10 -sv tests/integration -m serial diff --git a/pyteal/ast/router.py b/pyteal/ast/router.py index 48721d6e2..465223c04 100644 --- a/pyteal/ast/router.py +++ b/pyteal/ast/router.py @@ -259,6 +259,15 @@ def approval_construction(self) -> Optional[Expr]: ) return Cond(*[[n.condition, n.branch] for n in conditions_n_branches]) + def get_method_config(self) -> MethodConfig: + return MethodConfig( + no_op=self.no_op.call_config, + opt_in=self.opt_in.call_config, + close_out=self.close_out.call_config, + update_application=self.update_application.call_config, + delete_application=self.delete_application.call_config, + ) + BareCallActions.__module__ = "pyteal" @@ -806,6 +815,12 @@ def __init__( self.bare_call_actions: BareCallActions = bare_calls or BareCallActions() + # maps method signature (or None for bare call) to MethodConfig: + self.method_configs: dict[str | None, MethodConfig] = dict() + + if not self.bare_call_actions.is_empty(): + self.method_configs[None] = self.bare_call_actions.get_method_config() + def _clean(self) -> None: self.approval_ast._clean_bare_calls() @@ -831,7 +846,7 @@ def add_method_handler( """ if not isinstance(method_call, ABIReturnSubroutine): raise TealInputError( - "for adding method handler, must be ABIReturnSubroutine" + f"for adding method handler, must be ABIReturnSubroutine but method_call is {type(method_call)}" ) method_signature = method_call.method_signature(overriding_name) if method_config is None: @@ -862,6 +877,7 @@ def add_method_handler( self.approval_ast.add_method_to_ast( method_signature, method_approval_cond, method_call ) + self.method_configs[method_signature] = method_config return method_call def method( diff --git a/pyteal/ast/router_test.py b/pyteal/ast/router_test.py index 9df220edf..2eb1fb937 100644 --- a/pyteal/ast/router_test.py +++ b/pyteal/ast/router_test.py @@ -380,7 +380,7 @@ def test_bare_call_config_clear_state_failure(): assert "Attempt to construct clear state program from bare app call" in str(tie) -def test_bare_call_actions_asdict(): +def test_BareCallActions_asdict(): no_action = pt.OnCompleteAction() del_action = pt.OnCompleteAction(action=pt.Int(1), call_config=pt.CallConfig.ALL) close_action = pt.OnCompleteAction(action=pt.Int(2), call_config=pt.CallConfig.CALL) @@ -409,6 +409,35 @@ def test_bare_call_actions_asdict(): } +def test_BareCallActions_get_method_config(): + from pyteal.ast.router import MethodConfig, CallConfig + + cc_all, cc_call, cc_create = ( + pt.CallConfig.ALL, + pt.CallConfig.CALL, + pt.CallConfig.CREATE, + ) + optin_action = pt.OnCompleteAction(action=pt.Int(1), call_config=cc_all) + noop_action = pt.OnCompleteAction(action=pt.Int(2), call_config=cc_call) + update_action = pt.OnCompleteAction(action=pt.Int(3), call_config=cc_create) + + bca = pt.BareCallActions( + update_application=update_action, + opt_in=optin_action, + no_op=noop_action, + ) + + mc = bca.get_method_config() + assert mc == MethodConfig( + close_out=CallConfig.NEVER, + delete_application=CallConfig.NEVER, + no_op=cc_call, + opt_in=cc_all, + update_application=cc_create, + clear_state=CallConfig.NEVER, + ) + + def test_router_register_method_clear_state_failure(): router = pt.Router("doomedToFail") diff --git a/pyteal/compiler/compiler_test.py b/pyteal/compiler/compiler_test.py index 9f7de4ac8..5e855944f 100644 --- a/pyteal/compiler/compiler_test.py +++ b/pyteal/compiler/compiler_test.py @@ -1,7 +1,11 @@ +from pathlib import Path + import pytest import pyteal as pt +ROUTER_FIXTURES = Path.cwd() / "tests" / "teal" / "router" + def test_compile_single(): expr = pt.Int(1) @@ -2195,2122 +2199,258 @@ def access_b4_store(magic_num: pt.abi.Uint64, *, output: pt.abi.Uint64): pt.compileTeal(program_access_b4_store_broken, pt.Mode.Application, version=6) -def test_router_app(): - def add_methods_to_router(router: pt.Router): - @pt.ABIReturnSubroutine - def add( - a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64 - ) -> pt.Expr: - return output.set(a.get() + b.get()) - - meth = router.add_method_handler(add) - assert meth.method_signature() == "add(uint64,uint64)uint64" - - @pt.ABIReturnSubroutine - def sub( - a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64 - ) -> pt.Expr: - return output.set(a.get() - b.get()) - - meth = router.add_method_handler(sub) - assert meth.method_signature() == "sub(uint64,uint64)uint64" - - @pt.ABIReturnSubroutine - def mul( - a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64 - ) -> pt.Expr: - return output.set(a.get() * b.get()) - - meth = router.add_method_handler(mul) - assert meth.method_signature() == "mul(uint64,uint64)uint64" - - @pt.ABIReturnSubroutine - def div( - a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64 - ) -> pt.Expr: - return output.set(a.get() / b.get()) - - meth = router.add_method_handler(div) - assert meth.method_signature() == "div(uint64,uint64)uint64" - - @pt.ABIReturnSubroutine - def mod( - a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64 - ) -> pt.Expr: - return output.set(a.get() % b.get()) - - meth = router.add_method_handler(mod) - assert meth.method_signature() == "mod(uint64,uint64)uint64" - - @pt.ABIReturnSubroutine - def all_laid_to_args( - _a: pt.abi.Uint64, - _b: pt.abi.Uint64, - _c: pt.abi.Uint64, - _d: pt.abi.Uint64, - _e: pt.abi.Uint64, - _f: pt.abi.Uint64, - _g: pt.abi.Uint64, - _h: pt.abi.Uint64, - _i: pt.abi.Uint64, - _j: pt.abi.Uint64, - _k: pt.abi.Uint64, - _l: pt.abi.Uint64, - _m: pt.abi.Uint64, - _n: pt.abi.Uint64, - _o: pt.abi.Uint64, - _p: pt.abi.Uint64, - *, - output: pt.abi.Uint64, - ): - return output.set( - _a.get() - + _b.get() - + _c.get() - + _d.get() - + _e.get() - + _f.get() - + _g.get() - + _h.get() - + _i.get() - + _j.get() - + _k.get() - + _l.get() - + _m.get() - + _n.get() - + _o.get() - + _p.get() - ) +def add_methods_to_router(router: pt.Router): + @pt.ABIReturnSubroutine + def add(a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64) -> pt.Expr: + return output.set(a.get() + b.get()) - meth = router.add_method_handler(all_laid_to_args) - assert ( - meth.method_signature() - == "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" - ) + meth = router.add_method_handler(add) + assert meth.method_signature() == "add(uint64,uint64)uint64" - @pt.ABIReturnSubroutine - def empty_return_subroutine() -> pt.Expr: - return pt.Log(pt.Bytes("appear in both approval and clear state")) + @pt.ABIReturnSubroutine + def sub(a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64) -> pt.Expr: + return output.set(a.get() - b.get()) - meth = router.add_method_handler( - empty_return_subroutine, - method_config=pt.MethodConfig( - no_op=pt.CallConfig.CALL, - opt_in=pt.CallConfig.ALL, - ), - ) - assert meth.method_signature() == "empty_return_subroutine()void" + meth = router.add_method_handler(sub) + assert meth.method_signature() == "sub(uint64,uint64)uint64" - @pt.ABIReturnSubroutine - def log_1(*, output: pt.abi.Uint64) -> pt.Expr: - return output.set(1) + @pt.ABIReturnSubroutine + def mul(a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64) -> pt.Expr: + return output.set(a.get() * b.get()) - meth = router.add_method_handler( - log_1, - method_config=pt.MethodConfig( - no_op=pt.CallConfig.CALL, - opt_in=pt.CallConfig.CALL, - ), - ) + meth = router.add_method_handler(mul) + assert meth.method_signature() == "mul(uint64,uint64)uint64" - assert meth.method_signature() == "log_1()uint64" + @pt.ABIReturnSubroutine + def div(a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64) -> pt.Expr: + return output.set(a.get() / b.get()) - @pt.ABIReturnSubroutine - def log_creation(*, output: pt.abi.String) -> pt.Expr: - return output.set("logging creation") + meth = router.add_method_handler(div) + assert meth.method_signature() == "div(uint64,uint64)uint64" - meth = router.add_method_handler( - log_creation, method_config=pt.MethodConfig(no_op=pt.CallConfig.CREATE) - ) - assert meth.method_signature() == "log_creation()string" + @pt.ABIReturnSubroutine + def mod(a: pt.abi.Uint64, b: pt.abi.Uint64, *, output: pt.abi.Uint64) -> pt.Expr: + return output.set(a.get() % b.get()) - on_completion_actions = pt.BareCallActions( - opt_in=pt.OnCompleteAction.call_only(pt.Log(pt.Bytes("optin call"))), - ) + meth = router.add_method_handler(mod) + assert meth.method_signature() == "mod(uint64,uint64)uint64" - with pytest.raises(pt.TealInputError) as e: - pt.Router("will-error", on_completion_actions).compile_program( - version=6, optimize=pt.OptimizeOptions(frame_pointers=True) + @pt.ABIReturnSubroutine + def all_laid_to_args( + _a: pt.abi.Uint64, + _b: pt.abi.Uint64, + _c: pt.abi.Uint64, + _d: pt.abi.Uint64, + _e: pt.abi.Uint64, + _f: pt.abi.Uint64, + _g: pt.abi.Uint64, + _h: pt.abi.Uint64, + _i: pt.abi.Uint64, + _j: pt.abi.Uint64, + _k: pt.abi.Uint64, + _l: pt.abi.Uint64, + _m: pt.abi.Uint64, + _n: pt.abi.Uint64, + _o: pt.abi.Uint64, + _p: pt.abi.Uint64, + *, + output: pt.abi.Uint64, + ): + return output.set( + _a.get() + + _b.get() + + _c.get() + + _d.get() + + _e.get() + + _f.get() + + _g.get() + + _h.get() + + _i.get() + + _j.get() + + _k.get() + + _l.get() + + _m.get() + + _n.get() + + _o.get() + + _p.get() ) - assert "Frame pointers aren't available" in str(e.value) + meth = router.add_method_handler(all_laid_to_args) + assert ( + meth.method_signature() + == "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" + ) - _router_with_oc = pt.Router( - "ASimpleQuestionablyRobustContract", - on_completion_actions, - clear_state=pt.Approve(), + @pt.ABIReturnSubroutine + def empty_return_subroutine() -> pt.Expr: + return pt.Log(pt.Bytes("appear in both approval and clear state")) + + meth = router.add_method_handler( + empty_return_subroutine, + method_config=pt.MethodConfig( + no_op=pt.CallConfig.CALL, + opt_in=pt.CallConfig.ALL, + ), ) - add_methods_to_router(_router_with_oc) - ( - actual_ap_with_oc_compiled, - actual_csp_with_oc_compiled, - _, - ) = _router_with_oc.compile_program(version=6) - - expected_ap_with_oc = """#pragma version 6 -txn NumAppArgs -int 0 -== -bnz main_l20 -txna ApplicationArgs 0 -method "add(uint64,uint64)uint64" -== -bnz main_l19 -txna ApplicationArgs 0 -method "sub(uint64,uint64)uint64" -== -bnz main_l18 -txna ApplicationArgs 0 -method "mul(uint64,uint64)uint64" -== -bnz main_l17 -txna ApplicationArgs 0 -method "div(uint64,uint64)uint64" -== -bnz main_l16 -txna ApplicationArgs 0 -method "mod(uint64,uint64)uint64" -== -bnz main_l15 -txna ApplicationArgs 0 -method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" -== -bnz main_l14 -txna ApplicationArgs 0 -method "empty_return_subroutine()void" -== -bnz main_l13 -txna ApplicationArgs 0 -method "log_1()uint64" -== -bnz main_l12 -txna ApplicationArgs 0 -method "log_creation()string" -== -bnz main_l11 -err -main_l11: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -== -&& -assert -callsub logcreation_8 -store 67 -byte 0x151f7c75 -load 67 -concat -log -int 1 -return -main_l12: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -txn ApplicationID -int 0 -!= -&& -|| -assert -callsub log1_7 -store 65 -byte 0x151f7c75 -load 65 -itob -concat -log -int 1 -return -main_l13: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -|| -assert -callsub emptyreturnsubroutine_6 -int 1 -return -main_l14: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 30 -txna ApplicationArgs 2 -btoi -store 31 -txna ApplicationArgs 3 -btoi -store 32 -txna ApplicationArgs 4 -btoi -store 33 -txna ApplicationArgs 5 -btoi -store 34 -txna ApplicationArgs 6 -btoi -store 35 -txna ApplicationArgs 7 -btoi -store 36 -txna ApplicationArgs 8 -btoi -store 37 -txna ApplicationArgs 9 -btoi -store 38 -txna ApplicationArgs 10 -btoi -store 39 -txna ApplicationArgs 11 -btoi -store 40 -txna ApplicationArgs 12 -btoi -store 41 -txna ApplicationArgs 13 -btoi -store 42 -txna ApplicationArgs 14 -btoi -store 43 -txna ApplicationArgs 15 -store 46 -load 46 -int 0 -extract_uint64 -store 44 -load 46 -int 8 -extract_uint64 -store 45 -load 30 -load 31 -load 32 -load 33 -load 34 -load 35 -load 36 -load 37 -load 38 -load 39 -load 40 -load 41 -load 42 -load 43 -load 44 -load 45 -callsub alllaidtoargs_5 -store 47 -byte 0x151f7c75 -load 47 -itob -concat -log -int 1 -return -main_l15: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 24 -txna ApplicationArgs 2 -btoi -store 25 -load 24 -load 25 -callsub mod_4 -store 26 -byte 0x151f7c75 -load 26 -itob -concat -log -int 1 -return -main_l16: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 18 -txna ApplicationArgs 2 -btoi -store 19 -load 18 -load 19 -callsub div_3 -store 20 -byte 0x151f7c75 -load 20 -itob -concat -log -int 1 -return -main_l17: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 12 -txna ApplicationArgs 2 -btoi -store 13 -load 12 -load 13 -callsub mul_2 -store 14 -byte 0x151f7c75 -load 14 -itob -concat -log -int 1 -return -main_l18: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 6 -txna ApplicationArgs 2 -btoi -store 7 -load 6 -load 7 -callsub sub_1 -store 8 -byte 0x151f7c75 -load 8 -itob -concat -log -int 1 -return -main_l19: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 0 -txna ApplicationArgs 2 -btoi -store 1 -load 0 -load 1 -callsub add_0 -store 2 -byte 0x151f7c75 -load 2 -itob -concat -log -int 1 -return -main_l20: -txn OnCompletion -int OptIn -== -bnz main_l22 -err -main_l22: -txn ApplicationID -int 0 -!= -assert -byte "optin call" -log -int 1 -return + assert meth.method_signature() == "empty_return_subroutine()void" -// add -add_0: -store 4 -store 3 -load 3 -load 4 -+ -store 5 -load 5 -retsub + @pt.ABIReturnSubroutine + def log_1(*, output: pt.abi.Uint64) -> pt.Expr: + return output.set(1) + + meth = router.add_method_handler( + log_1, + method_config=pt.MethodConfig( + no_op=pt.CallConfig.CALL, + opt_in=pt.CallConfig.CALL, + ), + ) -// sub -sub_1: -store 10 -store 9 -load 9 -load 10 -- -store 11 -load 11 -retsub + assert meth.method_signature() == "log_1()uint64" -// mul -mul_2: -store 16 -store 15 -load 15 -load 16 -* -store 17 -load 17 -retsub + @pt.ABIReturnSubroutine + def log_creation(*, output: pt.abi.String) -> pt.Expr: + return output.set("logging creation") -// div -div_3: -store 22 -store 21 -load 21 -load 22 -/ -store 23 -load 23 -retsub + meth = router.add_method_handler( + log_creation, method_config=pt.MethodConfig(no_op=pt.CallConfig.CREATE) + ) + assert meth.method_signature() == "log_creation()string" -// mod -mod_4: -store 28 -store 27 -load 27 -load 28 -% -store 29 -load 29 -retsub -// all_laid_to_args -alllaidtoargs_5: -store 63 -store 62 -store 61 -store 60 -store 59 -store 58 -store 57 -store 56 -store 55 -store 54 -store 53 -store 52 -store 51 -store 50 -store 49 -store 48 -load 48 -load 49 -+ -load 50 -+ -load 51 -+ -load 52 -+ -load 53 -+ -load 54 -+ -load 55 -+ -load 56 -+ -load 57 -+ -load 58 -+ -load 59 -+ -load 60 -+ -load 61 -+ -load 62 -+ -load 63 -+ -store 64 -load 64 -retsub +ON_COMPLETION_ACTIONS = pt.BareCallActions( + opt_in=pt.OnCompleteAction.call_only(pt.Log(pt.Bytes("optin call"))), +) -// empty_return_subroutine -emptyreturnsubroutine_6: -byte "appear in both approval and clear state" -log -retsub +FIRST_ROUTER = pt.Router( + "ASimpleQuestionablyRobustContract", + ON_COMPLETION_ACTIONS, + clear_state=pt.Approve(), +) +add_methods_to_router(FIRST_ROUTER) -// log_1 -log1_7: -int 1 -store 66 -load 66 -retsub -// log_creation -logcreation_8: -byte 0x00106c6f6767696e67206372656174696f6e -store 68 -load 68 -retsub""".strip() +def router_app_tester() -> tuple[list[pt.Router], dict[str, str]]: + routers = [] + sources = {} - assert expected_ap_with_oc == actual_ap_with_oc_compiled + def append_router_info(rinfo, programs): + assert len(rinfo) == 3 + assert len(programs) == 2 + routers.append(rinfo) + sources[rinfo[:2]] = programs - expected_csp_with_oc = """#pragma version 6 -int 1 -return""".strip() - assert expected_csp_with_oc == actual_csp_with_oc_compiled + # V6 not ready for Frame Pointers: + with pytest.raises(pt.TealInputError) as e: + pt.Router("will-error", ON_COMPLETION_ACTIONS).compile_program( + version=6, optimize=pt.OptimizeOptions(frame_pointers=True) + ) + assert "Frame pointers aren't available" in str(e.value) + def router_compilarison(router, version, fixture_ap, fixture_csp, key): + actual_ap, actual_csp, _ = router.compile_program(version=version) + + with open(ROUTER_FIXTURES / fixture_ap) as f: + expected_ap_with_oc = f.read() + with open(ROUTER_FIXTURES / fixture_csp) as f: + expected_csp_with_oc = f.read() + assert expected_ap_with_oc == actual_ap + assert expected_csp_with_oc == actual_csp + append_router_info( + ( + key, + version, + router, + ), + ( + actual_ap, + actual_csp, + ), + ) + + # QUESTIONABLE V6: + _router_with_oc = FIRST_ROUTER + router_compilarison( + _router_with_oc, + 6, + "questionable_approval_v6.teal", + "questionable_clear_v6.teal", + "questionable", + ) + + # YACC V6: _router_without_oc = pt.Router( "yetAnotherContractConstructedFromRouter", clear_state=pt.Approve() ) add_methods_to_router(_router_without_oc) - ( - actual_ap_without_oc_compiled, - actual_csp_without_oc_compiled, - _, - ) = _router_without_oc.compile_program(version=6) - expected_ap_without_oc = """#pragma version 6 -txna ApplicationArgs 0 -method "add(uint64,uint64)uint64" -== -bnz main_l18 -txna ApplicationArgs 0 -method "sub(uint64,uint64)uint64" -== -bnz main_l17 -txna ApplicationArgs 0 -method "mul(uint64,uint64)uint64" -== -bnz main_l16 -txna ApplicationArgs 0 -method "div(uint64,uint64)uint64" -== -bnz main_l15 -txna ApplicationArgs 0 -method "mod(uint64,uint64)uint64" -== -bnz main_l14 -txna ApplicationArgs 0 -method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" -== -bnz main_l13 -txna ApplicationArgs 0 -method "empty_return_subroutine()void" -== -bnz main_l12 -txna ApplicationArgs 0 -method "log_1()uint64" -== -bnz main_l11 -txna ApplicationArgs 0 -method "log_creation()string" -== -bnz main_l10 -err -main_l10: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -== -&& -assert -callsub logcreation_8 -store 67 -byte 0x151f7c75 -load 67 -concat -log -int 1 -return -main_l11: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -txn ApplicationID -int 0 -!= -&& -|| -assert -callsub log1_7 -store 65 -byte 0x151f7c75 -load 65 -itob -concat -log -int 1 -return -main_l12: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -|| -assert -callsub emptyreturnsubroutine_6 -int 1 -return -main_l13: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 30 -txna ApplicationArgs 2 -btoi -store 31 -txna ApplicationArgs 3 -btoi -store 32 -txna ApplicationArgs 4 -btoi -store 33 -txna ApplicationArgs 5 -btoi -store 34 -txna ApplicationArgs 6 -btoi -store 35 -txna ApplicationArgs 7 -btoi -store 36 -txna ApplicationArgs 8 -btoi -store 37 -txna ApplicationArgs 9 -btoi -store 38 -txna ApplicationArgs 10 -btoi -store 39 -txna ApplicationArgs 11 -btoi -store 40 -txna ApplicationArgs 12 -btoi -store 41 -txna ApplicationArgs 13 -btoi -store 42 -txna ApplicationArgs 14 -btoi -store 43 -txna ApplicationArgs 15 -store 46 -load 46 -int 0 -extract_uint64 -store 44 -load 46 -int 8 -extract_uint64 -store 45 -load 30 -load 31 -load 32 -load 33 -load 34 -load 35 -load 36 -load 37 -load 38 -load 39 -load 40 -load 41 -load 42 -load 43 -load 44 -load 45 -callsub alllaidtoargs_5 -store 47 -byte 0x151f7c75 -load 47 -itob -concat -log -int 1 -return -main_l14: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 24 -txna ApplicationArgs 2 -btoi -store 25 -load 24 -load 25 -callsub mod_4 -store 26 -byte 0x151f7c75 -load 26 -itob -concat -log -int 1 -return -main_l15: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 18 -txna ApplicationArgs 2 -btoi -store 19 -load 18 -load 19 -callsub div_3 -store 20 -byte 0x151f7c75 -load 20 -itob -concat -log -int 1 -return -main_l16: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 12 -txna ApplicationArgs 2 -btoi -store 13 -load 12 -load 13 -callsub mul_2 -store 14 -byte 0x151f7c75 -load 14 -itob -concat -log -int 1 -return -main_l17: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 6 -txna ApplicationArgs 2 -btoi -store 7 -load 6 -load 7 -callsub sub_1 -store 8 -byte 0x151f7c75 -load 8 -itob -concat -log -int 1 -return -main_l18: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -txna ApplicationArgs 1 -btoi -store 0 -txna ApplicationArgs 2 -btoi -store 1 -load 0 -load 1 -callsub add_0 -store 2 -byte 0x151f7c75 -load 2 -itob -concat -log -int 1 -return - -// add -add_0: -store 4 -store 3 -load 3 -load 4 -+ -store 5 -load 5 -retsub - -// sub -sub_1: -store 10 -store 9 -load 9 -load 10 -- -store 11 -load 11 -retsub - -// mul -mul_2: -store 16 -store 15 -load 15 -load 16 -* -store 17 -load 17 -retsub - -// div -div_3: -store 22 -store 21 -load 21 -load 22 -/ -store 23 -load 23 -retsub - -// mod -mod_4: -store 28 -store 27 -load 27 -load 28 -% -store 29 -load 29 -retsub - -// all_laid_to_args -alllaidtoargs_5: -store 63 -store 62 -store 61 -store 60 -store 59 -store 58 -store 57 -store 56 -store 55 -store 54 -store 53 -store 52 -store 51 -store 50 -store 49 -store 48 -load 48 -load 49 -+ -load 50 -+ -load 51 -+ -load 52 -+ -load 53 -+ -load 54 -+ -load 55 -+ -load 56 -+ -load 57 -+ -load 58 -+ -load 59 -+ -load 60 -+ -load 61 -+ -load 62 -+ -load 63 -+ -store 64 -load 64 -retsub - -// empty_return_subroutine -emptyreturnsubroutine_6: -byte "appear in both approval and clear state" -log -retsub - -// log_1 -log1_7: -int 1 -store 66 -load 66 -retsub - -// log_creation -logcreation_8: -byte 0x00106c6f6767696e67206372656174696f6e -store 68 -load 68 -retsub""".strip() - assert actual_ap_without_oc_compiled == expected_ap_without_oc - - expected_csp_without_oc = """#pragma version 6 -int 1 -return""".strip() - assert actual_csp_without_oc_compiled == expected_csp_without_oc - - _router_with_oc = pt.Router( - "QuestionableRouterGenerateCodeWithFramePointer", - on_completion_actions, - clear_state=pt.Approve(), - ) - add_methods_to_router(_router_with_oc) - ( - actual_ap_with_oc_compiled, - actual_csp_with_oc_compiled, - _, - ) = _router_with_oc.compile_program(version=8) - - expected_ap_with_oc = """#pragma version 8 -txn NumAppArgs -int 0 -== -bnz main_l20 -txna ApplicationArgs 0 -method "add(uint64,uint64)uint64" -== -bnz main_l19 -txna ApplicationArgs 0 -method "sub(uint64,uint64)uint64" -== -bnz main_l18 -txna ApplicationArgs 0 -method "mul(uint64,uint64)uint64" -== -bnz main_l17 -txna ApplicationArgs 0 -method "div(uint64,uint64)uint64" -== -bnz main_l16 -txna ApplicationArgs 0 -method "mod(uint64,uint64)uint64" -== -bnz main_l15 -txna ApplicationArgs 0 -method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" -== -bnz main_l14 -txna ApplicationArgs 0 -method "empty_return_subroutine()void" -== -bnz main_l13 -txna ApplicationArgs 0 -method "log_1()uint64" -== -bnz main_l12 -txna ApplicationArgs 0 -method "log_creation()string" -== -bnz main_l11 -err -main_l11: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -== -&& -assert -callsub logcreationcaster_17 -int 1 -return -main_l12: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -txn ApplicationID -int 0 -!= -&& -|| -assert -callsub log1caster_16 -int 1 -return -main_l13: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -|| -assert -callsub emptyreturnsubroutinecaster_15 -int 1 -return -main_l14: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub alllaidtoargscaster_14 -int 1 -return -main_l15: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub modcaster_13 -int 1 -return -main_l16: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub divcaster_12 -int 1 -return -main_l17: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub mulcaster_11 -int 1 -return -main_l18: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub subcaster_10 -int 1 -return -main_l19: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub addcaster_9 -int 1 -return -main_l20: -txn OnCompletion -int OptIn -== -bnz main_l22 -err -main_l22: -txn ApplicationID -int 0 -!= -assert -byte "optin call" -log -int 1 -return - -// add -add_0: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -+ -frame_bury 0 -retsub - -// sub -sub_1: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -- -frame_bury 0 -retsub - -// mul -mul_2: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -* -frame_bury 0 -retsub - -// div -div_3: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -/ -frame_bury 0 -retsub - -// mod -mod_4: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -% -frame_bury 0 -retsub - -// all_laid_to_args -alllaidtoargs_5: -proto 16 1 -int 0 -frame_dig -16 -frame_dig -15 -+ -frame_dig -14 -+ -frame_dig -13 -+ -frame_dig -12 -+ -frame_dig -11 -+ -frame_dig -10 -+ -frame_dig -9 -+ -frame_dig -8 -+ -frame_dig -7 -+ -frame_dig -6 -+ -frame_dig -5 -+ -frame_dig -4 -+ -frame_dig -3 -+ -frame_dig -2 -+ -frame_dig -1 -+ -frame_bury 0 -retsub - -// empty_return_subroutine -emptyreturnsubroutine_6: -proto 0 0 -byte "appear in both approval and clear state" -log -retsub - -// log_1 -log1_7: -proto 0 1 -int 0 -int 1 -frame_bury 0 -retsub - -// log_creation -logcreation_8: -proto 0 1 -byte "" -byte 0x00106c6f6767696e67206372656174696f6e -frame_bury 0 -retsub - -// add_caster -addcaster_9: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub add_0 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// sub_caster -subcaster_10: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub sub_1 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// mul_caster -mulcaster_11: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub mul_2 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// div_caster -divcaster_12: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub div_3 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// mod_caster -modcaster_13: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub mod_4 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// all_laid_to_args_caster -alllaidtoargscaster_14: -proto 0 0 -int 0 -dupn 16 -byte "" -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -txna ApplicationArgs 3 -btoi -frame_bury 3 -txna ApplicationArgs 4 -btoi -frame_bury 4 -txna ApplicationArgs 5 -btoi -frame_bury 5 -txna ApplicationArgs 6 -btoi -frame_bury 6 -txna ApplicationArgs 7 -btoi -frame_bury 7 -txna ApplicationArgs 8 -btoi -frame_bury 8 -txna ApplicationArgs 9 -btoi -frame_bury 9 -txna ApplicationArgs 10 -btoi -frame_bury 10 -txna ApplicationArgs 11 -btoi -frame_bury 11 -txna ApplicationArgs 12 -btoi -frame_bury 12 -txna ApplicationArgs 13 -btoi -frame_bury 13 -txna ApplicationArgs 14 -btoi -frame_bury 14 -txna ApplicationArgs 15 -frame_bury 17 -frame_dig 17 -int 0 -extract_uint64 -frame_bury 15 -frame_dig 17 -int 8 -extract_uint64 -frame_bury 16 -frame_dig 1 -frame_dig 2 -frame_dig 3 -frame_dig 4 -frame_dig 5 -frame_dig 6 -frame_dig 7 -frame_dig 8 -frame_dig 9 -frame_dig 10 -frame_dig 11 -frame_dig 12 -frame_dig 13 -frame_dig 14 -frame_dig 15 -frame_dig 16 -callsub alllaidtoargs_5 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// empty_return_subroutine_caster -emptyreturnsubroutinecaster_15: -proto 0 0 -callsub emptyreturnsubroutine_6 -retsub - -// log_1_caster -log1caster_16: -proto 0 0 -int 0 -callsub log1_7 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// log_creation_caster -logcreationcaster_17: -proto 0 0 -byte "" -callsub logcreation_8 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -concat -log -retsub""".strip() - assert actual_ap_with_oc_compiled == expected_ap_with_oc + router_compilarison( + _router_without_oc, 6, "yacc_approval_v6.teal", "yacc_clear_v6.teal", "yacc" + ) - expected_csp_with_oc = """#pragma version 8 -int 1 -return""".strip() - assert actual_csp_with_oc_compiled == expected_csp_with_oc + # QUESTIONABLE FP V8: + _router_with_oc = pt.Router( + "QuestionableRouterGenerateCodeWithFramePointer", + ON_COMPLETION_ACTIONS, + clear_state=pt.Approve(), + ) + add_methods_to_router(_router_with_oc) + router_compilarison( + _router_with_oc, + 8, + "questionableFP_approval_v8.teal", + "questionableFP_clear_v8.teal", + "questionable", + ) + # YACC FP V8: _router_without_oc = pt.Router( "yetAnotherContractConstructedFromRouterWithFramePointer", clear_state=pt.Approve(), ) add_methods_to_router(_router_without_oc) - ( - actual_ap_without_oc_compiled, - actual_csp_without_oc_compiled, - _, - ) = _router_without_oc.compile_program(version=8) - - expected_ap_without_oc = """#pragma version 8 -txna ApplicationArgs 0 -method "add(uint64,uint64)uint64" -== -bnz main_l18 -txna ApplicationArgs 0 -method "sub(uint64,uint64)uint64" -== -bnz main_l17 -txna ApplicationArgs 0 -method "mul(uint64,uint64)uint64" -== -bnz main_l16 -txna ApplicationArgs 0 -method "div(uint64,uint64)uint64" -== -bnz main_l15 -txna ApplicationArgs 0 -method "mod(uint64,uint64)uint64" -== -bnz main_l14 -txna ApplicationArgs 0 -method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" -== -bnz main_l13 -txna ApplicationArgs 0 -method "empty_return_subroutine()void" -== -bnz main_l12 -txna ApplicationArgs 0 -method "log_1()uint64" -== -bnz main_l11 -txna ApplicationArgs 0 -method "log_creation()string" -== -bnz main_l10 -err -main_l10: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -== -&& -assert -callsub logcreationcaster_17 -int 1 -return -main_l11: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -txn ApplicationID -int 0 -!= -&& -|| -assert -callsub log1caster_16 -int 1 -return -main_l12: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -txn OnCompletion -int OptIn -== -|| -assert -callsub emptyreturnsubroutinecaster_15 -int 1 -return -main_l13: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub alllaidtoargscaster_14 -int 1 -return -main_l14: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub modcaster_13 -int 1 -return -main_l15: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub divcaster_12 -int 1 -return -main_l16: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub mulcaster_11 -int 1 -return -main_l17: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub subcaster_10 -int 1 -return -main_l18: -txn OnCompletion -int NoOp -== -txn ApplicationID -int 0 -!= -&& -assert -callsub addcaster_9 -int 1 -return - -// add -add_0: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -+ -frame_bury 0 -retsub - -// sub -sub_1: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -- -frame_bury 0 -retsub - -// mul -mul_2: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -* -frame_bury 0 -retsub - -// div -div_3: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -/ -frame_bury 0 -retsub - -// mod -mod_4: -proto 2 1 -int 0 -frame_dig -2 -frame_dig -1 -% -frame_bury 0 -retsub - -// all_laid_to_args -alllaidtoargs_5: -proto 16 1 -int 0 -frame_dig -16 -frame_dig -15 -+ -frame_dig -14 -+ -frame_dig -13 -+ -frame_dig -12 -+ -frame_dig -11 -+ -frame_dig -10 -+ -frame_dig -9 -+ -frame_dig -8 -+ -frame_dig -7 -+ -frame_dig -6 -+ -frame_dig -5 -+ -frame_dig -4 -+ -frame_dig -3 -+ -frame_dig -2 -+ -frame_dig -1 -+ -frame_bury 0 -retsub - -// empty_return_subroutine -emptyreturnsubroutine_6: -proto 0 0 -byte "appear in both approval and clear state" -log -retsub - -// log_1 -log1_7: -proto 0 1 -int 0 -int 1 -frame_bury 0 -retsub - -// log_creation -logcreation_8: -proto 0 1 -byte "" -byte 0x00106c6f6767696e67206372656174696f6e -frame_bury 0 -retsub - -// add_caster -addcaster_9: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub add_0 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// sub_caster -subcaster_10: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub sub_1 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// mul_caster -mulcaster_11: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub mul_2 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// div_caster -divcaster_12: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub div_3 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// mod_caster -modcaster_13: -proto 0 0 -int 0 -dupn 2 -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -frame_dig 1 -frame_dig 2 -callsub mod_4 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub - -// all_laid_to_args_caster -alllaidtoargscaster_14: -proto 0 0 -int 0 -dupn 16 -byte "" -txna ApplicationArgs 1 -btoi -frame_bury 1 -txna ApplicationArgs 2 -btoi -frame_bury 2 -txna ApplicationArgs 3 -btoi -frame_bury 3 -txna ApplicationArgs 4 -btoi -frame_bury 4 -txna ApplicationArgs 5 -btoi -frame_bury 5 -txna ApplicationArgs 6 -btoi -frame_bury 6 -txna ApplicationArgs 7 -btoi -frame_bury 7 -txna ApplicationArgs 8 -btoi -frame_bury 8 -txna ApplicationArgs 9 -btoi -frame_bury 9 -txna ApplicationArgs 10 -btoi -frame_bury 10 -txna ApplicationArgs 11 -btoi -frame_bury 11 -txna ApplicationArgs 12 -btoi -frame_bury 12 -txna ApplicationArgs 13 -btoi -frame_bury 13 -txna ApplicationArgs 14 -btoi -frame_bury 14 -txna ApplicationArgs 15 -frame_bury 17 -frame_dig 17 -int 0 -extract_uint64 -frame_bury 15 -frame_dig 17 -int 8 -extract_uint64 -frame_bury 16 -frame_dig 1 -frame_dig 2 -frame_dig 3 -frame_dig 4 -frame_dig 5 -frame_dig 6 -frame_dig 7 -frame_dig 8 -frame_dig 9 -frame_dig 10 -frame_dig 11 -frame_dig 12 -frame_dig 13 -frame_dig 14 -frame_dig 15 -frame_dig 16 -callsub alllaidtoargs_5 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub + router_compilarison( + _router_without_oc, + 8, + "yaccFP_approval_v8.teal", + "yaccFP_clear_v8.teal", + "yacc", + ) -// empty_return_subroutine_caster -emptyreturnsubroutinecaster_15: -proto 0 0 -callsub emptyreturnsubroutine_6 -retsub + non_trivial = pt.Seq( + pt.Cond( + [pt.Txn.application_args.length() < pt.Int(2), pt.Approve()], + [pt.Txn.application_args[0] != pt.Bytes("CLEANUP"), pt.Approve()], + [pt.Txn.application_args[1] != pt.Bytes("ABORTING"), pt.Approve()], + [pt.Int(1), pt.Reject()], + ), + ) + _router_with_nontriv_clear = pt.Router( + "QuestionableRouterWithNontrivialClear", + bare_calls=ON_COMPLETION_ACTIONS, + clear_state=non_trivial, + ) + add_methods_to_router(_router_with_nontriv_clear) + + router_compilarison( + _router_with_nontriv_clear, + 6, + "nontriv_clear_approval_v6.teal", + "nontriv_clear_clear_v6.teal", + "nontriv_clear", + ) -// log_1_caster -log1caster_16: -proto 0 0 -int 0 -callsub log1_7 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -itob -concat -log -retsub + router_compilarison( + _router_with_nontriv_clear, + 8, + "nontriv_clear_approval_v8.teal", + "nontriv_clear_clear_v8.teal", + "nontriv_clear", + ) -// log_creation_caster -logcreationcaster_17: -proto 0 0 -byte "" -callsub logcreation_8 -frame_bury 0 -byte 0x151f7c75 -frame_dig 0 -concat -log -retsub""".strip() - assert actual_ap_without_oc_compiled == expected_ap_without_oc + return routers, sources - expected_csp_without_oc = """#pragma version 8 -int 1 -return""".strip() - assert actual_csp_without_oc_compiled == expected_csp_without_oc +def test_router_app(): + # TODO: this test is redundant as router_app_tester is imported and run by + # tests/integration/abi_router_test.py's setup + router_app_tester() diff --git a/requirements.txt b/requirements.txt index ba07a0959..5811b9870 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ black==22.3.0 flake8==5.0.4 flake8-tidy-imports==4.6.0 -graviton@git+https://github.com/algorand/graviton@v0.8.0 +graviton@git+https://github.com/algorand/graviton@v0.9.0 mypy==0.991 pytest==7.2.0 pytest-cov==3.0.0 diff --git a/tests/blackbox.py b/tests/blackbox.py index 00e85e9ef..0b0aafc9d 100644 --- a/tests/blackbox.py +++ b/tests/blackbox.py @@ -1,16 +1,30 @@ -from typing import Any, Callable, Sequence, cast +from collections import defaultdict +from copy import deepcopy +from dataclasses import dataclass, asdict +import json +from typing import Any, Callable, Final, Literal, Sequence, Type, cast -import algosdk.abi -from algosdk.v2client import algod +import algosdk.abi as sdk_abi +from algosdk.transaction import OnComplete +from algosdk import v2client from graviton import blackbox +from graviton.abi_strategy import ( + ABIArgsMod, + ABICallStrategy, + ABIStrategy, + RandomArgLengthCallStrategy, +) from graviton.blackbox import ( DryRunInspector, DryRunExecutor, DryRunTransactionParams as TxParams, ) -from graviton.models import PyTypes +from graviton.inspector import DryRunProperty as DRProp +from graviton.models import ExecutionMode, PyTypes +from graviton.sim import InputStrategy, Simulation, SimulationResults +from pyteal.compiler.compiler import OptimizeOptions from pyteal.ast.subroutine import OutputKwArgInfo from pyteal import ( @@ -18,14 +32,17 @@ Arg, Btoi, Bytes, + CallConfig, compileTeal, Expr, Int, Itob, Len, Log, + MethodConfig, Mode, Pop, + Router, ScratchVar, Seq, SubroutineFnWrapper, @@ -35,6 +52,26 @@ from pyteal.ast.subroutine import ABIReturnSubroutine +# ---- Types ---- # + +Predicates = dict[DRProp, Any] # same as in graviton + + +CLEAR_STATE_CALL: Final[str] = "ClearStateCall" +ClearStateCallType = Literal["ClearStateCall"] + +# str for methods, None for bare app calls: +CallType = str | None + +# CallType for app calls, CLEAR_STATE_CALL for clear state calls: +RouterCallType = CallType | ClearStateCallType # type: ignore + +ABICallConfigs = dict[CallType, MethodConfig] + +# key `method_config == None` indicates that the MethodConfig's +# should be picked up from the router +CallPredicates = dict[RouterCallType, Predicates] + # ---- Clients ---- # @@ -46,9 +83,9 @@ def algod_with_assertion(): def _algod_client( algod_address="http://localhost:4001", algod_token="a" * 64 -) -> algod.AlgodClient: +) -> v2client.algod.AlgodClient: """Instantiate and return Algod client object.""" - return algod.AlgodClient(algod_token, algod_address) + return v2client.algod.AlgodClient(algod_token, algod_address) # ---- Decorator ---- # @@ -205,7 +242,7 @@ def abi_method_signature(self) -> None | str: return None - def abi_argument_types(self) -> None | list[algosdk.abi.ABIType]: + def abi_argument_types(self) -> None | list[sdk_abi.ABIType]: if not (self.input_types or self.is_abi()): return None @@ -216,7 +253,7 @@ def handle_arg(arg): return [handle_arg(arg) for arg in self.input_types] - def abi_return_type(self) -> None | algosdk.abi.ABIType: + def abi_return_type(self) -> None | sdk_abi.ABIType: if not self.is_abi(): return None @@ -440,3 +477,440 @@ def dryrun_one( return self.executor(compiler_version).run_one( args, txn_params=txn_params, verbose=verbose ) + + +def as_on_complete(oc_str: str) -> OnComplete: + match oc_str: + case "no_op": + return OnComplete.NoOpOC + case "opt_in": + return OnComplete.OptInOC + case "close_out": + return OnComplete.CloseOutOC + case "clear_state": + return OnComplete.ClearStateOC + case "update_application": + return OnComplete.UpdateApplicationOC + case "delete_application": + return OnComplete.DeleteApplicationOC + + raise ValueError(f"unrecognized {oc_str=}") + + +def negate_cc(cc: CallConfig) -> CallConfig: + return CallConfig(3 - cc) + + +@dataclass(frozen=True) +class RouterSimulationResults: + stats: dict[str, Any] + results: dict + approval_simulator: Simulation | None + clear_simulator: Simulation | None + + +class RouterSimulation: + """ + Lifecycle of a RouterSimulation + + 1. Creation (__init__ method): + * router: Router (no version or other options specified) + * predicates: CallPredicates - the Router ought satisfy. Type has shape: + * method --> predicate ...> ... + * model_router: Router (optional) - in the case when the predicates provided + are of type PredicateKind.IdenticalPair, this parameter needs to be + provided for comparison. + NOTE: model_router may in fact be the same as router, and in this case + it is expected that something else such as version or optimization option + would differ between model_router and router during the simulation + * algod (optional) - if missing, just get one + + Artifacts from Step 1 are stored in self.results: _SimConfig + + 2. Simulation (simulate_and_assert method): - using self.results artifacts Step 1, also takes params: + * approval_arg_strat_type: Type[ABICallStrategy] + - strategy type to use for approval program's arg generation + * clear_arg_strat_type_or_inputs: Type[ABICallStrategy] | Iterable[Sequence[PyTypes]] | None + - strategy type to use for clear program's arg generation + * approval_abi_args_mod: ABIArgsMod (default None) + - used to specify any arg mutation + # TODO: currently there aren't any clear_abi_args_mod, but we might need these for testing non-trivial clear programs + * version: int - for compiling self.router + * method_configs: ABICallConfigs - these drive all the test cases + * assemble_constants: bool (optional) - for compiling self.router + * optimize: OptimizeOptions (optional) - for compiling self.router + * num_dryruns: int (default 1) + - the number of input runs to generate per method X config combination + * txn_params: TxParams (optional) + - other TxParams to append in addition to the (is_app_create, OnComplete) information + * model_version: int - for compiling self.model_router + * model_assemble_constants: bool (optional) - for compiling self.model_router + * model_optimize: OptimizeOptions (optional) - for compiling self.model_router + * msg: string (optional) - message to report when an assertion is violated + * omit_approval_call: bool (default False) - allow purely testing the clear program + * omit_clear_call: bool (default False) - allow purely testing the approval program + NOTE: one of omit_approval_call or omit_clear_call must remain False + * executor_validation (default True) - when False, skip the DryRunExecutor's validation + * skip_validation (default False) - when False, skip the Router's validation + """ + + def __init__( + self, + router: Router, + predicates: CallPredicates, + *, + model_router: Router | None = None, + algod: v2client.algod.AlgodClient | None = None, + ): + self.router: Router = self._validate_router(router) + + self.predicates: CallPredicates = self._validate_predicates(predicates) + + self.model_router: Router | None = None + if model_router: + self.model_router = self._validate_router(model_router, kind="Model") + + self.algod: v2client.algod.AlgodClient = algod or algod_with_assertion() + + self.results: dict[ + str | None, dict[tuple[bool, OnComplete], SimulationResults] + ] = {} + + # ---- Validation ---- # + + @classmethod + def _validate_router(cls, router: Router, kind: str = "Base") -> Router: + assert isinstance( + router, Router + ), f"Wrong type for {kind} Router: {type(router)}" + cls._validate_method_configs(router.method_configs) + + return router + + @classmethod + def _validate_method_configs(cls, method_configs): + assert isinstance( + method_configs, dict + ), f"method_configs '{method_configs}' has type {type(method_configs)} but only 'dict' and 'NoneType' are allowed." + + assert ( + method_configs + ), "make sure to give at least one key/value pair in method_configs" + + for call, meth_config in method_configs.items(): + assert isinstance( # type: ignore + call, CallType + ), f"method_configs dict key '{call}' has type {type(call)} but only str and NoneType are allowed." + cls._validate_single_method_config(call, meth_config) + + @classmethod + def _validate_single_method_config(cls, call, meth_config): + assert isinstance( + meth_config, MethodConfig + ), f"method_configs['{call}'] = has type {type(meth_config)} but only MethodConfig is allowed." + assert ( + not meth_config.is_never() + ), f"method_configs['{call}'] specifies NEVER to be called; for driving the test, each configured method should ACTUALLY be tested." + assert ( + meth_config.clear_state is CallConfig.NEVER + ), "unexpected value for method_config's clear_state" + + @classmethod + def _validate_predicates(cls, predicates): + assert isinstance(predicates, dict), ( + f"Wrong type for predicates: {type(predicates)}. Please provide: " + f"dict[str | None, dict[graviton.DryRunProporty, Any]." + ) + + assert ( + len(predicates) > 0 + ), "Please provide at least one method to call and assert against." + + for method, preds in predicates.items(): + assert isinstance(method, (str, type(None), type(ClearStateCallType))), ( + f"Predicates method '{method}' has type {type(method)} but only " + "'str' and 'NoneType' and Literal['ClearStateCall'] (== ClearStateCall)" + " are allowed." + ) + if isinstance(method, type(ClearStateCallType)): + assert method == ClearStateCallType, ( + f"Predicates method '{method}' is not allowed. " + "Only Literal['ClearStateCall'] (== ClearStateCall) " + "is allowed for a Literal." + ) + assert ( + preds + ), f"Every method must provide at least one predicate for assertion but method '{method}' is missing predicates." + assert isinstance( + preds, dict + ), f"Method '{method}' is expected to have dict[graviton.DryRunProperty, Any] for its predicates value but the type is {type(preds)}." + for prop in preds: + assert isinstance( + prop, DRProp + ), f"Method '{method}' is expected to have dict[graviton.DryRunProperty, Any] for its predicates value but predicates['{method}'] has key '{prop}' of {type(prop)}." + + return predicates + + def _validate_simulation( + self, + approval_args_strat_type, + clear_args_strat_type, + approval_abi_args_mod, + num_dryruns, + txn_params, + model_version, + method_configs, + contract, + model_contract, + omit_clear_call, + ): + assert isinstance(approval_args_strat_type, type) and issubclass( + approval_args_strat_type, ABIStrategy + ), f"approval_args_strat_type should _BE_ a subtype of ABIStrategy but we have {approval_args_strat_type} (its type is {type(approval_args_strat_type)})." + if not omit_clear_call: + assert isinstance(clear_args_strat_type, type) and issubclass( + clear_args_strat_type, ABIStrategy + ), f"clear_args_strat_type should _BE_ a subtype of ABIStrategy but we have {clear_args_strat_type} (its type is {type(clear_args_strat_type)})." + + assert isinstance( + approval_abi_args_mod, (ABIArgsMod, type(None)) + ), f"approval_abi_args_mod '{approval_abi_args_mod}' has type {type(approval_abi_args_mod)} but only 'ABIArgsMod' and 'NoneType' are allowed." + + self._validate_method_configs(method_configs) + + self._validate_meths_in_contract(method_configs, contract) + + if model_contract: + self._validate_meths_in_contract( + method_configs, model_contract, router_prefix="model" + ) + + assert ( + isinstance(num_dryruns, int) and num_dryruns >= 1 + ), f"num_dryruns must be a positive int but is {num_dryruns}." + + assert isinstance( + txn_params, (TxParams, type(None)) + ), f"txn_params must have type DryRunTransactionParams or NoneType but has type {type(txn_params)}." + + if not self.model_router: + assert ( + model_version is None + ), f"model_version '{model_version}' was provided which is nonsensical because model_router was never provided for." + + def _validate_meths_in_contract( + self, method_configs, contract, router_prefix="base" + ): + for meth in method_configs: + if meth is None: + continue + try: + contract.get_method_by_name(meth) + except KeyError: + raise ValueError( + f"method_configs has a method '{meth}' missing from {router_prefix}-Router's contract." + ) + + def simulate_and_assert( + self, + approval_args_strat_type: Type[ABIStrategy], + clear_args_strat_type_or_inputs: Type[ABIStrategy] + | list[Sequence[PyTypes]] + | None, + approval_abi_args_mod: ABIArgsMod | None, + version: int, + method_configs: ABICallConfigs, + *, + assemble_constants: bool = False, + optimize: OptimizeOptions | None = None, + num_dryruns: int = 1, + txn_params: TxParams | None = None, + model_version: int | None = None, + model_assemble_constants: bool = False, + model_optimize: OptimizeOptions | None = None, + msg: str = "", + omit_approval_call: bool = False, + omit_clear_call: bool = False, + executor_validation: bool = True, + skip_validation: bool = False, + ) -> RouterSimulationResults: + assert not ( + omit_approval_call and omit_clear_call + ), "Aborting and failing as all tests are being omitted" + + # --- setup local functions including reporter and stats. Also declare closure vars --- # + + # for purposes of clarity, declare all the variables for closures before each function: + approve_sim: Simulation | None # required for return RouterResults + + # msg4simulate: + + # msg - cf. parameters + approval_strat: ABICallStrategy | None + meth_name: str | None # simulate_approval's closure as well + call_cfg: CallConfig | None + is_app_create: bool + stats: dict[str, int | str] = defaultdict(int) + + def msg4simulate() -> str: + return f"""user provide message={msg} +call_strat={type(approval_strat)} +{meth_name=} +{oc=} +{call_cfg=} +{is_app_create=} +{len(self.predicates[meth_name])=} +{stats["method_combo_count"]=} +{stats["dryrun_count"]=} +{stats["assertions_count"]=} +""" + + # update_stats: + # num_dryruns - cf. parameters + # stats - cf. above + def update_stats(meth, num_preds): + stats[str(meth)] += num_dryruns + stats["method_combo_count"] += 1 + stats["dryrun_count"] += num_dryruns + stats["assertions_count"] += num_dryruns * num_preds + + # simulate_approval: + # txn_params - cf. parameters + oc: OnComplete + # approval_strat - cf. above + + def simulate_approval(on_create): + tp: TxParams = deepcopy(txn_params) + tp.update_fields(TxParams.for_app(is_app_create=on_create, on_complete=oc)) + sim_results = approve_sim.run_and_assert( + approval_strat, txn_params=tp, msg=msg4simulate() + ) + assert sim_results.succeeded + if meth_name not in self.results: + self.results[meth_name] = {} + self.results[meth_name][(on_create, oc)] = sim_results + update_stats(meth_name, len(self.predicates[meth_name])) + + # --- Compile Programs --- # + approval_teal, clear_teal, contract = self.router.compile_program( + version=version, assemble_constants=assemble_constants, optimize=optimize + ) + + model_approval_teal: str | None = None + model_clear_teal: str | None = None + model_contract: sdk_abi.Contract | None = None + if self.model_router: + ( + model_approval_teal, + model_clear_teal, + model_contract, + ) = self.model_router.compile_program( + version=cast(int, model_version), + assemble_constants=model_assemble_constants, + optimize=model_optimize, + ) + + if not skip_validation: + self._validate_simulation( + approval_args_strat_type, + clear_args_strat_type_or_inputs, + approval_abi_args_mod, + num_dryruns, + txn_params, + model_version, + method_configs, + contract, + model_contract, + omit_clear_call, + ) + + if not txn_params: + txn_params = TxParams() + + stats["name"] = self.router.name + + # ---- APPROVAL PROGRAM SIMULATION ---- # + if not omit_approval_call: + approval_strat = ABICallStrategy( + json.dumps(contract.dictify()), + approval_args_strat_type, + num_dryruns=num_dryruns, + abi_args_mod=approval_abi_args_mod, + ) + double_check_at_least_one_method = False + for meth_name, meth_cfg in method_configs.items(): + sig = approval_strat.method_signature(meth_name) + approve_sim = Simulation( + self.algod, + ExecutionMode.Application, + approval_teal, + self.predicates[meth_name], + abi_method_signature=sig, + identities_teal=model_approval_teal, + validation=executor_validation, + ) + + for oc_str, call_cfg in asdict(meth_cfg).items(): + oc = as_on_complete(oc_str) + + # weird walrus is_app_create := ... to fill closure of msg4simulate() + if cast(CallConfig, call_cfg) & CallConfig.CALL: + double_check_at_least_one_method = True + simulate_approval(is_app_create := False) + + if cast(CallConfig, call_cfg) & CallConfig.CREATE: + double_check_at_least_one_method = True + simulate_approval(is_app_create := True) + assert double_check_at_least_one_method, "no method was simulated" + + # ---- CLEAR PROGRAM SIMULATION ---- # + approval_strat = None + call_cfg = None + approve_sim = None + clear_strat_or_inputs: InputStrategy # CallStrategy | Iterable[Sequence[PyTypes]] + clear_sim: Simulation | None = None + if not omit_clear_call: + assert clear_args_strat_type_or_inputs # therefore Type[ABIStrategy] | list[Sequence[PyTypes]] + if isinstance(clear_args_strat_type_or_inputs, list): + clear_strat_or_inputs = cast( + list[Sequence[PyTypes]], clear_args_strat_type_or_inputs + ) + # for the closure of local update_stats(): + num_dryruns = len(clear_strat_or_inputs) + else: + clear_strat_or_inputs = RandomArgLengthCallStrategy( + cast(Type[ABIStrategy], clear_args_strat_type_or_inputs), + max_args=2, + num_dryruns=num_dryruns, + min_args=0, + type_for_args=sdk_abi.ABIType.from_string("byte[8]"), + ) + + meth_name = CLEAR_STATE_CALL + is_app_create = False + oc = OnComplete.ClearStateOC + clear_sim = Simulation( + self.algod, + ExecutionMode.Application, + clear_teal, + self.predicates[meth_name], + identities_teal=model_clear_teal, + validation=executor_validation, + ) + + sim_results = clear_sim.run_and_assert( + clear_strat_or_inputs, msg=msg4simulate() + ) + assert sim_results.succeeded + if meth_name not in self.results: + self.results[meth_name] = {} + self.results[meth_name][(is_app_create, oc)] = sim_results + update_stats(meth_name, len(self.predicates[meth_name])) + + # ---- Summary Statistics ---- # + return RouterSimulationResults( + stats=stats, + results=self.results, + approval_simulator=approve_sim, + clear_simulator=clear_sim, + ) diff --git a/tests/integration/abi_router_test.py b/tests/integration/abi_router_test.py new file mode 100644 index 000000000..8f53db916 --- /dev/null +++ b/tests/integration/abi_router_test.py @@ -0,0 +1,540 @@ +import json +import re +from collections import defaultdict +from dataclasses import asdict +from pathlib import Path + +import pytest +from graviton.abi_strategy import ( + ABIArgsMod, + RandomABIStrategy, + RandomABIStrategyHalfSized, +) +from graviton.blackbox import DryRunEncoder +from graviton.invariant import DryRunProperty as DRProp +from graviton.invariant import PredicateKind + +import pyteal as pt +from pyteal.compiler.compiler_test import router_app_tester +from tests.blackbox import ( + CLEAR_STATE_CALL, + ABICallConfigs, + Predicates, + RouterCallType, + RouterSimulation, + negate_cc, +) + +NUM_ROUTER_DRYRUNS = 7 +FIXTURES = Path.cwd() / "tests" / "teal" / "router" + + +ALL_ROUTER_CASES, ROUTER_SOURCES = router_app_tester() + +ROUTER_CASES, NONTRIV_CLEAR_ROUTER_CASES = ALL_ROUTER_CASES[:-2], ALL_ROUTER_CASES[-2:] + +TYPICAL_IAC_OC = pt.MethodConfig(no_op=pt.CallConfig.CALL) + +# TEST DRIVERS LEGEND - combines method_configs + predicates +# * @0 - method: RouterCallType +# method == None indicates bare app call +# method == CLEAR_STATE_CALL indicates clear state app call +# +# * @1 - method_config: MethodConfig +# defines how to call the method +# +# * @2 - predicates: Predicates ~ dict[DRProp, Any] +# these are being asserted after being processed into Invariant's +# +# NOTE: the "yacc" routers will simply ignore the case with method `None` +# as they do not have any bare-app-calls +QUESTIONABLE_DRIVER: list[tuple[RouterCallType, pt.MethodConfig, Predicates]] = [ + ( + "add", + TYPICAL_IAC_OC, + {DRProp.passed: True, DRProp.lastLog: lambda args: args[1] + args[2]}, + ), + ( + "sub", + TYPICAL_IAC_OC, + { + DRProp.passed: lambda args: args[1] >= args[2], + DRProp.lastLog: ( + lambda args, actual: True + if args[1] < args[2] + else actual == args[1] - args[2] + ), + }, + ), + ( + "mul", + TYPICAL_IAC_OC, + {DRProp.passed: True, DRProp.lastLog: lambda args: args[1] * args[2]}, + ), + ( + "div", + TYPICAL_IAC_OC, + {DRProp.passed: True, DRProp.lastLog: lambda args: args[1] // args[2]}, + ), + ( + "mod", + TYPICAL_IAC_OC, + {DRProp.passed: True, DRProp.lastLog: lambda args: args[1] % args[2]}, + ), + ( + "all_laid_to_args", + TYPICAL_IAC_OC, + {DRProp.passed: True, DRProp.lastLog: lambda args: sum(args[1:])}, + ), + ( + "empty_return_subroutine", + pt.MethodConfig( + no_op=pt.CallConfig.CALL, + opt_in=pt.CallConfig.ALL, + ), + { + DRProp.passed: True, + DRProp.lastLog: DryRunEncoder.hex( + "appear in both approval and clear state" + ), + }, + ), + ( + "log_1", + pt.MethodConfig( + no_op=pt.CallConfig.CALL, + opt_in=pt.CallConfig.CALL, + # clear_state=pt.CallConfig.CALL, + ), + {DRProp.passed: True, DRProp.lastLog: 1}, + ), + ( + "log_creation", + pt.MethodConfig(no_op=pt.CallConfig.CREATE), + {DRProp.passed: True, DRProp.lastLog: "logging creation"}, + ), + ( + None, + pt.MethodConfig( + opt_in=pt.CallConfig.CALL, + ), + { + DRProp.passed: True, + DRProp.lastLog: lambda _, actual: actual + in (None, DryRunEncoder.hex("optin call")), + }, + ), + ( + CLEAR_STATE_CALL, + pt.MethodConfig(), # ignored in this case + { + DRProp.passed: True, + DRProp.cost: 2, + }, + ), +] + +YACC_DRIVER = [case for case in QUESTIONABLE_DRIVER if case[0]] + +DRIVERS = { + "questionable": QUESTIONABLE_DRIVER, + "yacc": YACC_DRIVER, + "nontriv_clear": QUESTIONABLE_DRIVER, +} + + +def split_driver2predicates_methconfigs(driver) -> tuple[Predicates, ABICallConfigs]: + predicates = {} + methconfigs = {} + for meth, meth_config, predicate in driver: + predicates[meth] = predicate + if meth != CLEAR_STATE_CALL: + methconfigs[meth] = meth_config + + return predicates, methconfigs + + +def assert_full_method_coverage(router, methconfigs): + assert len(methconfigs) == len(rmc := router.method_configs) + for meth_sig, meth_config in rmc.items(): + k = meth_sig + if k: + k = meth_sig.split("(")[0] + assert k in methconfigs, f"{k=} (derived from {meth_sig=} not in methconfigs" + assert meth_config == methconfigs[k] + + +@pytest.mark.parametrize("case, version, router", ROUTER_CASES) +def test_abi_router_positive(case, version, router): + """ + Test the _positive_ version of a case. In other words, ensure that for each + router encountered and its driver, iterate through the driver as follows: + * consider each method or bare call + * consider each (OnComplete, CallConfig) combination + * assert that all predicates hold for this call + """ + driver = DRIVERS[case] + predicates, methconfigs = split_driver2predicates_methconfigs(driver) + + assert_full_method_coverage(router, methconfigs) + + rsim = RouterSimulation(router, predicates) + + def msg(): + return f"""test_abi_router_positive() +{case=} +{version=} +{router.name=}""" + + results = rsim.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=RandomABIStrategy, + approval_abi_args_mod=None, + version=version, + method_configs=methconfigs, + num_dryruns=NUM_ROUTER_DRYRUNS, + msg=msg(), + ) + # won't even get here if there was an error, but some extra sanity checks: + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + + print("\nstats:", json.dumps(stats := results.stats, indent=2)) + assert stats and all(stats.values()) + + # TODO: add these assertions after the flakiness of issue #199 is fixed for good + # These fail because of differing scratch slot assignments: + # pregen_approval, pregen_clear = ROUTER_SOURCES[(case, version)] + # assert pregen_clear == results.clear_simulator.simulate_dre.program + # assert pregen_approval == results.approval_simulator.simulate_dre.program + + +# cf. https://death.andgravity.com/f-re for an explanation of verbose regex'es +EXPECTED_ERR_PATTERN = r""" + err\ opcode # pyteal generated err's ok +| assert\ failed\ pc= # pyteal generated assert's ok +| invalid\ ApplicationArgs\ index # failing because an app arg wasn't provided +| extraction\ end\ 16\ is\ beyond\ length # failing because couldn't extract when omitted final arg or jammed in tuple +""" + + +APPROVAL_NEGATIVE_PREDS = { + DRProp.rejected: True, + DRProp.error: True, + DRProp.errorMessage: lambda _, actual: ( + bool(re.search(EXPECTED_ERR_PATTERN, actual, re.VERBOSE)) + ), +} + +CLEAR_NEGATIVE_INVARIANTS_MUST_APPROVE = [ + inv for m, _, inv in QUESTIONABLE_DRIVER if m == CLEAR_STATE_CALL +][0] + + +def scenario_assert_stats(scenario, results, totals): + part_a = f""" +SCENARIO: {scenario} +""" + if results: + part_b = json.dumps(stats := results.stats, indent=2) + assert stats and all(stats.values()) + for k, v in stats.items(): + if isinstance(v, int): + totals[k] += v + else: + part_b = "SKIPPED" + print(f"{part_a}stats:", part_b) + + +@pytest.mark.parametrize("case, version, router", ROUTER_CASES) +def test_abi_router_negative(case, version, router): + totals = defaultdict(int) + + contract = router.contract_construct() + + driver = DRIVERS[case] + pos_predicates, pos_mconfigs = split_driver2predicates_methconfigs(driver) + + # assert FULL coverage (before modifying the dict): + assert_full_method_coverage(router, pos_mconfigs) + + if None not in pos_mconfigs: + pos_mconfigs[None] = pt.MethodConfig() + pos_predicates[None] = APPROVAL_NEGATIVE_PREDS + + pure_meth_mconfigs = { + meth: methconfig + for meth, methconfig in pos_mconfigs.items() + if meth is not None + } + + neg_predicates = { + meth: ( + APPROVAL_NEGATIVE_PREDS + if meth != CLEAR_STATE_CALL + else CLEAR_NEGATIVE_INVARIANTS_MUST_APPROVE + ) + for meth in pos_predicates + } + + rsim = RouterSimulation(router, neg_predicates) + + def msg(): + return f"""test_abi_router_negative() +{scenario=} +{case=} +{version=} +{router.name=}""" + + scenario = "I. explore all UNEXPECTED (is_app_create, on_complete) combos" + + # NOTE: We're NOT including clear_state calls for the approval program + # as they would never be applied. + # Also, we're ONLY including clear_state for the clear program. + neg_mconfigs = { + meth: pt.MethodConfig( + **{k: negate_cc(v) for k, v in asdict(mc).items() if k != "clear_state"} + ) + for meth, mc in pos_mconfigs.items() + } + + results = rsim.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=RandomABIStrategy, + approval_abi_args_mod=None, + version=version, + method_configs=neg_mconfigs, + num_dryruns=NUM_ROUTER_DRYRUNS, + executor_validation=False, + msg=msg(), + ) + # won't even get here if there was an error, but some extra sanity checks: + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(scenario, results, totals) + + # II. the case of bare-app-calls + scenario = "II. adding an argument to a bare app call" + if None in pos_mconfigs and not pos_mconfigs[None].is_never(): + bare_only_methconfigs = {None: pos_mconfigs[None]} + results = rsim.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=None, + approval_abi_args_mod=ABIArgsMod.parameter_append, + version=version, + method_configs=bare_only_methconfigs, + omit_clear_call=True, + num_dryruns=NUM_ROUTER_DRYRUNS, + executor_validation=False, + msg=msg(), + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(scenario, results, totals) + else: + scenario_assert_stats(scenario, None, totals) + + # For the rest, we may assume method calls (i.e. non bare-app calls) + # III. explore changing method selector arg[0] by edit distance 1 + + # NOTE: We don't test the case of adding an argument to method calls + # because the SDK's will guard against this case. + # However, we should re-think this assumption. + # Cf. https://github.com/algorand/go-algorand-internal/issues/2772 + # Cf. https://github.com/algorand/algorand-sdk-testing/issues/190 + + scenario = "III(a). inserting an extra random byte into method selector" + results = rsim.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=None, + approval_abi_args_mod=ABIArgsMod.selector_byte_insert, + version=version, + method_configs=pure_meth_mconfigs, + omit_clear_call=True, + num_dryruns=NUM_ROUTER_DRYRUNS, + executor_validation=False, + msg=msg(), + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(scenario, results, totals) + + scenario = "III(b). removing a random byte from method selector" + results = rsim.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=None, + approval_abi_args_mod=ABIArgsMod.selector_byte_delete, + version=version, + method_configs=pure_meth_mconfigs, + omit_clear_call=True, + num_dryruns=NUM_ROUTER_DRYRUNS, + executor_validation=False, + msg=msg(), + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(scenario, results, totals) + + scenario = "III(c). replacing a random byte in method selector" + results = rsim.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=None, + approval_abi_args_mod=ABIArgsMod.selector_byte_replace, + version=version, + method_configs=pure_meth_mconfigs, + omit_clear_call=True, + num_dryruns=NUM_ROUTER_DRYRUNS, + executor_validation=False, + msg=msg(), + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(scenario, results, totals) + + # IV. explore changing the number of args over the 'good' call_types + # NOTE: We don't test the case of adding an argument to method calls + # We also remove methods with 0 arguments, as these degenerate to the + # already tested bare-app call case. + scenario = "IV. removing the final argument" + atleast_one_param_mconfigs = { + meth: mconfig + for meth, mconfig in pure_meth_mconfigs.items() + if len(contract.get_method_by_name(meth).args) > 0 + } + results = rsim.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=None, + approval_abi_args_mod=ABIArgsMod.parameter_delete, + version=version, + method_configs=atleast_one_param_mconfigs, + omit_clear_call=True, + num_dryruns=NUM_ROUTER_DRYRUNS, + executor_validation=False, + msg=msg(), + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(scenario, results, totals) + + print("SUMMARY STATS: ", json.dumps(totals, indent=2)) + + +IDENTITY_PREDICATES = { + DRProp.lastLog: PredicateKind.IdenticalPair, + DRProp.status: PredicateKind.IdenticalPair, + DRProp.error: PredicateKind.IdenticalPair, +} + + +def test_nontriv_clear(): + totals = defaultdict(int) + + questionable = [ + r for name, ver, r in ALL_ROUTER_CASES if name == "questionable" and ver == 6 + ][0] + nontriv_clear = [ + r for name, ver, r in ALL_ROUTER_CASES if name == "nontriv_clear" and ver == 6 + ][0] + + _, mconfigs = split_driver2predicates_methconfigs(DRIVERS["nontriv_clear"]) + predicates = {meth: IDENTITY_PREDICATES for meth in mconfigs} + + rsim_nt_vs_q = RouterSimulation( + nontriv_clear, predicates, model_router=questionable + ) + + # Sanity check the approval programs (_POSITIVE_ cases only): + msg = "APPROVAL nontriv@v6 vs. questionable@v8" + version = 6 + results = rsim_nt_vs_q.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=None, + approval_abi_args_mod=None, + version=version, + method_configs=mconfigs, + num_dryruns=NUM_ROUTER_DRYRUNS, + omit_clear_call=True, + model_version=8, + msg=msg, + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(msg, results, totals) + + msg = "APPROVAL nontriv@v8 vs. questionable@v8" + version = 8 + results = rsim_nt_vs_q.simulate_and_assert( + approval_args_strat_type=RandomABIStrategyHalfSized, + clear_args_strat_type_or_inputs=None, + approval_abi_args_mod=None, + version=version, + method_configs=mconfigs, + num_dryruns=NUM_ROUTER_DRYRUNS, + omit_clear_call=True, + model_version=8, + msg=msg, + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(msg, results, totals) + + print("PARTIAL SUMMARY STATS: ", json.dumps(totals, indent=2)) + + # Finally, a bespoke test for the non-trivial clear program: + bespoke = { + DRProp.passed: { + (): True, + (b"random bytes",): True, + (b"CLEANUP",): True, + (b"CLEANUP", b"random bytes"): True, + (b"CLEANUP", b"ABORTING"): False, + (b"CLEANUP", b"ABORTING", b"random bytes"): False, + } + } + inputs = list(bespoke[DRProp.passed].keys()) + clear_preds = {CLEAR_STATE_CALL: bespoke} + + msg = "CLEAR nontriv@v6" + version = 6 + clear_rsim = RouterSimulation(nontriv_clear, clear_preds) + results = clear_rsim.simulate_and_assert( + approval_args_strat_type=None, + clear_args_strat_type_or_inputs=inputs, + approval_abi_args_mod=None, + version=version, + method_configs=None, + msg=msg, + omit_approval_call=True, + skip_validation=True, + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(msg, results, totals) + + msg = "CLEAR nontriv@v8" + version = 8 + clear_rsim = RouterSimulation(nontriv_clear, clear_preds) + results = clear_rsim.simulate_and_assert( + approval_args_strat_type=None, + clear_args_strat_type_or_inputs=inputs, + approval_abi_args_mod=None, + version=version, + method_configs=None, + msg=msg, + omit_approval_call=True, + skip_validation=True, + ) + assert (sim_results := results.results) and all( + sim.succeeded for meth in sim_results.values() for sim in meth.values() + ) + scenario_assert_stats(msg, results, totals) diff --git a/tests/teal/router/nontriv_clear_approval_v6.teal b/tests/teal/router/nontriv_clear_approval_v6.teal new file mode 100644 index 000000000..51455774a --- /dev/null +++ b/tests/teal/router/nontriv_clear_approval_v6.teal @@ -0,0 +1,460 @@ +#pragma version 6 +txn NumAppArgs +int 0 +== +bnz main_l20 +txna ApplicationArgs 0 +method "add(uint64,uint64)uint64" +== +bnz main_l19 +txna ApplicationArgs 0 +method "sub(uint64,uint64)uint64" +== +bnz main_l18 +txna ApplicationArgs 0 +method "mul(uint64,uint64)uint64" +== +bnz main_l17 +txna ApplicationArgs 0 +method "div(uint64,uint64)uint64" +== +bnz main_l16 +txna ApplicationArgs 0 +method "mod(uint64,uint64)uint64" +== +bnz main_l15 +txna ApplicationArgs 0 +method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" +== +bnz main_l14 +txna ApplicationArgs 0 +method "empty_return_subroutine()void" +== +bnz main_l13 +txna ApplicationArgs 0 +method "log_1()uint64" +== +bnz main_l12 +txna ApplicationArgs 0 +method "log_creation()string" +== +bnz main_l11 +err +main_l11: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +== +&& +assert +callsub logcreation_8 +store 67 +byte 0x151f7c75 +load 67 +concat +log +int 1 +return +main_l12: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +txn ApplicationID +int 0 +!= +&& +|| +assert +callsub log1_7 +store 65 +byte 0x151f7c75 +load 65 +itob +concat +log +int 1 +return +main_l13: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +|| +assert +callsub emptyreturnsubroutine_6 +int 1 +return +main_l14: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 30 +txna ApplicationArgs 2 +btoi +store 31 +txna ApplicationArgs 3 +btoi +store 32 +txna ApplicationArgs 4 +btoi +store 33 +txna ApplicationArgs 5 +btoi +store 34 +txna ApplicationArgs 6 +btoi +store 35 +txna ApplicationArgs 7 +btoi +store 36 +txna ApplicationArgs 8 +btoi +store 37 +txna ApplicationArgs 9 +btoi +store 38 +txna ApplicationArgs 10 +btoi +store 39 +txna ApplicationArgs 11 +btoi +store 40 +txna ApplicationArgs 12 +btoi +store 41 +txna ApplicationArgs 13 +btoi +store 42 +txna ApplicationArgs 14 +btoi +store 43 +txna ApplicationArgs 15 +store 46 +load 46 +int 0 +extract_uint64 +store 44 +load 46 +int 8 +extract_uint64 +store 45 +load 30 +load 31 +load 32 +load 33 +load 34 +load 35 +load 36 +load 37 +load 38 +load 39 +load 40 +load 41 +load 42 +load 43 +load 44 +load 45 +callsub alllaidtoargs_5 +store 47 +byte 0x151f7c75 +load 47 +itob +concat +log +int 1 +return +main_l15: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 24 +txna ApplicationArgs 2 +btoi +store 25 +load 24 +load 25 +callsub mod_4 +store 26 +byte 0x151f7c75 +load 26 +itob +concat +log +int 1 +return +main_l16: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 18 +txna ApplicationArgs 2 +btoi +store 19 +load 18 +load 19 +callsub div_3 +store 20 +byte 0x151f7c75 +load 20 +itob +concat +log +int 1 +return +main_l17: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 12 +txna ApplicationArgs 2 +btoi +store 13 +load 12 +load 13 +callsub mul_2 +store 14 +byte 0x151f7c75 +load 14 +itob +concat +log +int 1 +return +main_l18: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 6 +txna ApplicationArgs 2 +btoi +store 7 +load 6 +load 7 +callsub sub_1 +store 8 +byte 0x151f7c75 +load 8 +itob +concat +log +int 1 +return +main_l19: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 0 +txna ApplicationArgs 2 +btoi +store 1 +load 0 +load 1 +callsub add_0 +store 2 +byte 0x151f7c75 +load 2 +itob +concat +log +int 1 +return +main_l20: +txn OnCompletion +int OptIn +== +bnz main_l22 +err +main_l22: +txn ApplicationID +int 0 +!= +assert +byte "optin call" +log +int 1 +return + +// add +add_0: +store 4 +store 3 +load 3 +load 4 ++ +store 5 +load 5 +retsub + +// sub +sub_1: +store 10 +store 9 +load 9 +load 10 +- +store 11 +load 11 +retsub + +// mul +mul_2: +store 16 +store 15 +load 15 +load 16 +* +store 17 +load 17 +retsub + +// div +div_3: +store 22 +store 21 +load 21 +load 22 +/ +store 23 +load 23 +retsub + +// mod +mod_4: +store 28 +store 27 +load 27 +load 28 +% +store 29 +load 29 +retsub + +// all_laid_to_args +alllaidtoargs_5: +store 63 +store 62 +store 61 +store 60 +store 59 +store 58 +store 57 +store 56 +store 55 +store 54 +store 53 +store 52 +store 51 +store 50 +store 49 +store 48 +load 48 +load 49 ++ +load 50 ++ +load 51 ++ +load 52 ++ +load 53 ++ +load 54 ++ +load 55 ++ +load 56 ++ +load 57 ++ +load 58 ++ +load 59 ++ +load 60 ++ +load 61 ++ +load 62 ++ +load 63 ++ +store 64 +load 64 +retsub + +// empty_return_subroutine +emptyreturnsubroutine_6: +byte "appear in both approval and clear state" +log +retsub + +// log_1 +log1_7: +int 1 +store 66 +load 66 +retsub + +// log_creation +logcreation_8: +byte 0x00106c6f6767696e67206372656174696f6e +store 68 +load 68 +retsub \ No newline at end of file diff --git a/tests/teal/router/nontriv_clear_approval_v8.teal b/tests/teal/router/nontriv_clear_approval_v8.teal new file mode 100644 index 000000000..00a013082 --- /dev/null +++ b/tests/teal/router/nontriv_clear_approval_v8.teal @@ -0,0 +1,512 @@ +#pragma version 8 +txn NumAppArgs +int 0 +== +bnz main_l20 +txna ApplicationArgs 0 +method "add(uint64,uint64)uint64" +== +bnz main_l19 +txna ApplicationArgs 0 +method "sub(uint64,uint64)uint64" +== +bnz main_l18 +txna ApplicationArgs 0 +method "mul(uint64,uint64)uint64" +== +bnz main_l17 +txna ApplicationArgs 0 +method "div(uint64,uint64)uint64" +== +bnz main_l16 +txna ApplicationArgs 0 +method "mod(uint64,uint64)uint64" +== +bnz main_l15 +txna ApplicationArgs 0 +method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" +== +bnz main_l14 +txna ApplicationArgs 0 +method "empty_return_subroutine()void" +== +bnz main_l13 +txna ApplicationArgs 0 +method "log_1()uint64" +== +bnz main_l12 +txna ApplicationArgs 0 +method "log_creation()string" +== +bnz main_l11 +err +main_l11: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +== +&& +assert +callsub logcreationcaster_17 +int 1 +return +main_l12: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +txn ApplicationID +int 0 +!= +&& +|| +assert +callsub log1caster_16 +int 1 +return +main_l13: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +|| +assert +callsub emptyreturnsubroutinecaster_15 +int 1 +return +main_l14: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub alllaidtoargscaster_14 +int 1 +return +main_l15: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub modcaster_13 +int 1 +return +main_l16: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub divcaster_12 +int 1 +return +main_l17: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub mulcaster_11 +int 1 +return +main_l18: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub subcaster_10 +int 1 +return +main_l19: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub addcaster_9 +int 1 +return +main_l20: +txn OnCompletion +int OptIn +== +bnz main_l22 +err +main_l22: +txn ApplicationID +int 0 +!= +assert +byte "optin call" +log +int 1 +return + +// add +add_0: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 ++ +frame_bury 0 +retsub + +// sub +sub_1: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +- +frame_bury 0 +retsub + +// mul +mul_2: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +* +frame_bury 0 +retsub + +// div +div_3: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +/ +frame_bury 0 +retsub + +// mod +mod_4: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +% +frame_bury 0 +retsub + +// all_laid_to_args +alllaidtoargs_5: +proto 16 1 +int 0 +frame_dig -16 +frame_dig -15 ++ +frame_dig -14 ++ +frame_dig -13 ++ +frame_dig -12 ++ +frame_dig -11 ++ +frame_dig -10 ++ +frame_dig -9 ++ +frame_dig -8 ++ +frame_dig -7 ++ +frame_dig -6 ++ +frame_dig -5 ++ +frame_dig -4 ++ +frame_dig -3 ++ +frame_dig -2 ++ +frame_dig -1 ++ +frame_bury 0 +retsub + +// empty_return_subroutine +emptyreturnsubroutine_6: +proto 0 0 +byte "appear in both approval and clear state" +log +retsub + +// log_1 +log1_7: +proto 0 1 +int 0 +int 1 +frame_bury 0 +retsub + +// log_creation +logcreation_8: +proto 0 1 +byte "" +byte 0x00106c6f6767696e67206372656174696f6e +frame_bury 0 +retsub + +// add_caster +addcaster_9: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub add_0 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// sub_caster +subcaster_10: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub sub_1 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// mul_caster +mulcaster_11: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub mul_2 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// div_caster +divcaster_12: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub div_3 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// mod_caster +modcaster_13: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub mod_4 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// all_laid_to_args_caster +alllaidtoargscaster_14: +proto 0 0 +int 0 +dupn 16 +byte "" +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +txna ApplicationArgs 3 +btoi +frame_bury 3 +txna ApplicationArgs 4 +btoi +frame_bury 4 +txna ApplicationArgs 5 +btoi +frame_bury 5 +txna ApplicationArgs 6 +btoi +frame_bury 6 +txna ApplicationArgs 7 +btoi +frame_bury 7 +txna ApplicationArgs 8 +btoi +frame_bury 8 +txna ApplicationArgs 9 +btoi +frame_bury 9 +txna ApplicationArgs 10 +btoi +frame_bury 10 +txna ApplicationArgs 11 +btoi +frame_bury 11 +txna ApplicationArgs 12 +btoi +frame_bury 12 +txna ApplicationArgs 13 +btoi +frame_bury 13 +txna ApplicationArgs 14 +btoi +frame_bury 14 +txna ApplicationArgs 15 +frame_bury 17 +frame_dig 17 +int 0 +extract_uint64 +frame_bury 15 +frame_dig 17 +int 8 +extract_uint64 +frame_bury 16 +frame_dig 1 +frame_dig 2 +frame_dig 3 +frame_dig 4 +frame_dig 5 +frame_dig 6 +frame_dig 7 +frame_dig 8 +frame_dig 9 +frame_dig 10 +frame_dig 11 +frame_dig 12 +frame_dig 13 +frame_dig 14 +frame_dig 15 +frame_dig 16 +callsub alllaidtoargs_5 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// empty_return_subroutine_caster +emptyreturnsubroutinecaster_15: +proto 0 0 +callsub emptyreturnsubroutine_6 +retsub + +// log_1_caster +log1caster_16: +proto 0 0 +int 0 +callsub log1_7 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// log_creation_caster +logcreationcaster_17: +proto 0 0 +byte "" +callsub logcreation_8 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +concat +log +retsub \ No newline at end of file diff --git a/tests/teal/router/nontriv_clear_clear_v6.teal b/tests/teal/router/nontriv_clear_clear_v6.teal new file mode 100644 index 000000000..22f69de3e --- /dev/null +++ b/tests/teal/router/nontriv_clear_clear_v6.teal @@ -0,0 +1,28 @@ +#pragma version 6 +txn NumAppArgs +int 2 +< +bnz main_l8 +txna ApplicationArgs 0 +byte "CLEANUP" +!= +bnz main_l7 +txna ApplicationArgs 1 +byte "ABORTING" +!= +bnz main_l6 +int 1 +bnz main_l5 +err +main_l5: +int 0 +return +main_l6: +int 1 +return +main_l7: +int 1 +return +main_l8: +int 1 +return \ No newline at end of file diff --git a/tests/teal/router/nontriv_clear_clear_v8.teal b/tests/teal/router/nontriv_clear_clear_v8.teal new file mode 100644 index 000000000..f9a349e0b --- /dev/null +++ b/tests/teal/router/nontriv_clear_clear_v8.teal @@ -0,0 +1,28 @@ +#pragma version 8 +txn NumAppArgs +int 2 +< +bnz main_l8 +txna ApplicationArgs 0 +byte "CLEANUP" +!= +bnz main_l7 +txna ApplicationArgs 1 +byte "ABORTING" +!= +bnz main_l6 +int 1 +bnz main_l5 +err +main_l5: +int 0 +return +main_l6: +int 1 +return +main_l7: +int 1 +return +main_l8: +int 1 +return \ No newline at end of file diff --git a/tests/teal/router/questionableFP_approval_v8.teal b/tests/teal/router/questionableFP_approval_v8.teal new file mode 100644 index 000000000..00a013082 --- /dev/null +++ b/tests/teal/router/questionableFP_approval_v8.teal @@ -0,0 +1,512 @@ +#pragma version 8 +txn NumAppArgs +int 0 +== +bnz main_l20 +txna ApplicationArgs 0 +method "add(uint64,uint64)uint64" +== +bnz main_l19 +txna ApplicationArgs 0 +method "sub(uint64,uint64)uint64" +== +bnz main_l18 +txna ApplicationArgs 0 +method "mul(uint64,uint64)uint64" +== +bnz main_l17 +txna ApplicationArgs 0 +method "div(uint64,uint64)uint64" +== +bnz main_l16 +txna ApplicationArgs 0 +method "mod(uint64,uint64)uint64" +== +bnz main_l15 +txna ApplicationArgs 0 +method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" +== +bnz main_l14 +txna ApplicationArgs 0 +method "empty_return_subroutine()void" +== +bnz main_l13 +txna ApplicationArgs 0 +method "log_1()uint64" +== +bnz main_l12 +txna ApplicationArgs 0 +method "log_creation()string" +== +bnz main_l11 +err +main_l11: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +== +&& +assert +callsub logcreationcaster_17 +int 1 +return +main_l12: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +txn ApplicationID +int 0 +!= +&& +|| +assert +callsub log1caster_16 +int 1 +return +main_l13: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +|| +assert +callsub emptyreturnsubroutinecaster_15 +int 1 +return +main_l14: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub alllaidtoargscaster_14 +int 1 +return +main_l15: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub modcaster_13 +int 1 +return +main_l16: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub divcaster_12 +int 1 +return +main_l17: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub mulcaster_11 +int 1 +return +main_l18: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub subcaster_10 +int 1 +return +main_l19: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub addcaster_9 +int 1 +return +main_l20: +txn OnCompletion +int OptIn +== +bnz main_l22 +err +main_l22: +txn ApplicationID +int 0 +!= +assert +byte "optin call" +log +int 1 +return + +// add +add_0: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 ++ +frame_bury 0 +retsub + +// sub +sub_1: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +- +frame_bury 0 +retsub + +// mul +mul_2: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +* +frame_bury 0 +retsub + +// div +div_3: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +/ +frame_bury 0 +retsub + +// mod +mod_4: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +% +frame_bury 0 +retsub + +// all_laid_to_args +alllaidtoargs_5: +proto 16 1 +int 0 +frame_dig -16 +frame_dig -15 ++ +frame_dig -14 ++ +frame_dig -13 ++ +frame_dig -12 ++ +frame_dig -11 ++ +frame_dig -10 ++ +frame_dig -9 ++ +frame_dig -8 ++ +frame_dig -7 ++ +frame_dig -6 ++ +frame_dig -5 ++ +frame_dig -4 ++ +frame_dig -3 ++ +frame_dig -2 ++ +frame_dig -1 ++ +frame_bury 0 +retsub + +// empty_return_subroutine +emptyreturnsubroutine_6: +proto 0 0 +byte "appear in both approval and clear state" +log +retsub + +// log_1 +log1_7: +proto 0 1 +int 0 +int 1 +frame_bury 0 +retsub + +// log_creation +logcreation_8: +proto 0 1 +byte "" +byte 0x00106c6f6767696e67206372656174696f6e +frame_bury 0 +retsub + +// add_caster +addcaster_9: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub add_0 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// sub_caster +subcaster_10: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub sub_1 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// mul_caster +mulcaster_11: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub mul_2 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// div_caster +divcaster_12: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub div_3 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// mod_caster +modcaster_13: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub mod_4 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// all_laid_to_args_caster +alllaidtoargscaster_14: +proto 0 0 +int 0 +dupn 16 +byte "" +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +txna ApplicationArgs 3 +btoi +frame_bury 3 +txna ApplicationArgs 4 +btoi +frame_bury 4 +txna ApplicationArgs 5 +btoi +frame_bury 5 +txna ApplicationArgs 6 +btoi +frame_bury 6 +txna ApplicationArgs 7 +btoi +frame_bury 7 +txna ApplicationArgs 8 +btoi +frame_bury 8 +txna ApplicationArgs 9 +btoi +frame_bury 9 +txna ApplicationArgs 10 +btoi +frame_bury 10 +txna ApplicationArgs 11 +btoi +frame_bury 11 +txna ApplicationArgs 12 +btoi +frame_bury 12 +txna ApplicationArgs 13 +btoi +frame_bury 13 +txna ApplicationArgs 14 +btoi +frame_bury 14 +txna ApplicationArgs 15 +frame_bury 17 +frame_dig 17 +int 0 +extract_uint64 +frame_bury 15 +frame_dig 17 +int 8 +extract_uint64 +frame_bury 16 +frame_dig 1 +frame_dig 2 +frame_dig 3 +frame_dig 4 +frame_dig 5 +frame_dig 6 +frame_dig 7 +frame_dig 8 +frame_dig 9 +frame_dig 10 +frame_dig 11 +frame_dig 12 +frame_dig 13 +frame_dig 14 +frame_dig 15 +frame_dig 16 +callsub alllaidtoargs_5 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// empty_return_subroutine_caster +emptyreturnsubroutinecaster_15: +proto 0 0 +callsub emptyreturnsubroutine_6 +retsub + +// log_1_caster +log1caster_16: +proto 0 0 +int 0 +callsub log1_7 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// log_creation_caster +logcreationcaster_17: +proto 0 0 +byte "" +callsub logcreation_8 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +concat +log +retsub \ No newline at end of file diff --git a/tests/teal/router/questionableFP_clear_v8.teal b/tests/teal/router/questionableFP_clear_v8.teal new file mode 100644 index 000000000..31588a8ec --- /dev/null +++ b/tests/teal/router/questionableFP_clear_v8.teal @@ -0,0 +1,3 @@ +#pragma version 8 +int 1 +return \ No newline at end of file diff --git a/tests/teal/router/questionable_approval_v6.teal b/tests/teal/router/questionable_approval_v6.teal new file mode 100644 index 000000000..51455774a --- /dev/null +++ b/tests/teal/router/questionable_approval_v6.teal @@ -0,0 +1,460 @@ +#pragma version 6 +txn NumAppArgs +int 0 +== +bnz main_l20 +txna ApplicationArgs 0 +method "add(uint64,uint64)uint64" +== +bnz main_l19 +txna ApplicationArgs 0 +method "sub(uint64,uint64)uint64" +== +bnz main_l18 +txna ApplicationArgs 0 +method "mul(uint64,uint64)uint64" +== +bnz main_l17 +txna ApplicationArgs 0 +method "div(uint64,uint64)uint64" +== +bnz main_l16 +txna ApplicationArgs 0 +method "mod(uint64,uint64)uint64" +== +bnz main_l15 +txna ApplicationArgs 0 +method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" +== +bnz main_l14 +txna ApplicationArgs 0 +method "empty_return_subroutine()void" +== +bnz main_l13 +txna ApplicationArgs 0 +method "log_1()uint64" +== +bnz main_l12 +txna ApplicationArgs 0 +method "log_creation()string" +== +bnz main_l11 +err +main_l11: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +== +&& +assert +callsub logcreation_8 +store 67 +byte 0x151f7c75 +load 67 +concat +log +int 1 +return +main_l12: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +txn ApplicationID +int 0 +!= +&& +|| +assert +callsub log1_7 +store 65 +byte 0x151f7c75 +load 65 +itob +concat +log +int 1 +return +main_l13: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +|| +assert +callsub emptyreturnsubroutine_6 +int 1 +return +main_l14: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 30 +txna ApplicationArgs 2 +btoi +store 31 +txna ApplicationArgs 3 +btoi +store 32 +txna ApplicationArgs 4 +btoi +store 33 +txna ApplicationArgs 5 +btoi +store 34 +txna ApplicationArgs 6 +btoi +store 35 +txna ApplicationArgs 7 +btoi +store 36 +txna ApplicationArgs 8 +btoi +store 37 +txna ApplicationArgs 9 +btoi +store 38 +txna ApplicationArgs 10 +btoi +store 39 +txna ApplicationArgs 11 +btoi +store 40 +txna ApplicationArgs 12 +btoi +store 41 +txna ApplicationArgs 13 +btoi +store 42 +txna ApplicationArgs 14 +btoi +store 43 +txna ApplicationArgs 15 +store 46 +load 46 +int 0 +extract_uint64 +store 44 +load 46 +int 8 +extract_uint64 +store 45 +load 30 +load 31 +load 32 +load 33 +load 34 +load 35 +load 36 +load 37 +load 38 +load 39 +load 40 +load 41 +load 42 +load 43 +load 44 +load 45 +callsub alllaidtoargs_5 +store 47 +byte 0x151f7c75 +load 47 +itob +concat +log +int 1 +return +main_l15: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 24 +txna ApplicationArgs 2 +btoi +store 25 +load 24 +load 25 +callsub mod_4 +store 26 +byte 0x151f7c75 +load 26 +itob +concat +log +int 1 +return +main_l16: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 18 +txna ApplicationArgs 2 +btoi +store 19 +load 18 +load 19 +callsub div_3 +store 20 +byte 0x151f7c75 +load 20 +itob +concat +log +int 1 +return +main_l17: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 12 +txna ApplicationArgs 2 +btoi +store 13 +load 12 +load 13 +callsub mul_2 +store 14 +byte 0x151f7c75 +load 14 +itob +concat +log +int 1 +return +main_l18: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 6 +txna ApplicationArgs 2 +btoi +store 7 +load 6 +load 7 +callsub sub_1 +store 8 +byte 0x151f7c75 +load 8 +itob +concat +log +int 1 +return +main_l19: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 0 +txna ApplicationArgs 2 +btoi +store 1 +load 0 +load 1 +callsub add_0 +store 2 +byte 0x151f7c75 +load 2 +itob +concat +log +int 1 +return +main_l20: +txn OnCompletion +int OptIn +== +bnz main_l22 +err +main_l22: +txn ApplicationID +int 0 +!= +assert +byte "optin call" +log +int 1 +return + +// add +add_0: +store 4 +store 3 +load 3 +load 4 ++ +store 5 +load 5 +retsub + +// sub +sub_1: +store 10 +store 9 +load 9 +load 10 +- +store 11 +load 11 +retsub + +// mul +mul_2: +store 16 +store 15 +load 15 +load 16 +* +store 17 +load 17 +retsub + +// div +div_3: +store 22 +store 21 +load 21 +load 22 +/ +store 23 +load 23 +retsub + +// mod +mod_4: +store 28 +store 27 +load 27 +load 28 +% +store 29 +load 29 +retsub + +// all_laid_to_args +alllaidtoargs_5: +store 63 +store 62 +store 61 +store 60 +store 59 +store 58 +store 57 +store 56 +store 55 +store 54 +store 53 +store 52 +store 51 +store 50 +store 49 +store 48 +load 48 +load 49 ++ +load 50 ++ +load 51 ++ +load 52 ++ +load 53 ++ +load 54 ++ +load 55 ++ +load 56 ++ +load 57 ++ +load 58 ++ +load 59 ++ +load 60 ++ +load 61 ++ +load 62 ++ +load 63 ++ +store 64 +load 64 +retsub + +// empty_return_subroutine +emptyreturnsubroutine_6: +byte "appear in both approval and clear state" +log +retsub + +// log_1 +log1_7: +int 1 +store 66 +load 66 +retsub + +// log_creation +logcreation_8: +byte 0x00106c6f6767696e67206372656174696f6e +store 68 +load 68 +retsub \ No newline at end of file diff --git a/tests/teal/router/questionable_clear_v6.teal b/tests/teal/router/questionable_clear_v6.teal new file mode 100644 index 000000000..c7de51891 --- /dev/null +++ b/tests/teal/router/questionable_clear_v6.teal @@ -0,0 +1,3 @@ +#pragma version 6 +int 1 +return \ No newline at end of file diff --git a/tests/teal/router/yaccFP_approval_v8.teal b/tests/teal/router/yaccFP_approval_v8.teal new file mode 100644 index 000000000..5c5bda6d0 --- /dev/null +++ b/tests/teal/router/yaccFP_approval_v8.teal @@ -0,0 +1,493 @@ +#pragma version 8 +txna ApplicationArgs 0 +method "add(uint64,uint64)uint64" +== +bnz main_l18 +txna ApplicationArgs 0 +method "sub(uint64,uint64)uint64" +== +bnz main_l17 +txna ApplicationArgs 0 +method "mul(uint64,uint64)uint64" +== +bnz main_l16 +txna ApplicationArgs 0 +method "div(uint64,uint64)uint64" +== +bnz main_l15 +txna ApplicationArgs 0 +method "mod(uint64,uint64)uint64" +== +bnz main_l14 +txna ApplicationArgs 0 +method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" +== +bnz main_l13 +txna ApplicationArgs 0 +method "empty_return_subroutine()void" +== +bnz main_l12 +txna ApplicationArgs 0 +method "log_1()uint64" +== +bnz main_l11 +txna ApplicationArgs 0 +method "log_creation()string" +== +bnz main_l10 +err +main_l10: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +== +&& +assert +callsub logcreationcaster_17 +int 1 +return +main_l11: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +txn ApplicationID +int 0 +!= +&& +|| +assert +callsub log1caster_16 +int 1 +return +main_l12: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +|| +assert +callsub emptyreturnsubroutinecaster_15 +int 1 +return +main_l13: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub alllaidtoargscaster_14 +int 1 +return +main_l14: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub modcaster_13 +int 1 +return +main_l15: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub divcaster_12 +int 1 +return +main_l16: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub mulcaster_11 +int 1 +return +main_l17: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub subcaster_10 +int 1 +return +main_l18: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +callsub addcaster_9 +int 1 +return + +// add +add_0: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 ++ +frame_bury 0 +retsub + +// sub +sub_1: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +- +frame_bury 0 +retsub + +// mul +mul_2: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +* +frame_bury 0 +retsub + +// div +div_3: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +/ +frame_bury 0 +retsub + +// mod +mod_4: +proto 2 1 +int 0 +frame_dig -2 +frame_dig -1 +% +frame_bury 0 +retsub + +// all_laid_to_args +alllaidtoargs_5: +proto 16 1 +int 0 +frame_dig -16 +frame_dig -15 ++ +frame_dig -14 ++ +frame_dig -13 ++ +frame_dig -12 ++ +frame_dig -11 ++ +frame_dig -10 ++ +frame_dig -9 ++ +frame_dig -8 ++ +frame_dig -7 ++ +frame_dig -6 ++ +frame_dig -5 ++ +frame_dig -4 ++ +frame_dig -3 ++ +frame_dig -2 ++ +frame_dig -1 ++ +frame_bury 0 +retsub + +// empty_return_subroutine +emptyreturnsubroutine_6: +proto 0 0 +byte "appear in both approval and clear state" +log +retsub + +// log_1 +log1_7: +proto 0 1 +int 0 +int 1 +frame_bury 0 +retsub + +// log_creation +logcreation_8: +proto 0 1 +byte "" +byte 0x00106c6f6767696e67206372656174696f6e +frame_bury 0 +retsub + +// add_caster +addcaster_9: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub add_0 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// sub_caster +subcaster_10: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub sub_1 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// mul_caster +mulcaster_11: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub mul_2 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// div_caster +divcaster_12: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub div_3 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// mod_caster +modcaster_13: +proto 0 0 +int 0 +dupn 2 +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +frame_dig 1 +frame_dig 2 +callsub mod_4 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// all_laid_to_args_caster +alllaidtoargscaster_14: +proto 0 0 +int 0 +dupn 16 +byte "" +txna ApplicationArgs 1 +btoi +frame_bury 1 +txna ApplicationArgs 2 +btoi +frame_bury 2 +txna ApplicationArgs 3 +btoi +frame_bury 3 +txna ApplicationArgs 4 +btoi +frame_bury 4 +txna ApplicationArgs 5 +btoi +frame_bury 5 +txna ApplicationArgs 6 +btoi +frame_bury 6 +txna ApplicationArgs 7 +btoi +frame_bury 7 +txna ApplicationArgs 8 +btoi +frame_bury 8 +txna ApplicationArgs 9 +btoi +frame_bury 9 +txna ApplicationArgs 10 +btoi +frame_bury 10 +txna ApplicationArgs 11 +btoi +frame_bury 11 +txna ApplicationArgs 12 +btoi +frame_bury 12 +txna ApplicationArgs 13 +btoi +frame_bury 13 +txna ApplicationArgs 14 +btoi +frame_bury 14 +txna ApplicationArgs 15 +frame_bury 17 +frame_dig 17 +int 0 +extract_uint64 +frame_bury 15 +frame_dig 17 +int 8 +extract_uint64 +frame_bury 16 +frame_dig 1 +frame_dig 2 +frame_dig 3 +frame_dig 4 +frame_dig 5 +frame_dig 6 +frame_dig 7 +frame_dig 8 +frame_dig 9 +frame_dig 10 +frame_dig 11 +frame_dig 12 +frame_dig 13 +frame_dig 14 +frame_dig 15 +frame_dig 16 +callsub alllaidtoargs_5 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// empty_return_subroutine_caster +emptyreturnsubroutinecaster_15: +proto 0 0 +callsub emptyreturnsubroutine_6 +retsub + +// log_1_caster +log1caster_16: +proto 0 0 +int 0 +callsub log1_7 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +itob +concat +log +retsub + +// log_creation_caster +logcreationcaster_17: +proto 0 0 +byte "" +callsub logcreation_8 +frame_bury 0 +byte 0x151f7c75 +frame_dig 0 +concat +log +retsub \ No newline at end of file diff --git a/tests/teal/router/yaccFP_clear_v8.teal b/tests/teal/router/yaccFP_clear_v8.teal new file mode 100644 index 000000000..31588a8ec --- /dev/null +++ b/tests/teal/router/yaccFP_clear_v8.teal @@ -0,0 +1,3 @@ +#pragma version 8 +int 1 +return \ No newline at end of file diff --git a/tests/teal/router/yacc_approval_v6.teal b/tests/teal/router/yacc_approval_v6.teal new file mode 100644 index 000000000..b36bc397e --- /dev/null +++ b/tests/teal/router/yacc_approval_v6.teal @@ -0,0 +1,441 @@ +#pragma version 6 +txna ApplicationArgs 0 +method "add(uint64,uint64)uint64" +== +bnz main_l18 +txna ApplicationArgs 0 +method "sub(uint64,uint64)uint64" +== +bnz main_l17 +txna ApplicationArgs 0 +method "mul(uint64,uint64)uint64" +== +bnz main_l16 +txna ApplicationArgs 0 +method "div(uint64,uint64)uint64" +== +bnz main_l15 +txna ApplicationArgs 0 +method "mod(uint64,uint64)uint64" +== +bnz main_l14 +txna ApplicationArgs 0 +method "all_laid_to_args(uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64,uint64)uint64" +== +bnz main_l13 +txna ApplicationArgs 0 +method "empty_return_subroutine()void" +== +bnz main_l12 +txna ApplicationArgs 0 +method "log_1()uint64" +== +bnz main_l11 +txna ApplicationArgs 0 +method "log_creation()string" +== +bnz main_l10 +err +main_l10: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +== +&& +assert +callsub logcreation_8 +store 67 +byte 0x151f7c75 +load 67 +concat +log +int 1 +return +main_l11: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +txn ApplicationID +int 0 +!= +&& +|| +assert +callsub log1_7 +store 65 +byte 0x151f7c75 +load 65 +itob +concat +log +int 1 +return +main_l12: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +txn OnCompletion +int OptIn +== +|| +assert +callsub emptyreturnsubroutine_6 +int 1 +return +main_l13: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 30 +txna ApplicationArgs 2 +btoi +store 31 +txna ApplicationArgs 3 +btoi +store 32 +txna ApplicationArgs 4 +btoi +store 33 +txna ApplicationArgs 5 +btoi +store 34 +txna ApplicationArgs 6 +btoi +store 35 +txna ApplicationArgs 7 +btoi +store 36 +txna ApplicationArgs 8 +btoi +store 37 +txna ApplicationArgs 9 +btoi +store 38 +txna ApplicationArgs 10 +btoi +store 39 +txna ApplicationArgs 11 +btoi +store 40 +txna ApplicationArgs 12 +btoi +store 41 +txna ApplicationArgs 13 +btoi +store 42 +txna ApplicationArgs 14 +btoi +store 43 +txna ApplicationArgs 15 +store 46 +load 46 +int 0 +extract_uint64 +store 44 +load 46 +int 8 +extract_uint64 +store 45 +load 30 +load 31 +load 32 +load 33 +load 34 +load 35 +load 36 +load 37 +load 38 +load 39 +load 40 +load 41 +load 42 +load 43 +load 44 +load 45 +callsub alllaidtoargs_5 +store 47 +byte 0x151f7c75 +load 47 +itob +concat +log +int 1 +return +main_l14: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 24 +txna ApplicationArgs 2 +btoi +store 25 +load 24 +load 25 +callsub mod_4 +store 26 +byte 0x151f7c75 +load 26 +itob +concat +log +int 1 +return +main_l15: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 18 +txna ApplicationArgs 2 +btoi +store 19 +load 18 +load 19 +callsub div_3 +store 20 +byte 0x151f7c75 +load 20 +itob +concat +log +int 1 +return +main_l16: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 12 +txna ApplicationArgs 2 +btoi +store 13 +load 12 +load 13 +callsub mul_2 +store 14 +byte 0x151f7c75 +load 14 +itob +concat +log +int 1 +return +main_l17: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 6 +txna ApplicationArgs 2 +btoi +store 7 +load 6 +load 7 +callsub sub_1 +store 8 +byte 0x151f7c75 +load 8 +itob +concat +log +int 1 +return +main_l18: +txn OnCompletion +int NoOp +== +txn ApplicationID +int 0 +!= +&& +assert +txna ApplicationArgs 1 +btoi +store 0 +txna ApplicationArgs 2 +btoi +store 1 +load 0 +load 1 +callsub add_0 +store 2 +byte 0x151f7c75 +load 2 +itob +concat +log +int 1 +return + +// add +add_0: +store 4 +store 3 +load 3 +load 4 ++ +store 5 +load 5 +retsub + +// sub +sub_1: +store 10 +store 9 +load 9 +load 10 +- +store 11 +load 11 +retsub + +// mul +mul_2: +store 16 +store 15 +load 15 +load 16 +* +store 17 +load 17 +retsub + +// div +div_3: +store 22 +store 21 +load 21 +load 22 +/ +store 23 +load 23 +retsub + +// mod +mod_4: +store 28 +store 27 +load 27 +load 28 +% +store 29 +load 29 +retsub + +// all_laid_to_args +alllaidtoargs_5: +store 63 +store 62 +store 61 +store 60 +store 59 +store 58 +store 57 +store 56 +store 55 +store 54 +store 53 +store 52 +store 51 +store 50 +store 49 +store 48 +load 48 +load 49 ++ +load 50 ++ +load 51 ++ +load 52 ++ +load 53 ++ +load 54 ++ +load 55 ++ +load 56 ++ +load 57 ++ +load 58 ++ +load 59 ++ +load 60 ++ +load 61 ++ +load 62 ++ +load 63 ++ +store 64 +load 64 +retsub + +// empty_return_subroutine +emptyreturnsubroutine_6: +byte "appear in both approval and clear state" +log +retsub + +// log_1 +log1_7: +int 1 +store 66 +load 66 +retsub + +// log_creation +logcreation_8: +byte 0x00106c6f6767696e67206372656174696f6e +store 68 +load 68 +retsub \ No newline at end of file diff --git a/tests/teal/router/yacc_clear_v6.teal b/tests/teal/router/yacc_clear_v6.teal new file mode 100644 index 000000000..c7de51891 --- /dev/null +++ b/tests/teal/router/yacc_clear_v6.teal @@ -0,0 +1,3 @@ +#pragma version 6 +int 1 +return \ No newline at end of file diff --git a/tests/unit/blackbox_test.py b/tests/unit/blackbox_test.py index 36763d693..b12b63e5f 100644 --- a/tests/unit/blackbox_test.py +++ b/tests/unit/blackbox_test.py @@ -1,12 +1,19 @@ from itertools import product from pathlib import Path -import pytest from typing import Literal, Optional, Tuple +from unittest.mock import MagicMock -import pyteal as pt - -from tests.blackbox import Blackbox, BlackboxWrapper, PyTealDryRunExecutor +import pytest +from algosdk.v2client.algod import AlgodClient +from graviton.inspector import DryRunProperty as DRProp +import pyteal as pt +from tests.blackbox import ( + Blackbox, + BlackboxWrapper, + PyTealDryRunExecutor, + RouterSimulation, +) from tests.compile_asserts import assert_teal_as_expected PATH = Path.cwd() / "tests" / "unit" @@ -256,3 +263,91 @@ def test_PyTealBlackboxExecutor_abi_return_type( assert PyTealDryRunExecutor(fn, mode).abi_return_type() is not None else: assert PyTealDryRunExecutor(fn, mode).abi_return_type() is None + + +def successful_RouterSimulation(router, model_router, predicates, algod): + rsim = RouterSimulation( + router, + predicates, + model_router=model_router, + algod=algod, + ) + assert rsim.router == router + assert rsim.predicates == predicates + assert rsim.model_router == model_router + assert rsim.algod == algod + + return rsim + + +def failing_RouterSimulation(router, model_router, predicates, algod, err_msg): + with pytest.raises(AssertionError) as ae: + RouterSimulation( + router, + predicates, + model_router=model_router, + algod=algod, + ) + assert err_msg == str(ae.value) + + +def test_RouterSimulation_init(): + router = "not a router" + model_router = "not a router either" + predicates = "totally unchecked at init" + algod = MagicMock(spec=AlgodClient) + + # many paths to misery: + err_msg = "Wrong type for Base Router: " + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + router = pt.Router("test_router") + err_msg = "make sure to give at least one key/value pair in method_configs" + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + router.add_method_handler( + pt.ABIReturnSubroutine(lambda: pt.Int(1)), overriding_name="foo" + ) + err_msg = "Wrong type for predicates: . Please provide: dict[str | None, dict[graviton.DryRunProporty, Any]." + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + predicates = {} + err_msg = "Please provide at least one method to call and assert against." + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + predicates = {3: "blah"} + err_msg = "Predicates method '3' has type but only 'str' and 'NoneType' and Literal['ClearStateCall'] (== ClearStateCall) are allowed." + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + predicates = {Literal[42]: "blah"} + err_msg = "Predicates method 'typing.Literal[42]' is not allowed. Only Literal['ClearStateCall'] (== ClearStateCall) is allowed for a Literal." + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + predicates = {"bar": {DRProp.passed: True}, "foo": {}} + err_msg = "Every method must provide at least one predicate for assertion but method 'foo' is missing predicates." + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + predicates = {"bar": {DRProp.passed: True}, "foo": 42} + err_msg = "Method 'foo' is expected to have dict[graviton.DryRunProperty, Any] for its predicates value but the type is ." + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + predicates = {"bar": {DRProp.passed: True}, "foo": {"blah": 45}} + err_msg = "Method 'foo' is expected to have dict[graviton.DryRunProperty, Any] for its predicates value but predicates['foo'] has key 'blah' of ." + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + predicates = {"bar": {DRProp.passed: True}, "foo": {DRProp.budgetAdded: 45}} + err_msg = "Wrong type for Model Router: " + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + model_router = pt.Router("test_router") + err_msg = "make sure to give at least one key/value pair in method_configs" + failing_RouterSimulation(router, model_router, predicates, algod, err_msg) + + # Finally, two happy paths + model_router.add_method_handler( + pt.ABIReturnSubroutine(lambda: pt.Int(1)), overriding_name="foo" + ) + successful_RouterSimulation(router, model_router, predicates, algod) + + model_router = None + successful_RouterSimulation(router, model_router, predicates, algod)