示例#1
0
def test_ecdsa_decompress(curve: pt.EcdsaCurve):
    compressed_pubkey = pt.Bytes("XY")
    pubkey = pt.EcdsaDecompress(curve, compressed_pubkey)
    assert pubkey.type_of() == pt.TealType.none

    expected = pt.TealSimpleBlock([
        pt.TealOp(compressed_pubkey, pt.Op.byte, '"XY"'),
        pt.TealOp(pubkey, pt.Op.ecdsa_pk_decompress, curve.arg_name),
        pt.TealOp(pubkey.output_slots[1].store(), pt.Op.store,
                  pubkey.output_slots[1]),
        pt.TealOp(pubkey.output_slots[0].store(), pt.Op.store,
                  pubkey.output_slots[0]),
    ])

    actual, _ = pubkey.__teal__(curve_options_map[curve])
    actual.addIncoming()
    actual = pt.TealBlock.NormalizeBlocks(actual)

    with pt.TealComponent.Context.ignoreExprEquality():
        assert actual == expected

    # compile without errors this is necessary so assembly is also tested
    pt.compileTeal(pt.Seq(pubkey, pt.Approve()),
                   pt.Mode.Application,
                   version=curve.min_version)

    with pytest.raises(pt.TealInputError):
        pt.compileTeal(
            pt.Seq(pubkey, pt.Approve()),
            pt.Mode.Application,
            version=curve.min_version - 1,
        )
示例#2
0
def test_ecdsa_verify_basic(curve: pt.EcdsaCurve):
    args = [pt.Bytes("data"), pt.Bytes("sigA"), pt.Bytes("sigB")]
    pubkey = (pt.Bytes("X"), pt.Bytes("Y"))
    expr = pt.EcdsaVerify(curve, args[0], args[1], args[2], pubkey)
    assert expr.type_of() == pt.TealType.uint64

    expected = pt.TealSimpleBlock([
        pt.TealOp(args[0], pt.Op.byte, '"data"'),
        pt.TealOp(args[1], pt.Op.byte, '"sigA"'),
        pt.TealOp(args[2], pt.Op.byte, '"sigB"'),
        pt.TealOp(pubkey[0], pt.Op.byte, '"X"'),
        pt.TealOp(pubkey[1], pt.Op.byte, '"Y"'),
        pt.TealOp(expr, pt.Op.ecdsa_verify, curve.arg_name),
    ])

    actual, _ = expr.__teal__(curve_options_map[curve])
    actual.addIncoming()
    actual = pt.TealBlock.NormalizeBlocks(actual)

    assert actual == expected

    # compile without errors this is necessary so assembly is also tested
    pt.compileTeal(
        pt.Seq(pt.Pop(expr), pt.Approve()),
        pt.Mode.Application,
        version=curve.min_version,
    )

    with pytest.raises(pt.TealInputError):
        pt.compileTeal(
            pt.Seq(pt.Pop(expr), pt.Approve()),
            pt.Mode.Application,
            version=curve.min_version - 1,
        )
示例#3
0
def test_wrap_handler_bare_call():
    BARE_CALL_CASES = [
        dummy_doing_nothing,
        safe_clear_state_delete,
        pt.Approve(),
        pt.Log(pt.Bytes("message")),
    ]
    for bare_call in BARE_CALL_CASES:
        wrapped: pt.Expr = ASTBuilder.wrap_handler(False, bare_call)
        expected: pt.Expr
        match bare_call:
            case pt.Expr():
                if bare_call.has_return():
                    expected = bare_call
                else:
                    expected = pt.Seq(bare_call, pt.Approve())
            case pt.SubroutineFnWrapper() | pt.ABIReturnSubroutine():
                expected = pt.Seq(bare_call(), pt.Approve())
            case _:
                raise pt.TealInputError("how you got here?")
        wrapped_assemble = assemble_helper(wrapped)
        wrapped_helper = assemble_helper(expected)
        with pt.TealComponent.Context.ignoreExprEquality():
            assert wrapped_assemble == wrapped_helper

    ERROR_CASES = [
        (
            pt.Int(1),
            f"bare appcall handler should be TealType.none not {pt.TealType.uint64}.",
        ),
        (
            returning_u64,
            f"subroutine call should be returning TealType.none not {pt.TealType.uint64}.",
        ),
        (
            mult_over_u64_and_log,
            "subroutine call should take 0 arg for bare-app call. this subroutine takes 2.",
        ),
        (
            eine_constant,
            f"abi-returning subroutine call should be returning void not {pt.abi.Uint64TypeSpec()}.",
        ),
        (
            take_abi_and_log,
            "abi-returning subroutine call should take 0 arg for bare-app call. this abi-returning subroutine takes 1.",
        ),
        (
            1,
            "bare appcall can only accept: none type Expr, or Subroutine/ABIReturnSubroutine with none return and no arg",
        ),
    ]
    for error_case, error_msg in ERROR_CASES:
        with pytest.raises(pt.TealInputError) as bug:
            ASTBuilder.wrap_handler(False, error_case)
        assert error_msg in str(bug)
