Module hebi.tests.test_misc

Expand source code
import unittest

import frozendict
from hypothesis import given
from hypothesis import strategies as st
from parameterized import parameterized
from uplc import ast as uplc, eval as uplc_eval

from ..util import CompilerError
from .. import compiler, prelude


def fib(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a


class MiscTest(unittest.TestCase):
    def test_assert_sum_contract_succeed(self):
        input_file = "examples/smart_contracts/assert_sum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(20), uplc.PlutusInteger(22), uplc.BuiltinUnit()]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(ret, uplc.PlutusConstr(0, []))

    def test_assert_sum_contract_fail(self):
        input_file = "examples/smart_contracts/assert_sum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        try:
            f = code.term
            # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
            for d in [
                uplc.PlutusInteger(0),
                uplc.PlutusInteger(23),
                uplc.BuiltinUnit(),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    @given(
        a=st.integers(),
        b=st.integers(),
    )
    def test_sum(self, a: int, b: int):
        input_file = "examples/sum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(a), uplc.PlutusInteger(b)]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(ret, uplc.PlutusInteger(a + b))

    def test_complex_datum_correct_vals(self):
        input_file = "examples/complex_datum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.data_from_cbor(
                bytes.fromhex(
                    "d8799fd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd87a80d8799f1a38220b0bff1a001e84801a001e8480582051176daeee7f2ce62963c50a16f641951e21b8522da262980d4dd361a9bf331b4e4d7565736c69537761705f414d4dff"
                )
            )
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusByteString(
                bytes.fromhex(
                    "81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acf"
                )
            ),
            ret,
        )

    def test_hello_world(self):
        input_file = "examples/hello_world.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusConstr(0, [])]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)

    def test_list_datum_correct_vals(self):
        input_file = "examples/list_datum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.data_from_cbor(bytes.fromhex("d8799f9f41014102ffff"))]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusInteger(1),
            ret,
        )

    def test_showcase(self):
        input_file = "examples/showcase.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(1)]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusInteger(40),
            ret,
        )

    @given(n=st.integers(min_value=0, max_value=5))
    def test_fib_rec(self, n):
        input_file = "examples/fib_rec.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(n)]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusInteger(fib(n)),
            ret,
        )

    def test_gift_contract_succeed(self):
        input_file = "examples/smart_contracts/gift.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(
                0,
                [
                    uplc.PlutusByteString(
                        bytes.fromhex(
                            "dc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2"
                        )
                    )
                ],
            ),
            uplc.PlutusConstr(0, []),
            uplc.data_from_cbor(
                bytes.fromhex(
                    (
                        "d8799fd8799f9fd8799fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffd8799fd8799fd87a9f581cdbe769758f26efb21f008dc097bb194cffc622acc37fcefc5372eee3ffd87a80ffa140a1401a00989680d87a9f5820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dffd87a80ffffff809fd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a14000d87980d87a80ffd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a1401a000f4240d87980d87a80ffffa140a14000a140a1400080a0d8799fd8799fd87a9f1b000001836ac117d8ffd87a80ffd8799fd87b80d87a80ffff9f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffa1d87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffd87980a15820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd8799f5820c17c32f6433ae22c2acaebfb796bbfaee3993ff7ebb58a2bac6b4a3bdd2f6d28ffffd87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffff"
                    )
                )
            ),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(ret, uplc.PlutusConstr(0, []))

    def test_gift_contract_fail(self):
        input_file = "examples/smart_contracts/gift.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            # required sig missing int this script context
            for d in [
                uplc.PlutusConstr(
                    0,
                    [
                        uplc.PlutusByteString(
                            bytes.fromhex(
                                "dc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2"
                            )
                        )
                    ],
                ),
                uplc.PlutusConstr(0, []),
                uplc.data_from_cbor(
                    bytes.fromhex(
                        (
                            "d8799fd8799f9fd8799fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffd8799fd8799fd87a9f581cdbe769758f26efb21f008dc097bb194cffc622acc37fcefc5372eee3ffd87a80ffa140a1401a00989680d87a9f5820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dffd87a80ffffff809fd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a14000d87980d87a80ffffa140a14000a140a1400080a0d8799fd8799fd87a9f1b000001836ac117d8ffd87a80ffd8799fd87b80d87a80ffff80a1d87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffd87980a15820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd8799f5820797a1e1720b63621c6b185088184cb8e23af6e46b55bd83e7a91024c823a6c2affffd87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffff"
                        )
                    )
                ),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    def test_recursion(self):
        source_code = """
def validator(_: None) -> int:
    def a(n: int) -> int:
      if n == 0:
        res = 0
      else:
        res = a(n-1)
      return res
    b = a
    def a(x: int) -> int:
      return 100
    return b(1)
        """
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast)
            self.fail("Example compiled")
        except CompilerError:
            pass

    def test_datum_cast(self):
        input_file = "examples/datum_cast.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # Note that this passes even though we pass in a "wrong" datum - the cast only changes the type, it does not do any checks for correctness
        for d in [
            uplc.data_from_cbor(
                bytes.fromhex(
                    "d8799fd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd87a80d8799f1a38220b0bff1a001e84801a001e8480582051176daeee7f2ce62963c50a16f641951e21b8522da262980d4dd361a9bf331b4e4d7565736c69537761705f414d4dff"
                )
            ),
            uplc.PlutusByteString(b"test"),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusByteString(
                bytes.fromhex(
                    "81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acf"
                )
                + b"test"
            ),
            ret,
        )

    def test_wrapping_contract_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/wrapped_token.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast, force_three_params=True)
        code = code.compile()
        f = code.term

    def test_dual_use_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/dual_use.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast, force_three_params=True)
        code = code.compile()
        f = code.term

    def test_marketplace_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/marketplace.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term

    def test_marketplace_compile_fail(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/marketplace.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast, force_three_params=True)
            self.fail(
                "Allowed to compile an incompatible contract with three parameters"
            )
        except Exception:
            pass

    def test_parameterized_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/parameterized.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term

    def test_dict_datum(self):
        input_file = "examples/dict_datum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            # required sig missing int this script context
            for d in [
                uplc.PlutusConstr(
                    0,
                    [
                        uplc.PlutusMap(
                            frozendict.frozendict(
                                {
                                    uplc.PlutusConstr(
                                        0, [uplc.PlutusByteString(b"\x01")]
                                    ): 2
                                }
                            )
                        )
                    ],
                ),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    def test_overopt_removedeadvar(self):
        # this tests that errors that are caused by assignments are actually triggered at the time of assigning
        source_code = """
from hebi.prelude import *

def validator(x: Token) -> bool:
    a = x.policy_id
    return True
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            for d in [
                uplc.PlutusConstr(0, []),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    def test_list_expr(self):
        # this tests that the list expression is evaluated correctly
        source_code = """
def validator(x: None) -> List[int]:
    return [1, 2, 3, 4, 5]
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = [x.value for x in uplc_eval(f).value]
        self.assertEqual(ret, [1, 2, 3, 4, 5], "Machine did validate the content")

    def test_redefine_constr(self):
        # this tests that classes defined by assignment inherit constructors
        source_code = """
def validator(x: None) -> bytes:
    a = bytes
    return a([2, 3])
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f).value
        self.assertEqual(ret, bytes([2, 3]), "Machine did validate the content")

    def test_wrap_into_generic_data(self):
        # this tests that errors that are caused by assignments are actually triggered at the time of assigning
        source_code = """
from hebi.prelude import *
def validator(_: None) -> SomeOutputDatum:
    return SomeOutputDatum(b"a")
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            ret,
            uplc.data_from_cbor(
                prelude.SomeOutputDatum(b"a").to_cbor(encoding="bytes")
            ),
            "Machine did validate the content",
        )

    def test_list_comprehension_even(self):
        input_file = "examples/list_comprehensions.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusInteger(8),
            uplc.PlutusInteger(1),
        ]:
            f = uplc.Apply(f, d)
        ret = [x.value for x in uplc_eval(f).value]
        self.assertEqual(
            ret,
            [x * x for x in range(8) if x % 2 == 0],
            "List comprehension with filter incorrectly evaluated",
        )

    def test_list_comprehension_all(self):
        input_file = "examples/list_comprehensions.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusInteger(8),
            uplc.PlutusInteger(0),
        ]:
            f = uplc.Apply(f, d)
        ret = [x.value for x in uplc_eval(f).value]
        self.assertEqual(
            ret,
            [x * x for x in range(8)],
            "List comprehension incorrectly evaluated",
        )

    def test_union_type_attr_access_all_records(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatumHash
    
@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: SomeOutputDatum

def validator(x: Union[A, B]) -> Union[SomeOutputDatumHash, SomeOutputDatum]:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    @unittest.expectedFailure
    def test_union_type_all_records_same_constr(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatumHash

@dataclass()
class B(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatum

def validator(x: Union[A, B]) -> Union[SomeOutputDatumHash, SomeOutputDatum]:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    @unittest.expectedFailure
    def test_union_type_attr_access_all_records_same_constr(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: Token

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: Address

def validator(x: Union[A, B]) -> int:
    m = x.foo
    if isinstance(m, Address):
        k = 0
    else:
        k = 1
    return k
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_union_type_attr_access_maximum_type(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: int

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: int

def validator(x: Union[A, B]) -> int:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_union_type_attr_anytype(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: str

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: int

def validator(x: Union[A, B]) -> Anything:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_no_reassign(self):
        # this tests that variables can not be re-assigned
        source_code = """
def validator(x: int) -> int:
    x = 1
    return x
        """
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast)
            self.fail("Compilation passed")
        except CompilerError:
            pass

    def test_typecast_anything_int(self):
        source_code = """
def validator(x: Anything) -> int:
    b: int = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)

    def test_typecast_int_anything(self):
        # this should compile, it happens implicitly anyways when calling a function with Any parameters
        source_code = """
def validator(x: int) -> Anything:
    b: Anything = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)

    def test_typecast_int_anything_int(self):
        source_code = """
def validator(x: int) -> Anything:
    b: Anything = x
    c: int = b
    return c + 1
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 1)

    def test_typecast_anything_int_anything(self):
        source_code = """
def validator(x: Anything) -> Anything:
    b: int = x
    c: Anything = b + 1
    return c
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 1)

    @unittest.expectedFailure
    def test_typecast_int_str(self):
        # this should compile, the two types are unrelated and there is no meaningful way to cast them either direction
        source_code = """
def validator(x: int) -> str:
    b: str = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_typecast_int_int(self):
        source_code = """
def validator(x: int) -> int:
    b: int = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)

    def test_zero_ary(self):
        source_code = """
def a() -> None:
    assert False, "Executed a"

def validator(x: None) -> None:
    b = a
    if False:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    @unittest.expectedFailure
    def test_zero_ary_exec(self):
        source_code = """
def a() -> None:
    assert False, "Executed a"

def validator(x: None) -> None:
    b = a
    if True:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    def test_zero_ary_method(self):
        source_code = """
def validator(x: None) -> None:
    b = b"\\xFF".decode
    if False:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    @unittest.expectedFailure
    def test_zero_ary_method_exec(self):
        source_code = """
def validator(x: None) -> None:
    b = b"\\xFF".decode
    if True:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    def test_return_anything(self):
        source_code = """
from hebi.prelude import *

def validator() -> Anything:
    return b""
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusByteString(b""))

    def test_no_return_annotation(self):
        source_code = """
from hebi.prelude import *

def validator():
    return b""
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusByteString(b""))

    def test_no_parameter_annotation(self):
        source_code = """
from hebi.prelude import *

def validator(a) -> bytes:
    b: bytes = a
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusByteString(b"")))
        self.assertEqual(res, uplc.PlutusByteString(b""))

    def test_nested_deconstruction(self):
        source_code = """
def validator(xs) -> int:
    a, ((b, c), d) = (1, ((2, 3), 4))
    return a + b + c + d
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        f = uplc.Apply(f, uplc.PlutusConstr(0, []))
        ret = uplc_eval(f).value
        self.assertEqual(
            ret,
            1 + 2 + 3 + 4,
            "for loop deconstruction did not behave as expected",
        )

    def test_different_return_types_anything(self):
        source_code = """
from hebi.prelude import *

def validator(a: int) -> Anything:
    if a > 0:
        return b""
    else:
        return 0
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(1)))
        self.assertEqual(res, uplc.PlutusByteString(b""))
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(-1)))
        self.assertEqual(res, uplc.PlutusInteger(0))

    def test_no_return_annotation_no_return(self):
        source_code = """
from hebi.prelude import *

def validator(a):
    pass
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusConstr(0, []))

    def test_opt_unsafe_cast(self):
        # test that unsafe casts are not optimized away
        source_code = """
from hebi.prelude import *

def validator(x: Token) -> bool:
    b: Anything = x
    a: int = b
    return True
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            for d in [
                uplc.PlutusConstr(0, []),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

Functions

def fib(n)
Expand source code
def fib(n):
    a, b = 0, 1
    for _ in range(n):
        a, b = b, a + b
    return a

Classes

class MiscTest (methodName='runTest')

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the init method, the base class init method must always be called. It is important that subclasses should not change the signature of their init method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes: * failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'. * longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed. * maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.

Create an instance of the class that will use the named test method when executed. Raises a ValueError if the instance does not have a method with the specified name.

Expand source code
class MiscTest(unittest.TestCase):
    def test_assert_sum_contract_succeed(self):
        input_file = "examples/smart_contracts/assert_sum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(20), uplc.PlutusInteger(22), uplc.BuiltinUnit()]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(ret, uplc.PlutusConstr(0, []))

    def test_assert_sum_contract_fail(self):
        input_file = "examples/smart_contracts/assert_sum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        try:
            f = code.term
            # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
            for d in [
                uplc.PlutusInteger(0),
                uplc.PlutusInteger(23),
                uplc.BuiltinUnit(),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    @given(
        a=st.integers(),
        b=st.integers(),
    )
    def test_sum(self, a: int, b: int):
        input_file = "examples/sum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(a), uplc.PlutusInteger(b)]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(ret, uplc.PlutusInteger(a + b))

    def test_complex_datum_correct_vals(self):
        input_file = "examples/complex_datum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.data_from_cbor(
                bytes.fromhex(
                    "d8799fd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd87a80d8799f1a38220b0bff1a001e84801a001e8480582051176daeee7f2ce62963c50a16f641951e21b8522da262980d4dd361a9bf331b4e4d7565736c69537761705f414d4dff"
                )
            )
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusByteString(
                bytes.fromhex(
                    "81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acf"
                )
            ),
            ret,
        )

    def test_hello_world(self):
        input_file = "examples/hello_world.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusConstr(0, [])]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)

    def test_list_datum_correct_vals(self):
        input_file = "examples/list_datum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.data_from_cbor(bytes.fromhex("d8799f9f41014102ffff"))]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusInteger(1),
            ret,
        )

    def test_showcase(self):
        input_file = "examples/showcase.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(1)]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusInteger(40),
            ret,
        )

    @given(n=st.integers(min_value=0, max_value=5))
    def test_fib_rec(self, n):
        input_file = "examples/fib_rec.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [uplc.PlutusInteger(n)]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusInteger(fib(n)),
            ret,
        )

    def test_gift_contract_succeed(self):
        input_file = "examples/smart_contracts/gift.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(
                0,
                [
                    uplc.PlutusByteString(
                        bytes.fromhex(
                            "dc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2"
                        )
                    )
                ],
            ),
            uplc.PlutusConstr(0, []),
            uplc.data_from_cbor(
                bytes.fromhex(
                    (
                        "d8799fd8799f9fd8799fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffd8799fd8799fd87a9f581cdbe769758f26efb21f008dc097bb194cffc622acc37fcefc5372eee3ffd87a80ffa140a1401a00989680d87a9f5820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dffd87a80ffffff809fd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a14000d87980d87a80ffd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a1401a000f4240d87980d87a80ffffa140a14000a140a1400080a0d8799fd8799fd87a9f1b000001836ac117d8ffd87a80ffd8799fd87b80d87a80ffff9f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffa1d87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffd87980a15820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd8799f5820c17c32f6433ae22c2acaebfb796bbfaee3993ff7ebb58a2bac6b4a3bdd2f6d28ffffd87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffff"
                    )
                )
            ),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(ret, uplc.PlutusConstr(0, []))

    def test_gift_contract_fail(self):
        input_file = "examples/smart_contracts/gift.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            # required sig missing int this script context
            for d in [
                uplc.PlutusConstr(
                    0,
                    [
                        uplc.PlutusByteString(
                            bytes.fromhex(
                                "dc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2"
                            )
                        )
                    ],
                ),
                uplc.PlutusConstr(0, []),
                uplc.data_from_cbor(
                    bytes.fromhex(
                        (
                            "d8799fd8799f9fd8799fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffd8799fd8799fd87a9f581cdbe769758f26efb21f008dc097bb194cffc622acc37fcefc5372eee3ffd87a80ffa140a1401a00989680d87a9f5820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dffd87a80ffffff809fd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a14000d87980d87a80ffffa140a14000a140a1400080a0d8799fd8799fd87a9f1b000001836ac117d8ffd87a80ffd8799fd87b80d87a80ffff80a1d87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffd87980a15820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd8799f5820797a1e1720b63621c6b185088184cb8e23af6e46b55bd83e7a91024c823a6c2affffd87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffff"
                        )
                    )
                ),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    def test_recursion(self):
        source_code = """
