Пример #1
0
def blackbox_pyteal_example1():
    # Example 1: Using blackbox_pyteal for a simple test of both an app and logic sig:
    from graviton.blackbox import DryRunEncoder

    from pyteal import Int, Mode, Subroutine, TealType
    from tests.blackbox import Blackbox

    @Blackbox(input_types=[TealType.uint64])
    @Subroutine(TealType.uint64)
    def square(x):
        return x**Int(2)

    # provide args for evaluation (will compute x^2)
    x = 9
    args = [x]

    # evaluate the programs
    app_result = PyTealDryRunExecutor(square, Mode.Application).dryrun(args)
    lsig_result = PyTealDryRunExecutor(square, Mode.Signature).dryrun(args)

    # check to see that x^2 is at the top of the stack as expected
    assert app_result.stack_top() == x**2, app_result.report(
        args, "stack_top() gave unexpected results for app")
    assert lsig_result.stack_top() == x**2, lsig_result.report(
        args, "stack_top() gave unexpected results for lsig")

    # check to see that itob of x^2 has been logged (only for the app case)
    assert app_result.last_log() == DryRunEncoder.hex(x**2), app_result.report(
        args, "last_log() gave unexpected results from app")
Пример #2
0
def test_recover():
    @Blackbox(input_types=[])
    @Subroutine(TealType.uint64)
    def recover():
        return EcdsaRecover(
            EcdsaCurve.Secp256k1,
            Sha512_256(Bytes("testdata")),
            Int(1),
            Bytes(
                "base16",
                "cabed943e1403fb93b388174c59a52c759b321855f2d7c4fcc23c99a8a6dce79",
            ),
            Bytes(
                "base16",
                "56192820dde344c32f81450db05e51c6a6f45a2a2db229f657d2c040baf31537",
            ),
        ).outputReducer(lambda x, y: And(
            x == Bytes(
                "base16",
                "71539e0c7a6902a3f5413d6e28a455b2a14316fcf0f6b21193343b3b9d455053",
            ),
            y == Bytes(
                "base16",
                "fa49ccd95795c7c9a447fdeee83a2193472507a4e41a47e0d50eeeb547b74c51",
            ),
        ))

    args = []
    app_result = PyTealDryRunExecutor(recover, Mode.Application).dryrun(
        args, compiler_version=5)

    assert app_result.stack_top() == 1, app_result.report(
        args,
        "stack_top() is not equal to 1, indicating ecdsa verification failed.")
Пример #3
0
def test_decompress():
    @Blackbox(input_types=[])
    @Subroutine(TealType.uint64)
    def decompress():
        return EcdsaDecompress(
            EcdsaCurve.Secp256k1,
            Bytes(
                "base16",
                "03bd83d54f6a799d05b496653b64bc933e17a898cda4793fe662d50645ecc977d1",
            ),
        ).outputReducer(lambda x, y: And(
            x == Bytes(
                "base16",
                "bd83d54f6a799d05b496653b64bc933e17a898cda4793fe662d50645ecc977d1",
            ),
            y == Bytes(
                "base16",
                "d4f3063a1ffca4139ea921b5696a6597640289175afece3bc38217a29d6270f9",
            ),
        ))

    args = []
    app_result = PyTealDryRunExecutor(decompress, Mode.Application).dryrun(
        args, compiler_version=5)

    assert app_result.stack_top() == 1, app_result.report(
        args,
        "stack_top() is not equal to 1, indicating ecdsa verification failed.")