示例#4
0
def test_build_program_clear_state_valid_config():
    action = pt.If(pt.Txn.fee() == pt.Int(4)).Then(pt.Approve()).Else(pt.Reject())
    config = pt.CallConfig.CALL

    router_with_bare_call = pt.Router(
        "test",
        pt.BareCallActions(
            clear_state=pt.OnCompleteAction(action=action, call_config=config)
        ),
    )
    _, actual_clear_state_with_bare_call, _ = router_with_bare_call.build_program()

    expected_clear_state_with_bare_call = assemble_helper(
        pt.Cond([pt.Txn.application_args.length() == pt.Int(0), action])
    )

    with pt.TealComponent.Context.ignoreExprEquality():
        assert (
            assemble_helper(actual_clear_state_with_bare_call)
            == expected_clear_state_with_bare_call
        )

    router_with_method = pt.Router("test")

    @pt.ABIReturnSubroutine
    def clear_state_method():
        return action

    router_with_method.add_method_handler(
        clear_state_method, method_config=pt.MethodConfig(clear_state=config)
    )

    _, actual_clear_state_with_method, _ = router_with_method.build_program()

    expected_clear_state_with_method = assemble_helper(
        pt.Cond(
            [
                pt.Txn.application_args[0]
                == pt.MethodSignature("clear_state_method()void"),
                pt.Seq(clear_state_method(), pt.Approve()),
            ]
        )
    )

    with pt.TealComponent.Context.ignoreExprEquality():
        assert (
            assemble_helper(actual_clear_state_with_method)
            == expected_clear_state_with_method
        )
示例#5
0
def test_wrap_handler_method_txn_types():
    wrapped: pt.Expr = ASTBuilder.wrap_handler(True, multiple_txn)
    actual: pt.TealBlock = assemble_helper(wrapped)

    args: list[pt.abi.Transaction] = [
        pt.abi.ApplicationCallTransaction(),
        pt.abi.AssetTransferTransaction(),
        pt.abi.PaymentTransaction(),
        pt.abi.Transaction(),
    ]
    output_temp = pt.abi.Uint64()
    expected_ast = pt.Seq(
        args[0]._set_index(pt.Txn.group_index() - pt.Int(4)),
        pt.Assert(args[0].get().type_enum() == pt.TxnType.ApplicationCall),
        args[1]._set_index(pt.Txn.group_index() - pt.Int(3)),
        pt.Assert(args[1].get().type_enum() == pt.TxnType.AssetTransfer),
        args[2]._set_index(pt.Txn.group_index() - pt.Int(2)),
        pt.Assert(args[2].get().type_enum() == pt.TxnType.Payment),
        args[3]._set_index(pt.Txn.group_index() - pt.Int(1)),
        multiple_txn(*args).store_into(output_temp),
        pt.abi.MethodReturn(output_temp),
        pt.Approve(),
    )

    expected = assemble_helper(expected_ast)
    with pt.TealComponent.Context.ignoreScratchSlotEquality(), pt.TealComponent.Context.ignoreExprEquality():
        assert actual == expected

    assert pt.TealBlock.MatchScratchSlotReferences(
        pt.TealBlock.GetReferencedScratchSlots(actual),
        pt.TealBlock.GetReferencedScratchSlots(expected),
    )