def validator(_: None) -> int:
    def a(n: int) -> int:
      if n == 0:
        res = 0
      else:
        res = a(n-1)
      return res
    b = a
    def a(x: int) -> int:
      return 100
    return b(1)
        """
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast)
            self.fail("Example compiled")
        except CompilerError:
            pass

    def test_datum_cast(self):
        input_file = "examples/datum_cast.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # Note that this passes even though we pass in a "wrong" datum - the cast only changes the type, it does not do any checks for correctness
        for d in [
            uplc.data_from_cbor(
                bytes.fromhex(
                    "d8799fd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd87a80d8799f1a38220b0bff1a001e84801a001e8480582051176daeee7f2ce62963c50a16f641951e21b8522da262980d4dd361a9bf331b4e4d7565736c69537761705f414d4dff"
                )
            ),
            uplc.PlutusByteString(b"test"),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            uplc.PlutusByteString(
                bytes.fromhex(
                    "81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acf"
                )
                + b"test"
            ),
            ret,
        )

    def test_wrapping_contract_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/wrapped_token.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast, force_three_params=True)
        code = code.compile()
        f = code.term

    def test_dual_use_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/dual_use.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast, force_three_params=True)
        code = code.compile()
        f = code.term

    def test_marketplace_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/marketplace.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term

    def test_marketplace_compile_fail(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/marketplace.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast, force_three_params=True)
            self.fail(
                "Allowed to compile an incompatible contract with three parameters"
            )
        except Exception:
            pass

    def test_parameterized_compile(self):
        # TODO devise tests for this
        input_file = "examples/smart_contracts/parameterized.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term

    def test_dict_datum(self):
        input_file = "examples/dict_datum.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            # required sig missing int this script context
            for d in [
                uplc.PlutusConstr(
                    0,
                    [
                        uplc.PlutusMap(
                            frozendict.frozendict(
                                {
                                    uplc.PlutusConstr(
                                        0, [uplc.PlutusByteString(b"\x01")]
                                    ): 2
                                }
                            )
                        )
                    ],
                ),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    def test_overopt_removedeadvar(self):
        # this tests that errors that are caused by assignments are actually triggered at the time of assigning
        source_code = """