Пример #4
0
def blackbox_pyteal_named_tupleness_test():
    from typing import Literal as L
    from tests.blackbox import Blackbox
    from pyteal import (
        Seq,
        abi,
        Subroutine,
        TealType,
        Return,
        And,
        Mode,
    )

    class NamedTupleExample(abi.NamedTuple):
        a: abi.Field[abi.Bool]
        b: abi.Field[abi.Address]
        c: abi.Field[abi.Tuple2[abi.Uint64, abi.Bool]]
        d: abi.Field[abi.StaticArray[abi.Byte, L[10]]]
        e: abi.Field[abi.StaticArray[abi.Bool, L[4]]]
        f: abi.Field[abi.Uint64]

    @Blackbox(input_types=[None] * 6)
    @Subroutine(TealType.uint64)
    def named_tuple_field_access(
        a_0: abi.Bool,
        a_1: abi.Address,
        a_2: abi.Tuple2[abi.Uint64, abi.Bool],
        a_3: abi.StaticArray[abi.Byte, L[10]],
        a_4: abi.StaticArray[abi.Bool, L[4]],
        a_5: abi.Uint64,
    ):
        return Seq(
            (v_tuple := NamedTupleExample()).set(a_0, a_1, a_2, a_3, a_4, a_5),
            (v_a := abi.Bool()).set(v_tuple.a),
            (v_b := abi.Address()).set(v_tuple.b),
            (v_c := abi.make(abi.Tuple2[abi.Uint64, abi.Bool])).set(v_tuple.c),
            (v_d := abi.make(abi.StaticArray[abi.Byte, L[10]])).set(v_tuple.d),
            (v_e := abi.make(abi.StaticArray[abi.Bool, L[4]])).set(v_tuple.e),
            (v_f := abi.Uint64()).set(v_tuple.f),
            Return(
                And(
                    a_0.get() == v_a.get(),
                    a_1.get() == v_b.get(),
                    a_2.encode() == v_c.encode(),
                    a_3.encode() == v_d.encode(),
                    a_4.encode() == v_e.encode(),
                    a_5.get() == v_f.get(),
                )),
        )

    lsig_pytealer = PyTealDryRunExecutor(named_tuple_field_access,
                                         Mode.Signature)
    args = (False, b"1" * 32, (0, False), b"0" * 10, [True] * 4, 0)

    inspector = lsig_pytealer.dryrun(args)

    assert inspector.stack_top() == 1
    assert inspector.passed()
Пример #5
0
def test_abi_blackbox_pyteal(subr_abi: Tuple[BlackboxWrapper,
                                             Optional[pt.ast.abi.BaseType]],
                             mode: pt.Mode):
    subr, abi_return_type = subr_abi
    name = f"{'app' if mode == pt.Mode.Application else 'lsig'}_{subr.name()}"
    print(f"Case {subr.name()=}, {abi_return_type=}, {mode=} ------> {name=}")

    pdre = PyTealDryRunExecutor(subr, mode)
    assert pdre.is_abi(), "should be an ABI subroutine"

    arg_types = pdre.abi_argument_types()
    if subr.name() != "fn_1tt_arg_uint64_ret":
        assert not arg_types or any(
            arg_types), "abi_argument_types() should have had some abi info"

    if abi_return_type:
        expected_sdk_return_type = pt.abi.algosdk_from_type_spec(
            abi_return_type.type_spec())
        assert expected_sdk_return_type == pdre.abi_return_type()
    else:
        assert pdre.abi_return_type() is None

    compiled = pdre.compile(version=6)
    tealdir = GENERATED / "abi"
    tealdir.mkdir(parents=True, exist_ok=True)
    save_to = tealdir / (name + ".teal")
    with open(save_to, "w") as f:
        f.write(compiled)

    assert_teal_as_expected(save_to, FIXTURES / "abi" / (name + ".teal"))
Пример #6
0
def blackbox_pyteal_example2():
    # Example 2: Using blackbox_pyteal to make 400 assertions and generate a CSV report with 400 dryrun rows
    from itertools import product
    import math
    from pathlib import Path
    import random

    from graviton.blackbox import DryRunInspector

    from pyteal import (
        For,
        If,
        Int,
        Mod,
        Mode,
        ScratchVar,
        Seq,
        Subroutine,
        TealType,
    )

    from tests.blackbox import Blackbox

    # GCD via the Euclidean Algorithm (iterative version):
    @Blackbox(input_types=[TealType.uint64, TealType.uint64])
    @Subroutine(TealType.uint64)
    def euclid(x, y):
        a = ScratchVar(TealType.uint64)
        b = ScratchVar(TealType.uint64)
        tmp = ScratchVar(TealType.uint64)
        start = If(x < y, Seq(a.store(y), b.store(x)),
                   Seq(a.store(x), b.store(y)))
        cond = b.load() > Int(0)
        step = Seq(tmp.store(b.load()), b.store(Mod(a.load(), b.load())),
                   a.store(tmp.load()))
        return Seq(For(start, cond, step).Do(Seq()), a.load())

    # generate a report with 400 = 20*20 dry run rows:
    N = 20
    inputs = list(
        product(
            tuple(random.randint(0, 1000) for _ in range(N)),
            tuple(random.randint(0, 1000) for _ in range(N)),
        ))

    # assert that each result is that same as what Python's math.gcd() computes
    inspectors = PyTealDryRunExecutor(
        euclid, Mode.Application).dryrun_on_sequence(inputs)
    for i, result in enumerate(inspectors):
        args = inputs[i]
        assert result.stack_top() == math.gcd(*args), result.report(
            args, f"failed for {args}")

    # save the CSV to ...current working directory.../euclid.csv
    euclid_csv = DryRunInspector.csv_report(inputs, inspectors)
    with open(Path.cwd() / "euclid.csv", "w") as f:
        f.write(euclid_csv)