示例#6
0
def test_build_program_clear_state_invalid_config():
    for config in (pt.CallConfig.CREATE, pt.CallConfig.ALL):
        bareCalls = pt.BareCallActions(
            clear_state=pt.OnCompleteAction(action=pt.Approve(), call_config=config)
        )
        with pytest.raises(
            pt.TealInputError,
            match=r"Only CallConfig.CALL or CallConfig.NEVER are valid for a clear state CallConfig, since clear state can never be invoked during creation$",
        ):
            pt.Router("test", bareCalls)

        router = pt.Router("test")

        @pt.ABIReturnSubroutine
        def clear_state_method():
            return pt.Approve()

        with pytest.raises(
            pt.TealInputError,
            match=r"Only CallConfig.CALL or CallConfig.NEVER are valid for a clear state CallConfig, since clear state can never be invoked during creation$",
        ):
            router.add_method_handler(
                clear_state_method,
                method_config=pt.MethodConfig(clear_state=config),
            )
示例#7
0
def test_ecdsa_verify_recovered_pk():
    curve = pt.EcdsaCurve.Secp256k1
    args = [pt.Bytes("data"), pt.Int(1), pt.Bytes("sigA"), pt.Bytes("sigB")]
    pubkey = pt.EcdsaRecover(curve, args[0], args[1], args[2], args[3])
    expr = pt.EcdsaVerify(curve, args[0], args[2], args[3], pubkey)
    assert expr.type_of() == pt.TealType.uint64

    expected = pt.TealSimpleBlock([
        pt.TealOp(args[0], pt.Op.byte, '"data"'),
        pt.TealOp(args[1], pt.Op.int, 1),
        pt.TealOp(args[2], pt.Op.byte, '"sigA"'),
        pt.TealOp(args[3], pt.Op.byte, '"sigB"'),
        pt.TealOp(pubkey, pt.Op.ecdsa_pk_recover, curve.arg_name),
        pt.TealOp(pubkey.output_slots[1].store(), pt.Op.store,
                  pubkey.output_slots[1]),
        pt.TealOp(pubkey.output_slots[0].store(), pt.Op.store,
                  pubkey.output_slots[0]),
        pt.TealOp(args[0], pt.Op.byte, '"data"'),
        pt.TealOp(args[1], pt.Op.byte, '"sigA"'),
        pt.TealOp(args[2], pt.Op.byte, '"sigB"'),
        pt.TealOp(pubkey.output_slots[0].load(), pt.Op.load,
                  pubkey.output_slots[0]),
        pt.TealOp(pubkey.output_slots[1].load(), pt.Op.load,
                  pubkey.output_slots[1]),
        pt.TealOp(expr, pt.Op.ecdsa_verify, curve.arg_name),
    ])

    actual, _ = expr.__teal__(curve_options_map[curve])
    actual.addIncoming()
    actual = pt.TealBlock.NormalizeBlocks(actual)

    with pt.TealComponent.Context.ignoreExprEquality():
        assert actual == expected

    # compile without errors this is necessary so assembly is also tested
    pt.compileTeal(
        pt.Seq(pt.Pop(expr), pt.Approve()),
        pt.Mode.Application,
        version=curve.min_version,
    )

    with pytest.raises(pt.TealInputError):
        pt.compileTeal(
            pt.Seq(pt.Pop(expr), pt.Approve()),
            pt.Mode.Application,
            version=curve.min_version - 1,
        )
示例#8
0
def test_pragma_expr(compiler_version, should_error):
    program = pt.Pragma(pt.Approve(), compiler_version=compiler_version)

    if should_error:
        with pytest.raises(pt.TealPragmaError):
            pt.compileTeal(program, mode=pt.Mode.Application, version=6)
    else:
        pt.compileTeal(program, mode=pt.Mode.Application, version=6)