from hebi.prelude import *

def validator(x: Token) -> bool:
    a = x.policy_id
    return True
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            for d in [
                uplc.PlutusConstr(0, []),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

    def test_list_expr(self):
        # this tests that the list expression is evaluated correctly
        source_code = """
def validator(x: None) -> List[int]:
    return [1, 2, 3, 4, 5]
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = [x.value for x in uplc_eval(f).value]
        self.assertEqual(ret, [1, 2, 3, 4, 5], "Machine did validate the content")

    def test_redefine_constr(self):
        # this tests that classes defined by assignment inherit constructors
        source_code = """
def validator(x: None) -> bytes:
    a = bytes
    return a([2, 3])
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f).value
        self.assertEqual(ret, bytes([2, 3]), "Machine did validate the content")

    def test_wrap_into_generic_data(self):
        # this tests that errors that are caused by assignments are actually triggered at the time of assigning
        source_code = """
from hebi.prelude import *
def validator(_: None) -> SomeOutputDatum:
    return SomeOutputDatum(b"a")
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            ret,
            uplc.data_from_cbor(
                prelude.SomeOutputDatum(b"a").to_cbor(encoding="bytes")
            ),
            "Machine did validate the content",
        )

    def test_list_comprehension_even(self):
        input_file = "examples/list_comprehensions.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusInteger(8),
            uplc.PlutusInteger(1),
        ]:
            f = uplc.Apply(f, d)
        ret = [x.value for x in uplc_eval(f).value]
        self.assertEqual(
            ret,
            [x * x for x in range(8) if x % 2 == 0],
            "List comprehension with filter incorrectly evaluated",
        )

    def test_list_comprehension_all(self):
        input_file = "examples/list_comprehensions.py"
        with open(input_file) as fp:
            source_code = fp.read()
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusInteger(8),
            uplc.PlutusInteger(0),
        ]:
            f = uplc.Apply(f, d)
        ret = [x.value for x in uplc_eval(f).value]
        self.assertEqual(
            ret,
            [x * x for x in range(8)],
            "List comprehension incorrectly evaluated",
        )

    def test_union_type_attr_access_all_records(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatumHash
    
@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: SomeOutputDatum

def validator(x: Union[A, B]) -> Union[SomeOutputDatumHash, SomeOutputDatum]:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    @unittest.expectedFailure
    def test_union_type_all_records_same_constr(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatumHash

@dataclass()
class B(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatum

def validator(x: Union[A, B]) -> Union[SomeOutputDatumHash, SomeOutputDatum]:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    @unittest.expectedFailure
    def test_union_type_attr_access_all_records_same_constr(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: Token

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: Address

def validator(x: Union[A, B]) -> int:
    m = x.foo
    if isinstance(m, Address):
        k = 0
    else:
        k = 1
    return k
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_union_type_attr_access_maximum_type(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: int

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: int

def validator(x: Union[A, B]) -> int:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_union_type_attr_anytype(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: str

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: int

def validator(x: Union[A, B]) -> Anything:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_no_reassign(self):
        # this tests that variables can not be re-assigned
        source_code = """