Пример #7
0
def test_blackbox_pyteal(subr: BlackboxWrapper, mode: pt.Mode):
    is_app = mode == pt.Mode.Application
    name = f"{'app' if is_app else 'lsig'}_{subr.name()}"

    compiled = PyTealDryRunExecutor(subr, mode).compile(version=6)
    tealdir = GENERATED / "blackbox"
    tealdir.mkdir(parents=True, exist_ok=True)
    save_to = tealdir / (name + ".teal")
    with open(save_to, "w") as f:
        f.write(compiled)

    assert_teal_as_expected(save_to, FIXTURES / "blackbox" / (name + ".teal"))
Пример #8
0
def test_PyTealBlackboxExecutor_is_abi(mode: pt.Mode, fn: BlackboxWrapper,
                                       expected_is_abi: bool):
    p = PyTealDryRunExecutor(fn, mode)
    assert p.is_abi() == expected_is_abi
    if expected_is_abi:
        assert p.abi_argument_types() is not None
        assert p.abi_return_type() is not None
    else:
        assert p.abi_argument_types() is None
        assert p.abi_return_type() is None
Пример #9
0
def blackbox_pyteal_example5():
    from graviton.blackbox import DryRunEncoder

    from pyteal import abi, Subroutine, TealType, Int, Mode
    from tests.blackbox import Blackbox

    @Blackbox([None])
    @Subroutine(TealType.uint64)
    def cubed(n: abi.Uint64):
        return n.get()**Int(3)

    app_pytealer = PyTealDryRunExecutor(cubed, Mode.Application)
    lsig_pytealer = PyTealDryRunExecutor(cubed, Mode.Signature)

    inputs = [[i] for i in range(1, 11)]

    app_inspect = app_pytealer.dryrun_on_sequence(inputs)
    lsig_inspect = lsig_pytealer.dryrun_on_sequence(inputs)

    for index, inspect in enumerate(app_inspect):
        input_var = inputs[index][0]
        assert inspect.stack_top() == input_var**3, inspect.report(
            args=inputs[index],
            msg="stack_top() gave unexpected results from app")
        assert inspect.last_log() == DryRunEncoder.hex(
            input_var**3), inspect.report(
                args=inputs[index],
                msg="last_log() gave unexpected results from app")

    for index, inspect in enumerate(lsig_inspect):
        input_var = inputs[index][0]
        assert inspect.stack_top() == input_var**3, inspect.report(
            args=inputs[index],
            msg="stack_top() gave unexpected results from app")
Пример #10
0
def wrap_compile_and_save(subr, mode, version, assemble_constants, test_name,
                          case_name):
    is_app = mode == pt.Mode.Application

    teal = PyTealDryRunExecutor(subr, mode).compile(version,
                                                    assemble_constants)
    tealfile = f'{"app" if is_app else "lsig"}_{case_name}.teal'

    tealdir = GENERATED / test_name
    tealdir.mkdir(parents=True, exist_ok=True)
    tealpath = tealdir / tealfile
    with open(tealpath, "w") as f:
        f.write(teal)

    print(f"""Subroutine {case_name}@{mode} generated TEAL.
saved to {tealpath}:
-------
{teal}
-------""")

    return teal, is_app, tealfile
Пример #11
0
def blackbox_pyteal_while_continue_test():
    from tests.blackbox import Blackbox
    from pyteal import (
        Continue,
        Int,
        Mode,
        Return,
        ScratchVar,
        Seq,
        Subroutine,
        TealType,
        While,
    )

    @Blackbox(input_types=[TealType.uint64])
    @Subroutine(TealType.uint64)
    def while_continue_accumulation(n):
        i = ScratchVar(TealType.uint64)
        return Seq(
            i.store(Int(0)),
            While(i.load() < n).Do(
                Seq(
                    i.store(i.load() + Int(1)),
                    Continue(),
                )),
            Return(i.load()),
        )

    for x in range(30):
        args = [x]
        lsig_result = PyTealDryRunExecutor(while_continue_accumulation,
                                           Mode.Signature).dryrun(args)
        if x == 0:
            assert not lsig_result.passed()
        else:
            assert lsig_result.passed()

        assert lsig_result.stack_top() == x, lsig_result.report(
            args, "stack_top() gave unexpected results for lsig")