示例#9
0
def test_optimize_subroutine_with_global_var():
    global_var = pt.ScratchVar(pt.TealType.uint64)

    @pt.Subroutine(pt.TealType.uint64)
    def add(a1: pt.Expr) -> pt.Expr:
        return pt.Seq(global_var.store(pt.Int(2)), global_var.load() + a1)

    program = pt.Seq([
        pt.If(pt.Txn.sender() == pt.Global.creator_address()).Then(
            pt.Pop(add(pt.Int(1)))),
        global_var.store(pt.Int(5)),
        pt.Approve(),
    ])

    optimize_options = OptimizeOptions()

    # unoptimized
    expected = """#pragma version 4
txn Sender
global CreatorAddress
==
bz main_l2
int 1
callsub add_0
pop
main_l2:
int 5
store 0
int 1
return

// add
add_0:
store 1
int 2
store 0
load 0
load 1
+
retsub
    """.strip()
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected

    # optimization should not apply to global vars
    optimize_options = OptimizeOptions(scratch_slots=True)
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected
示例#10
0
def test_optimize_subroutine_with_reserved_local_var():
    local_var = pt.ScratchVar(pt.TealType.uint64, 0)

    @pt.Subroutine(pt.TealType.uint64)
    def add(a1: pt.Expr) -> pt.Expr:
        return pt.Seq(local_var.store(pt.Int(2)), local_var.load() + a1)

    program = pt.Seq([
        pt.If(pt.Txn.sender() == pt.Global.creator_address()).Then(
            pt.Pop(add(pt.Int(1)))),
        pt.Approve(),
    ])

    optimize_options = OptimizeOptions()

    # unoptimized
    expected = """#pragma version 4
txn Sender
global CreatorAddress
==
bz main_l2
int 1
callsub add_0
pop
main_l2:
int 1
return

// add
add_0:
store 1
int 2
store 0
load 0
load 1
+
retsub
    """.strip()
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected

    # The optimization must skip over the reserved slot id so the expected result
    # hasn't changed.
    optimize_options = OptimizeOptions(scratch_slots=True)
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected
示例#11
0
def test_optimize_multi_value():
    # note: this is incorrect usage of the app_global_get_ex opcode
    program = pt.Seq(
        pt.MultiValue(
            pt.Op.app_global_get_ex,
            [pt.TealType.uint64, pt.TealType.uint64],
            immediate_args=[],
            args=[pt.Int(0), pt.Int(1)],
        ).outputReducer(lambda value, hasValue: pt.Pop(value + hasValue)),
        pt.Approve(),
    )

    optimize_options = OptimizeOptions()

    # unoptimized
    expected = """#pragma version 4
int 0
int 1
app_global_get_ex
store 1
store 0
load 0
load 1
+
pop
int 1
return""".strip()
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected

    # optimized
    expected = """#pragma version 4
int 0
int 1
app_global_get_ex
+
pop
int 1
return""".strip()
    optimize_options = OptimizeOptions(scratch_slots=True)
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected
示例#12
0
def test_wrap_handler_method_call_many_args():
    wrapped: pt.Expr = ASTBuilder.wrap_handler(True, many_args)
    actual: pt.TealBlock = assemble_helper(wrapped)

    args = [pt.abi.Uint64() for _ in range(20)]
    last_arg = pt.abi.TupleTypeSpec(
        *[pt.abi.Uint64TypeSpec() for _ in range(6)]
    ).new_instance()

    output_temp = pt.abi.Uint64()
    expected_ast = pt.Seq(
        args[0].decode(pt.Txn.application_args[1]),
        args[1].decode(pt.Txn.application_args[2]),
        args[2].decode(pt.Txn.application_args[3]),
        args[3].decode(pt.Txn.application_args[4]),
        args[4].decode(pt.Txn.application_args[5]),
        args[5].decode(pt.Txn.application_args[6]),
        args[6].decode(pt.Txn.application_args[7]),
        args[7].decode(pt.Txn.application_args[8]),
        args[8].decode(pt.Txn.application_args[9]),
        args[9].decode(pt.Txn.application_args[10]),
        args[10].decode(pt.Txn.application_args[11]),
        args[11].decode(pt.Txn.application_args[12]),
        args[12].decode(pt.Txn.application_args[13]),
        args[13].decode(pt.Txn.application_args[14]),
        last_arg.decode(pt.Txn.application_args[15]),
        last_arg[0].store_into(args[14]),
        last_arg[1].store_into(args[15]),
        last_arg[2].store_into(args[16]),
        last_arg[3].store_into(args[17]),
        last_arg[4].store_into(args[18]),
        last_arg[5].store_into(args[19]),
        many_args(*args).store_into(output_temp),
        pt.abi.MethodReturn(output_temp),
        pt.Approve(),
    )
    expected = assemble_helper(expected_ast)
    with pt.TealComponent.Context.ignoreScratchSlotEquality(), pt.TealComponent.Context.ignoreExprEquality():
        assert actual == expected

    assert pt.TealBlock.MatchScratchSlotReferences(
        pt.TealBlock.GetReferencedScratchSlots(actual),
        pt.TealBlock.GetReferencedScratchSlots(expected),
    )