def validator(x: int) -> int:
    x = 1
    return x
        """
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast)
            self.fail("Compilation passed")
        except CompilerError:
            pass

    def test_typecast_anything_int(self):
        source_code = """
def validator(x: Anything) -> int:
    b: int = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)

    def test_typecast_int_anything(self):
        # this should compile, it happens implicitly anyways when calling a function with Any parameters
        source_code = """
def validator(x: int) -> Anything:
    b: Anything = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)

    def test_typecast_int_anything_int(self):
        source_code = """
def validator(x: int) -> Anything:
    b: Anything = x
    c: int = b
    return c + 1
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 1)

    def test_typecast_anything_int_anything(self):
        source_code = """
def validator(x: Anything) -> Anything:
    b: int = x
    c: Anything = b + 1
    return c
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 1)

    @unittest.expectedFailure
    def test_typecast_int_str(self):
        # this should compile, the two types are unrelated and there is no meaningful way to cast them either direction
        source_code = """
def validator(x: int) -> str:
    b: str = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)

    def test_typecast_int_int(self):
        source_code = """
def validator(x: int) -> int:
    b: int = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)

    def test_zero_ary(self):
        source_code = """
def a() -> None:
    assert False, "Executed a"

def validator(x: None) -> None:
    b = a
    if False:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    @unittest.expectedFailure
    def test_zero_ary_exec(self):
        source_code = """
def a() -> None:
    assert False, "Executed a"

def validator(x: None) -> None:
    b = a
    if True:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    def test_zero_ary_method(self):
        source_code = """