Пример #12
0
def test_verify():
    @Blackbox(input_types=[])
    @Subroutine(TealType.uint64)
    def verify():
        return EcdsaVerify(
            EcdsaCurve.Secp256k1,
            Sha512_256(Bytes("testdata")),
            Bytes(
                "base16",
                "33602297203d2753372cea7794ffe1756a278cbc4907b15a0dd132c9fb82555e",
            ),
            Bytes(
                "base16",
                "20f112126cf3e2eac6e8d4f97a403d21bab07b8dbb77154511bb7b07c0173195",
            ),
            (
                Bytes(
                    "base16",
                    "d6143a58c90c06b594e4414cb788659c2805e0056b1dfceea32c03f59efec517",
                ),
                Bytes(
                    "base16",
                    "00bd2400c479efe5ea556f37e1dc11ccb20f1e642dbfe00ca346fffeae508298",
                ),
            ),
        )

    args = []
    app_result = PyTealDryRunExecutor(verify, Mode.Application).dryrun(
        args, compiler_version=5)

    assert app_result.stack_top() == 1, app_result.report(
        args,
        "stack_top() is not equal to 1, indicating ecdsa verification failed.")

    @Blackbox(input_types=[])
    @Subroutine(TealType.uint64)
    def verify_fail():
        return EcdsaVerify(
            EcdsaCurve.Secp256k1,
            Sha512_256(Bytes("testdata")),
            Bytes(
                "base16",
                "13602297203d2753372cea7794ffe1756a278cbc4907b15a0dd132c9fb82555e",
            ),
            Bytes(
                "base16",
                "20f112126cf3e2eac6e8d4f97a403d21bab07b8dbb77154511bb7b07c0173195",
            ),
            (
                Bytes(
                    "base16",
                    "d6143a58c90c06b594e4414cb788659c2805e0056b1dfceea32c03f59efec517",
                ),
                Bytes(
                    "base16",
                    "00bd2400c479efe5ea556f37e1dc11ccb20f1e642dbfe00ca346fffeae508298",
                ),
            ),
        )

    args = []
    app_result = PyTealDryRunExecutor(verify_fail, Mode.Application).dryrun(
        args, compiler_version=5)

    assert app_result.stack_top() == 0, app_result.report(
        args,
        "stack_top() is not equal to 0, indicating ecdsa verification succeeded when a failure was expected.",
    )
Пример #13
0
def blackbox_pyteal_example3():
    # Example 3: declarative Test Driven Development approach through Invariant's
    from itertools import product
    import math
    import random

    from graviton.blackbox import (
        DryRunEncoder,
        DryRunProperty as DRProp,
    )
    from graviton.invariant import Invariant

    from pyteal import If, Int, Mod, Mode, Subroutine, TealType

    from tests.blackbox import Blackbox

    # avoid flaky tests just in case I was wrong about the stack height invariant...
    random.seed(42)

    # helper that will be used for scratch-slots invariant:
    def is_subdict(x, y):
        return all(k in y and x[k] == y[k] for k in x)

    predicates = {
        # the program's log should be the hex encoding of Python's math.gcd:
        DRProp.lastLog:
        lambda args: (DryRunEncoder.hex(math.gcd(*args))
                      if math.gcd(*args) else None),
        # the program's scratch should contain math.gcd() at slot 0:
        DRProp.finalScratch:
        lambda args, actual: is_subdict({0: math.gcd(*args)}, actual),
        # the top of the stack should be math.gcd():
        DRProp.stackTop:
        lambda args: math.gcd(*args),
        # Making the rather weak assertion that the max stack height is between 2 and 3*log2(max(args)):
        DRProp.maxStackHeight:
        (lambda args, actual: 2 <= actual <= 3 * math.ceil(
            math.log2(max(args + (1, ))))),
        # the program PASS'es exactly for non-0 math.gcd (3 variants):
        DRProp.status:
        lambda args: "PASS" if math.gcd(*args) else "REJECT",
        DRProp.passed:
        lambda args: bool(math.gcd(*args)),
        DRProp.rejected:
        lambda args: not bool(math.gcd(*args)),
        # the program never errors:
        DRProp.errorMessage:
        None,
    }

    # Define a scenario 400 random pairs (x,y) as inputs:
    N = 20
    inputs = list(
        product(
            tuple(random.randint(0, 1000) for _ in range(N)),
            tuple(random.randint(0, 1000) for _ in range(N)),
        ))

    # GCD via the Euclidean Algorithm (recursive version):
    @Blackbox(input_types=[TealType.uint64, TealType.uint64])
    @Subroutine(TealType.uint64)
    def euclid(x, y):
        return (If(x < y).Then(euclid(y, x)).Else(
            If(y == Int(0)).Then(x).Else(euclid(y, Mod(x, y)))))

    # Execute on the input sequence to get a dry-run inspectors:
    inspectors = PyTealDryRunExecutor(
        euclid, Mode.Application).dryrun_on_sequence(inputs)

    # Assert that each invariant holds on the sequences of inputs and dry-runs:
    for property, predicate in predicates.items():
        Invariant(predicate).validates(property, inputs, inspectors)