示例#13
0
def test_build_program_clear_state_empty():
    router = pt.Router(
        "test", pt.BareCallActions(no_op=pt.OnCompleteAction.always(pt.Approve()))
    )

    approval, clear_state, contract = router.build_program()

    expected_empty_program = pt.TealSimpleBlock(
        [
            pt.TealOp(None, pt.Op.int, 0),
            pt.TealOp(None, pt.Op.return_),
        ]
    )

    with pt.TealComponent.Context.ignoreExprEquality():
        assert assemble_helper(approval) != expected_empty_program
        assert assemble_helper(clear_state) == expected_empty_program

    expected_contract = sdk_abi.Contract("test", [])
    assert contract == expected_contract
示例#14
0
def test_optimize_dynamic_var():
    myvar = pt.DynamicScratchVar()
    regvar = pt.ScratchVar()
    program = pt.Seq(
        regvar.store(pt.Int(1)),
        myvar.set_index(regvar),
        regvar.store(pt.Int(2)),
        pt.Pop(regvar.load()),
        pt.Approve(),
    )

    optimize_options = OptimizeOptions()

    # unoptimized
    expected = """#pragma version 4
int 1
store 1
int 1
store 0
int 2
store 1
load 1
pop
int 1
return""".strip()
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected

    # optimization should not change the code because the candidate slot
    # is used by the dynamic slot variable.
    optimize_options = OptimizeOptions(scratch_slots=True)
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected
示例#15
0
 def clear_state_method():
     return pt.Approve()