def validator(x: None) -> None:
    b = b"\\xFF".decode
    if False:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    @unittest.expectedFailure
    def test_zero_ary_method_exec(self):
        source_code = """
def validator(x: None) -> None:
    b = b"\\xFF".decode
    if True:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))

    def test_return_anything(self):
        source_code = """
from hebi.prelude import *

def validator() -> Anything:
    return b""
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusByteString(b""))

    def test_no_return_annotation(self):
        source_code = """
from hebi.prelude import *

def validator():
    return b""
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusByteString(b""))

    def test_no_parameter_annotation(self):
        source_code = """
from hebi.prelude import *

def validator(a) -> bytes:
    b: bytes = a
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusByteString(b"")))
        self.assertEqual(res, uplc.PlutusByteString(b""))

    def test_nested_deconstruction(self):
        source_code = """
def validator(xs) -> int:
    a, ((b, c), d) = (1, ((2, 3), 4))
    return a + b + c + d
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        f = uplc.Apply(f, uplc.PlutusConstr(0, []))
        ret = uplc_eval(f).value
        self.assertEqual(
            ret,
            1 + 2 + 3 + 4,
            "for loop deconstruction did not behave as expected",
        )

    def test_different_return_types_anything(self):
        source_code = """
from hebi.prelude import *

def validator(a: int) -> Anything:
    if a > 0:
        return b""
    else:
        return 0
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(1)))
        self.assertEqual(res, uplc.PlutusByteString(b""))
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(-1)))
        self.assertEqual(res, uplc.PlutusInteger(0))

    def test_no_return_annotation_no_return(self):
        source_code = """
from hebi.prelude import *

def validator(a):
    pass
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusConstr(0, []))

    def test_opt_unsafe_cast(self):
        # test that unsafe casts are not optimized away
        source_code = """
from hebi.prelude import *

def validator(x: Token) -> bool:
    b: Anything = x
    a: int = b
    return True
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            for d in [
                uplc.PlutusConstr(0, []),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")

Ancestors

  • unittest.case.TestCase

Methods

def test_assert_sum_contract_fail(self)
Expand source code
def test_assert_sum_contract_fail(self):
    input_file = "examples/smart_contracts/assert_sum.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    try:
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusInteger(0),
            uplc.PlutusInteger(23),
            uplc.BuiltinUnit(),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        failed = False
    except Exception as e:
        failed = True
    self.assertTrue(failed, "Machine did validate the content")
def test_assert_sum_contract_succeed(self)
Expand source code
def test_assert_sum_contract_succeed(self):
    input_file = "examples/smart_contracts/assert_sum.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [uplc.PlutusInteger(20), uplc.PlutusInteger(22), uplc.BuiltinUnit()]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(ret, uplc.PlutusConstr(0, []))
def test_complex_datum_correct_vals(self)
Expand source code
def test_complex_datum_correct_vals(self):
    input_file = "examples/complex_datum.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [
        uplc.data_from_cbor(
            bytes.fromhex(
                "d8799fd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd87a80d8799f1a38220b0bff1a001e84801a001e8480582051176daeee7f2ce62963c50a16f641951e21b8522da262980d4dd361a9bf331b4e4d7565736c69537761705f414d4dff"
            )
        )
    ]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(
        uplc.PlutusByteString(
            bytes.fromhex(
                "81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acf"
            )
        ),
        ret,
    )