Пример #14
0
 def pytealer(self) -> PyTealDryRunExecutor:
     roundtrip = self.roundtrip_factory()
     return PyTealDryRunExecutor(roundtrip, pt.Mode.Application)
Пример #15
0
def test_PyTealBlackboxExecutor_abi_return_type(
        mode: pt.Mode, fn: BlackboxWrapper, expected_does_produce_type: bool):
    if expected_does_produce_type:
        assert PyTealDryRunExecutor(fn, mode).abi_return_type() is not None
    else:
        assert PyTealDryRunExecutor(fn, mode).abi_return_type() is None
Пример #16
0
def test_PyTealBlackboxExecutor_abi_argument_types(mode: pt.Mode,
                                                   fn: BlackboxWrapper,
                                                   expected_arg_count: int):
    actual = PyTealDryRunExecutor(fn, mode).abi_argument_types()
    assert actual is not None
    assert len(actual) == expected_arg_count
Пример #17
0
def blackbox_pyteal_example4():
    # Example 4: Using PyTealDryRunExecutor to debug an ABIReturnSubroutine with an app, logic sig and csv report
    from pathlib import Path
    import random

    from graviton.blackbox import DryRunInspector

    from pyteal import (
        abi,
        ABIReturnSubroutine,
        Expr,
        For,
        Int,
        Mode,
        ScratchVar,
        Seq,
        TealType,
    )

    from tests.blackbox import Blackbox, PyTealDryRunExecutor

    # Sum a dynamic uint64 array
    @Blackbox(input_types=[None])
    @ABIReturnSubroutine
    def abi_sum(toSum: abi.DynamicArray[abi.Uint64], *,
                output: abi.Uint64) -> Expr:
        i = ScratchVar(TealType.uint64)
        valueAtIndex = abi.Uint64()
        return Seq(
            output.set(0),
            For(
                i.store(Int(0)),
                i.load() < toSum.length(),
                i.store(i.load() + Int(1)),
            ).Do(
                Seq(
                    toSum[i.load()].store_into(valueAtIndex),
                    output.set(output.get() + valueAtIndex.get()),
                )),
        )

    # instantiate PyTealDryRunExecutor objects for the app and lsig:
    app_pytealer = PyTealDryRunExecutor(abi_sum, Mode.Application)
    lsig_pytealer = PyTealDryRunExecutor(abi_sum, Mode.Signature)

    # generate reports with the same random inputs (fix the randomness with a seed):
    random.seed(42)

    N = 50  # the number of dry runs for each experiment
    choices = range(10_000)
    inputs = []
    for n in range(N):
        inputs.append(tuple([random.sample(choices, n)]))

    app_inspectors = app_pytealer.dryrun_on_sequence(inputs)

    lsig_inspectors = lsig_pytealer.dryrun_on_sequence(inputs)

    for i in range(N):
        args = inputs[i]

        app_inspector = app_inspectors[i]
        lsig_inspector = lsig_inspectors[i]

        def message(insp):
            return insp.report(args, f"failed for {args}", row=i)

        # the app should pass exactly when it's cost was within the 700 budget:
        assert app_inspector.passed() == (app_inspector.cost() <=
                                          700), message(app_inspector)
        # the lsig always passes (never goes over budget):
        assert lsig_inspector.passed(), message(lsig_inspector)

        expected = sum(args[0])
        actual4app = app_inspector.last_log()
        assert expected == actual4app, message(app_inspector)

        if i > 0:
            assert expected in app_inspector.final_scratch().values(), message(
                app_inspector)
            assert expected in lsig_inspector.final_scratch().values(
            ), message(lsig_inspector)

    def report(kind):
        assert kind in ("app", "lsig")
        insps = app_inspectors if kind == "app" else lsig_inspectors
        csv_report = DryRunInspector.csv_report(inputs, insps)
        with open(Path.cwd() / f"abi_sum_{kind}.csv", "w") as f:
            f.write(csv_report)

    report("app")
    report("lsig")