示例#16
0
def test_wrap_handler_method_call():
    with pytest.raises(pt.TealInputError) as bug:
        ASTBuilder.wrap_handler(True, not_registrable)
    assert "method call ABIReturnSubroutine is not routable" in str(bug)

    with pytest.raises(pt.TealInputError) as bug:
        ASTBuilder.wrap_handler(True, safe_clear_state_delete)
    assert "method call should be only registering ABIReturnSubroutine" in str(bug)

    ONLY_ABI_SUBROUTINE_CASES = list(
        filter(lambda x: isinstance(x, pt.ABIReturnSubroutine), GOOD_SUBROUTINE_CASES)
    )

    for abi_subroutine in ONLY_ABI_SUBROUTINE_CASES:
        wrapped: pt.Expr = ASTBuilder.wrap_handler(True, abi_subroutine)
        actual: pt.TealBlock = assemble_helper(wrapped)

        args: list[pt.abi.BaseType] = [
            spec.new_instance()
            for spec in typing.cast(
                list[pt.abi.TypeSpec], abi_subroutine.subroutine.expected_arg_types
            )
        ]

        app_args = [
            arg for arg in args if arg.type_spec() not in pt.abi.TransactionTypeSpecs
        ]

        app_arg_cnt = len(app_args)

        txn_args: list[pt.abi.Transaction] = [
            arg for arg in args if arg.type_spec() in pt.abi.TransactionTypeSpecs
        ]

        loading: list[pt.Expr] = []

        if app_arg_cnt > pt.METHOD_ARG_NUM_CUTOFF:
            sdk_last_arg = pt.abi.TupleTypeSpec(
                *[arg.type_spec() for arg in app_args[pt.METHOD_ARG_NUM_CUTOFF - 1 :]]
            ).new_instance()

            loading = [
                arg.decode(pt.Txn.application_args[index + 1])
                for index, arg in enumerate(app_args[: pt.METHOD_ARG_NUM_CUTOFF - 1])
            ]

            loading.append(
                sdk_last_arg.decode(pt.Txn.application_args[pt.METHOD_ARG_NUM_CUTOFF])
            )
        else:
            loading = [
                arg.decode(pt.Txn.application_args[index + 1])
                for index, arg in enumerate(app_args)
            ]

        if len(txn_args) > 0:
            for idx, txn_arg in enumerate(txn_args):
                loading.append(
                    txn_arg._set_index(
                        pt.Txn.group_index() - pt.Int(len(txn_args) - idx)
                    )
                )
                if str(txn_arg.type_spec()) != "txn":
                    loading.append(
                        pt.Assert(
                            txn_arg.get().type_enum()
                            == txn_arg.type_spec().txn_type_enum()
                        )
                    )

        if app_arg_cnt > pt.METHOD_ARG_NUM_CUTOFF:
            loading.extend(
                [
                    sdk_last_arg[idx].store_into(val)
                    for idx, val in enumerate(app_args[pt.METHOD_ARG_NUM_CUTOFF - 1 :])
                ]
            )

        evaluate: pt.Expr
        if abi_subroutine.type_of() != "void":
            output_temp = abi_subroutine.output_kwarg_info.abi_type.new_instance()
            evaluate = pt.Seq(
                abi_subroutine(*args).store_into(output_temp),
                pt.abi.MethodReturn(output_temp),
            )
        else:
            evaluate = abi_subroutine(*args)

        expected = assemble_helper(pt.Seq(*loading, evaluate, pt.Approve()))
        with pt.TealComponent.Context.ignoreScratchSlotEquality(), pt.TealComponent.Context.ignoreExprEquality():
            assert actual == expected

        assert pt.TealBlock.MatchScratchSlotReferences(
            pt.TealBlock.GetReferencedScratchSlots(actual),
            pt.TealBlock.GetReferencedScratchSlots(expected),
        )
示例#17
0
def test_optimize_subroutine():
    @pt.Subroutine(pt.TealType.uint64)
    def add(a1: pt.Expr, a2: pt.Expr) -> pt.Expr:
        return a1 + a2

    program = pt.Seq([
        pt.If(pt.Txn.sender() == pt.Global.creator_address()).Then(
            pt.Pop(add(pt.Int(1), pt.Int(2)))),
        pt.Approve(),
    ])

    optimize_options = OptimizeOptions()

    # unoptimized
    expected = """#pragma version 4
txn Sender
global CreatorAddress
==
bz main_l2
int 1
int 2
callsub add_0
pop
main_l2:
int 1
return

// add
add_0:
store 1
store 0
load 0
load 1
+
retsub
    """.strip()
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected

    # optimized
    expected = """#pragma version 4
txn Sender
global CreatorAddress
==
bz main_l2
int 1
int 2
callsub add_0
pop
main_l2:
int 1
return

// add
add_0:
+
retsub
    """.strip()
    optimize_options = OptimizeOptions(scratch_slots=True)
    actual = pt.compileTeal(program,
                            version=4,
                            mode=pt.Mode.Application,
                            optimize=optimize_options)
    assert actual == expected
示例#18
0
def safe_clear_state_delete():
    return (
        pt.If(pt.Txn.sender() == pt.Global.creator_address())
        .Then(pt.Approve())
        .Else(pt.Reject())
    )
示例#19
0
def test_pragma_expr_invalid_compiler_version(compiler_version):
    with pytest.raises(ValueError):
        pt.Pragma(pt.Approve(), compiler_version=compiler_version)