def test_datum_cast(self)
Expand source code
def test_datum_cast(self):
    input_file = "examples/datum_cast.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # Note that this passes even though we pass in a "wrong" datum - the cast only changes the type, it does not do any checks for correctness
    for d in [
        uplc.data_from_cbor(
            bytes.fromhex(
                "d8799fd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd8799fd8799f581c81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acfffd8799fd8799fd8799f581c145db8343296bd214dde862a64d700c29ed8a71d58bcf865659f5463ffffffffd87a80d8799f1a38220b0bff1a001e84801a001e8480582051176daeee7f2ce62963c50a16f641951e21b8522da262980d4dd361a9bf331b4e4d7565736c69537761705f414d4dff"
            )
        ),
        uplc.PlutusByteString(b"test"),
    ]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(
        uplc.PlutusByteString(
            bytes.fromhex(
                "81aab0790f33d26bad68a6a13ae98562aa1366da48cdce20dec21acf"
            )
            + b"test"
        ),
        ret,
    )
def test_dict_datum(self)
Expand source code
def test_dict_datum(self):
    input_file = "examples/dict_datum.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    try:
        # required sig missing int this script context
        for d in [
            uplc.PlutusConstr(
                0,
                [
                    uplc.PlutusMap(
                        frozendict.frozendict(
                            {
                                uplc.PlutusConstr(
                                    0, [uplc.PlutusByteString(b"\x01")]
                                ): 2
                            }
                        )
                    )
                ],
            ),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        failed = False
    except Exception as e:
        failed = True
    self.assertTrue(failed, "Machine did validate the content")
def test_different_return_types_anything(self)
Expand source code
    def test_different_return_types_anything(self):
        source_code = """
from hebi.prelude import *

def validator(a: int) -> Anything:
    if a > 0:
        return b""
    else:
        return 0
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(1)))
        self.assertEqual(res, uplc.PlutusByteString(b""))
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(-1)))
        self.assertEqual(res, uplc.PlutusInteger(0))
def test_dual_use_compile(self)
Expand source code
def test_dual_use_compile(self):
    # TODO devise tests for this
    input_file = "examples/smart_contracts/dual_use.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast, force_three_params=True)
    code = code.compile()
    f = code.term
def test_fib_rec(self) ‑> None
Expand source code
@given(n=st.integers(min_value=0, max_value=5))
def test_fib_rec(self, n):
    input_file = "examples/fib_rec.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [uplc.PlutusInteger(n)]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(
        uplc.PlutusInteger(fib(n)),
        ret,
    )
def test_gift_contract_fail(self)
Expand source code
def test_gift_contract_fail(self):
    input_file = "examples/smart_contracts/gift.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    try:
        # required sig missing int this script context
        for d in [
            uplc.PlutusConstr(
                0,
                [
                    uplc.PlutusByteString(
                        bytes.fromhex(
                            "dc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2"
                        )
                    )
                ],
            ),
            uplc.PlutusConstr(0, []),
            uplc.data_from_cbor(
                bytes.fromhex(
                    (
                        "d8799fd8799f9fd8799fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffd8799fd8799fd87a9f581cdbe769758f26efb21f008dc097bb194cffc622acc37fcefc5372eee3ffd87a80ffa140a1401a00989680d87a9f5820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dffd87a80ffffff809fd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a14000d87980d87a80ffffa140a14000a140a1400080a0d8799fd8799fd87a9f1b000001836ac117d8ffd87a80ffd8799fd87b80d87a80ffff80a1d87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffd87980a15820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd8799f5820797a1e1720b63621c6b185088184cb8e23af6e46b55bd83e7a91024c823a6c2affffd87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffff"
                    )
                )
            ),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        failed = False
    except Exception as e:
        failed = True
    self.assertTrue(failed, "Machine did validate the content")
def test_gift_contract_succeed(self)
Expand source code
def test_gift_contract_succeed(self):
    input_file = "examples/smart_contracts/gift.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [
        uplc.PlutusConstr(
            0,
            [
                uplc.PlutusByteString(
                    bytes.fromhex(
                        "dc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2"
                    )
                )
            ],
        ),
        uplc.PlutusConstr(0, []),
        uplc.data_from_cbor(
            bytes.fromhex(
                (
                    "d8799fd8799f9fd8799fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffd8799fd8799fd87a9f581cdbe769758f26efb21f008dc097bb194cffc622acc37fcefc5372eee3ffd87a80ffa140a1401a00989680d87a9f5820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dffd87a80ffffff809fd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a14000d87980d87a80ffd8799fd8799fd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd87a80ffa140a1401a000f4240d87980d87a80ffffa140a14000a140a1400080a0d8799fd8799fd87a9f1b000001836ac117d8ffd87a80ffd8799fd87b80d87a80ffff9f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffa1d87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffd87980a15820dfab81872ce2bbe6ee5af9bbfee4047f91c1f57db5e30da727d5fef1e7f02f4dd8799f581cdc315c289fee4484eda07038393f21dc4e572aff292d7926018725c2ffd8799f5820c17c32f6433ae22c2acaebfb796bbfaee3993ff7ebb58a2bac6b4a3bdd2f6d28ffffd87a9fd8799fd8799f582055d353acacaab6460b37ed0f0e3a1a0aabf056df4a7fa1e265d21149ccacc527ff01ffffff"
                )
            )
        ),
    ]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(ret, uplc.PlutusConstr(0, []))
def test_hello_world(self)
Expand source code
def test_hello_world(self):
    input_file = "examples/hello_world.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [uplc.PlutusConstr(0, [])]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
def test_list_comprehension_all(self)
Expand source code
def test_list_comprehension_all(self):
    input_file = "examples/list_comprehensions.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [
        uplc.PlutusInteger(8),
        uplc.PlutusInteger(0),
    ]:
        f = uplc.Apply(f, d)
    ret = [x.value for x in uplc_eval(f).value]
    self.assertEqual(
        ret,
        [x * x for x in range(8)],
        "List comprehension incorrectly evaluated",
    )
def test_list_comprehension_even(self)
Expand source code
def test_list_comprehension_even(self):
    input_file = "examples/list_comprehensions.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [
        uplc.PlutusInteger(8),
        uplc.PlutusInteger(1),
    ]:
        f = uplc.Apply(f, d)
    ret = [x.value for x in uplc_eval(f).value]
    self.assertEqual(
        ret,
        [x * x for x in range(8) if x % 2 == 0],
        "List comprehension with filter incorrectly evaluated",
    )
def test_list_datum_correct_vals(self)
Expand source code
def test_list_datum_correct_vals(self):
    input_file = "examples/list_datum.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [uplc.data_from_cbor(bytes.fromhex("d8799f9f41014102ffff"))]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(
        uplc.PlutusInteger(1),
        ret,
    )
def test_list_expr(self)
Expand source code
    def test_list_expr(self):
        # this tests that the list expression is evaluated correctly
        source_code = """
def validator(x: None) -> List[int]:
    return [1, 2, 3, 4, 5]
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = [x.value for x in uplc_eval(f).value]
        self.assertEqual(ret, [1, 2, 3, 4, 5], "Machine did validate the content")
def test_marketplace_compile(self)
Expand source code
def test_marketplace_compile(self):
    # TODO devise tests for this
    input_file = "examples/smart_contracts/marketplace.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
def test_marketplace_compile_fail(self)
Expand source code
def test_marketplace_compile_fail(self):
    # TODO devise tests for this
    input_file = "examples/smart_contracts/marketplace.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    try:
        code = compiler.compile(ast, force_three_params=True)
        self.fail(
            "Allowed to compile an incompatible contract with three parameters"
        )
    except Exception:
        pass
def test_nested_deconstruction(self)
Expand source code
    def test_nested_deconstruction(self):
        source_code = """
def validator(xs) -> int:
    a, ((b, c), d) = (1, ((2, 3), 4))
    return a + b + c + d
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        f = uplc.Apply(f, uplc.PlutusConstr(0, []))
        ret = uplc_eval(f).value
        self.assertEqual(
            ret,
            1 + 2 + 3 + 4,
            "for loop deconstruction did not behave as expected",
        )
def test_no_parameter_annotation(self)
Expand source code
    def test_no_parameter_annotation(self):
        source_code = """
from hebi.prelude import *

def validator(a) -> bytes:
    b: bytes = a
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusByteString(b"")))
        self.assertEqual(res, uplc.PlutusByteString(b""))
def test_no_reassign(self)
Expand source code
    def test_no_reassign(self):
        # this tests that variables can not be re-assigned
        source_code = """
def validator(x: int) -> int:
    x = 1
    return x
        """
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast)
            self.fail("Compilation passed")
        except CompilerError:
            pass
def test_no_return_annotation(self)
Expand source code
    def test_no_return_annotation(self):
        source_code = """
from hebi.prelude import *

def validator():
    return b""
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusByteString(b""))
def test_no_return_annotation_no_return(self)
Expand source code
    def test_no_return_annotation_no_return(self):
        source_code = """
from hebi.prelude import *

def validator(a):
    pass
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusConstr(0, []))
def test_opt_unsafe_cast(self)
Expand source code
    def test_opt_unsafe_cast(self):
        # test that unsafe casts are not optimized away
        source_code = """
from hebi.prelude import *

def validator(x: Token) -> bool:
    b: Anything = x
    a: int = b
    return True
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            for d in [
                uplc.PlutusConstr(0, []),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")
def test_overopt_removedeadvar(self)
Expand source code
    def test_overopt_removedeadvar(self):
        # this tests that errors that are caused by assignments are actually triggered at the time of assigning
        source_code = """
from hebi.prelude import *

def validator(x: Token) -> bool:
    a = x.policy_id
    return True
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        try:
            for d in [
                uplc.PlutusConstr(0, []),
            ]:
                f = uplc.Apply(f, d)
            ret = uplc_eval(f)
            failed = False
        except Exception as e:
            failed = True
        self.assertTrue(failed, "Machine did validate the content")
def test_parameterized_compile(self)
Expand source code
def test_parameterized_compile(self):
    # TODO devise tests for this
    input_file = "examples/smart_contracts/parameterized.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
def test_recursion(self)
Expand source code
    def test_recursion(self):
        source_code = """
def validator(_: None) -> int:
    def a(n: int) -> int:
      if n == 0:
        res = 0
      else:
        res = a(n-1)
      return res
    b = a
    def a(x: int) -> int:
      return 100
    return b(1)
        """
        ast = compiler.parse(source_code)
        try:
            code = compiler.compile(ast)
            self.fail("Example compiled")
        except CompilerError:
            pass
def test_redefine_constr(self)
Expand source code
    def test_redefine_constr(self):
        # this tests that classes defined by assignment inherit constructors
        source_code = """
def validator(x: None) -> bytes:
    a = bytes
    return a([2, 3])
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f).value
        self.assertEqual(ret, bytes([2, 3]), "Machine did validate the content")
def test_return_anything(self)
Expand source code
    def test_return_anything(self):
        source_code = """
from hebi.prelude import *

def validator() -> Anything:
    return b""
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusConstr(0, [])))
        self.assertEqual(res, uplc.PlutusByteString(b""))
def test_showcase(self)
Expand source code
def test_showcase(self):
    input_file = "examples/showcase.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [uplc.PlutusInteger(1)]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(
        uplc.PlutusInteger(40),
        ret,
    )
def test_sum(self) ‑> None
Expand source code
@given(
    a=st.integers(),
    b=st.integers(),
)
def test_sum(self, a: int, b: int):
    input_file = "examples/sum.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast)
    code = code.compile()
    f = code.term
    # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
    for d in [uplc.PlutusInteger(a), uplc.PlutusInteger(b)]:
        f = uplc.Apply(f, d)
    ret = uplc_eval(f)
    self.assertEqual(ret, uplc.PlutusInteger(a + b))
def test_typecast_anything_int(self)
Expand source code
    def test_typecast_anything_int(self):
        source_code = """
def validator(x: Anything) -> int:
    b: int = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)
def test_typecast_anything_int_anything(self)
Expand source code
    def test_typecast_anything_int_anything(self):
        source_code = """
def validator(x: Anything) -> Anything:
    b: int = x
    c: Anything = b + 1
    return c
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 1)
def test_typecast_int_anything(self)
Expand source code
    def test_typecast_int_anything(self):
        # this should compile, it happens implicitly anyways when calling a function with Any parameters
        source_code = """
def validator(x: int) -> Anything:
    b: Anything = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)
def test_typecast_int_anything_int(self)
Expand source code
    def test_typecast_int_anything_int(self):
        source_code = """
def validator(x: int) -> Anything:
    b: Anything = x
    c: int = b
    return c + 1
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 1)
def test_typecast_int_int(self)
Expand source code
    def test_typecast_int_int(self):
        source_code = """
def validator(x: int) -> int:
    b: int = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0))).value
        self.assertEqual(res, 0)
def test_typecast_int_str(self)
Expand source code
    @unittest.expectedFailure
    def test_typecast_int_str(self):
        # this should compile, the two types are unrelated and there is no meaningful way to cast them either direction
        source_code = """
def validator(x: int) -> str:
    b: str = x
    return b
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
def test_union_type_all_records_same_constr(self)
Expand source code
    @unittest.expectedFailure
    def test_union_type_all_records_same_constr(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatumHash

@dataclass()
class B(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatum

def validator(x: Union[A, B]) -> Union[SomeOutputDatumHash, SomeOutputDatum]:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
def test_union_type_attr_access_all_records(self)
Expand source code
    def test_union_type_attr_access_all_records(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: SomeOutputDatumHash
    
@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: SomeOutputDatum

def validator(x: Union[A, B]) -> Union[SomeOutputDatumHash, SomeOutputDatum]:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
def test_union_type_attr_access_all_records_same_constr(self)
Expand source code
    @unittest.expectedFailure
    def test_union_type_attr_access_all_records_same_constr(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: Token

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: Address

def validator(x: Union[A, B]) -> int:
    m = x.foo
    if isinstance(m, Address):
        k = 0
    else:
        k = 1
    return k
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
def test_union_type_attr_access_maximum_type(self)
Expand source code
    def test_union_type_attr_access_maximum_type(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: int

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: int

def validator(x: Union[A, B]) -> int:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
def test_union_type_attr_anytype(self)
Expand source code
    def test_union_type_attr_anytype(self):
        source_code = """
from hebi.prelude import *

@dataclass()
class A(PlutusData):
    CONSTR_ID = 0
    foo: str

@dataclass()
class B(PlutusData):
    CONSTR_ID = 1
    foo: int

def validator(x: Union[A, B]) -> Anything:
    return x.foo
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
def test_wrap_into_generic_data(self)
Expand source code
    def test_wrap_into_generic_data(self):
        # this tests that errors that are caused by assignments are actually triggered at the time of assigning
        source_code = """
from hebi.prelude import *
def validator(_: None) -> SomeOutputDatum:
    return SomeOutputDatum(b"a")
        """
        ast = compiler.parse(source_code)
        code = compiler.compile(ast)
        code = code.compile()
        f = code.term
        # UPLC lambdas may only take one argument at a time, so we evaluate by repeatedly applying
        for d in [
            uplc.PlutusConstr(0, []),
        ]:
            f = uplc.Apply(f, d)
        ret = uplc_eval(f)
        self.assertEqual(
            ret,
            uplc.data_from_cbor(
                prelude.SomeOutputDatum(b"a").to_cbor(encoding="bytes")
            ),
            "Machine did validate the content",
        )
def test_wrapping_contract_compile(self)
Expand source code
def test_wrapping_contract_compile(self):
    # TODO devise tests for this
    input_file = "examples/smart_contracts/wrapped_token.py"
    with open(input_file) as fp:
        source_code = fp.read()
    ast = compiler.parse(source_code)
    code = compiler.compile(ast, force_three_params=True)
    code = code.compile()
    f = code.term
def test_zero_ary(self)
Expand source code
    def test_zero_ary(self):
        source_code = """
def a() -> None:
    assert False, "Executed a"

def validator(x: None) -> None:
    b = a
    if False:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))
def test_zero_ary_exec(self)
Expand source code
    @unittest.expectedFailure
    def test_zero_ary_exec(self):
        source_code = """
def a() -> None:
    assert False, "Executed a"

def validator(x: None) -> None:
    b = a
    if True:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))
def test_zero_ary_method(self)
Expand source code
    def test_zero_ary_method(self):
        source_code = """
def validator(x: None) -> None:
    b = b"\\xFF".decode
    if False:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))
def test_zero_ary_method_exec(self)
Expand source code
    @unittest.expectedFailure
    def test_zero_ary_method_exec(self):
        source_code = """
def validator(x: None) -> None:
    b = b"\\xFF".decode
    if True:
        b()
"""
        ast = compiler.parse(source_code)
        code = compiler.compile(ast).compile()
        res = uplc_eval(uplc.Apply(code, uplc.PlutusInteger(0)))