From 147d2b695bce018963d64f046fe1081def1a5d95 Mon Sep 17 00:00:00 2001 From: Jason Paulos Date: Fri, 26 Mar 2021 17:06:02 -0400 Subject: [PATCH] Add error for scratch slot load before store (#47) --- pyteal/__init__.py | 5 +- pyteal/ast/addr.py | 3 +- pyteal/ast/addr_test.py | 2 +- pyteal/ast/app.py | 3 +- pyteal/ast/app_test.py | 110 ++++---- pyteal/ast/arg.py | 4 +- pyteal/ast/arg_test.py | 2 +- pyteal/ast/assert_.py | 4 +- pyteal/ast/assert_test.py | 3 +- pyteal/ast/asset.py | 2 +- pyteal/ast/asset_test.py | 186 +++++++------ pyteal/ast/binaryexpr.py | 3 +- pyteal/ast/binaryexpr_test.py | 372 ++++++++++++++----------- pyteal/ast/bytes.py | 3 +- pyteal/ast/bytes_test.py | 22 +- pyteal/ast/cond.py | 4 +- pyteal/ast/cond_test.py | 9 +- pyteal/ast/ed25519verify.py | 4 +- pyteal/ast/ed25519verify_test.py | 11 +- pyteal/ast/err.py | 5 +- pyteal/ast/err_test.py | 2 +- pyteal/ast/expr.py | 9 +- pyteal/ast/global_.py | 3 +- pyteal/ast/global_test.py | 18 +- pyteal/ast/gtxn.py | 4 +- pyteal/ast/gtxn_test.py | 94 +++---- pyteal/ast/if_.py | 2 +- pyteal/ast/if_test.py | 34 ++- pyteal/ast/int.py | 7 +- pyteal/ast/int_test.py | 4 +- pyteal/ast/maybe.py | 3 +- pyteal/ast/maybe_test.py | 15 +- pyteal/ast/naryexpr.py | 3 +- pyteal/ast/naryexpr_test.py | 62 +++-- pyteal/ast/nonce.py | 15 +- pyteal/ast/nonce_test.py | 112 ++++---- pyteal/ast/scratch.py | 15 +- pyteal/ast/scratch_test.py | 19 +- pyteal/ast/scratchvar_test.py | 9 +- pyteal/ast/seq.py | 2 + pyteal/ast/seq_test.py | 5 +- pyteal/ast/substring.py | 4 +- pyteal/ast/substring_test.py | 11 +- pyteal/ast/tmpl.py | 3 +- pyteal/ast/tmpl_test.py | 6 +- pyteal/ast/txn.py | 6 +- pyteal/ast/txn_test.py | 94 +++---- pyteal/ast/unaryexpr.py | 3 +- pyteal/ast/unaryexpr_test.py | 91 +++--- pyteal/compiler.py | 37 +-- pyteal/compiler_test.py | 241 +++++++++------- pyteal/errors.py | 39 ++- pyteal/ir/tealblock.py | 45 ++- pyteal/ir/tealblock_test.py | 136 ++++----- pyteal/ir/tealcomponent.py | 25 +- pyteal/ir/tealcomponent_test.py | 21 ++ pyteal/ir/tealconditionalblock_test.py | 28 +- pyteal/ir/teallabel.py | 12 +- pyteal/ir/tealop.py | 11 +- pyteal/ir/tealsimpleblock_test.py | 12 +- pyteal/util.py | 30 -- tests/compile_test.py | 4 - 62 files changed, 1159 insertions(+), 894 deletions(-) create mode 100644 pyteal/ir/tealcomponent_test.py diff --git a/pyteal/__init__.py b/pyteal/__init__.py index ff1f2fb4d..c81b5207f 100644 --- a/pyteal/__init__.py +++ b/pyteal/__init__.py @@ -4,8 +4,7 @@ from .ir import __all__ as ir_all from .compiler import compileTeal from .types import TealType -from .errors import TealInternalError, TealTypeError, TealInputError -from .util import execute +from .errors import TealInternalError, TealTypeError, TealInputError, TealCompileError from .config import MAX_GROUP_SIZE __all__ = ast_all + ir_all + [ @@ -14,6 +13,6 @@ "TealInternalError", "TealTypeError", "TealInputError", - "execute", + "TealCompileError", "MAX_GROUP_SIZE", ] diff --git a/pyteal/ast/addr.py b/pyteal/ast/addr.py index 4c6ea6fb3..041bce055 100644 --- a/pyteal/ast/addr.py +++ b/pyteal/ast/addr.py @@ -11,11 +11,12 @@ def __init__(self, address: str) -> None: Args: address: A string containing a valid base32 Algorand address """ + super().__init__() valid_address(address) self.address = address def __teal__(self): - op = TealOp(Op.addr, self.address) + op = TealOp(self, Op.addr, self.address) return TealBlock.FromOp(op) def __str__(self): diff --git a/pyteal/ast/addr_test.py b/pyteal/ast/addr_test.py index c2c34f05f..419fd215f 100644 --- a/pyteal/ast/addr_test.py +++ b/pyteal/ast/addr_test.py @@ -6,7 +6,7 @@ def test_addr(): expr = Addr("NJUWK3DJNZTWU2LFNRUW4Z3KNFSWY2LOM5VGSZLMNFXGO2TJMVWGS3THMF") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.addr, "NJUWK3DJNZTWU2LFNRUW4Z3KNFSWY2LOM5VGSZLMNFXGO2TJMVWGS3THMF") + TealOp(expr, Op.addr, "NJUWK3DJNZTWU2LFNRUW4Z3KNFSWY2LOM5VGSZLMNFXGO2TJMVWGS3THMF") ]) actual, _ = expr.__teal__() assert actual == expected diff --git a/pyteal/ast/app.py b/pyteal/ast/app.py index 6b2841bf6..f963a4ae1 100644 --- a/pyteal/ast/app.py +++ b/pyteal/ast/app.py @@ -47,6 +47,7 @@ class App(LeafExpr): """An expression related to applications.""" def __init__(self, field:AppField, args) -> None: + super().__init__() self.field = field self.args = args @@ -58,7 +59,7 @@ def __str__(self): return ret_str def __teal__(self): - return TealBlock.FromOp(TealOp(self.field.get_op()), *self.args) + return TealBlock.FromOp(TealOp(self, self.field.get_op()), *self.args) def type_of(self): return self.field.type_of() diff --git a/pyteal/ast/app_test.py b/pyteal/ast/app_test.py index 2288f5737..abf230560 100644 --- a/pyteal/ast/app_test.py +++ b/pyteal/ast/app_test.py @@ -4,42 +4,44 @@ def test_on_complete(): assert OnComplete.NoOp.__teal__()[0] == TealSimpleBlock([ - TealOp(Op.int, "NoOp") + TealOp(OnComplete.NoOp, Op.int, "NoOp") ]) assert OnComplete.OptIn.__teal__()[0] == TealSimpleBlock([ - TealOp(Op.int, "OptIn") + TealOp(OnComplete.OptIn, Op.int, "OptIn") ]) assert OnComplete.CloseOut.__teal__()[0] == TealSimpleBlock([ - TealOp(Op.int, "CloseOut") + TealOp(OnComplete.CloseOut, Op.int, "CloseOut") ]) assert OnComplete.ClearState.__teal__()[0] == TealSimpleBlock([ - TealOp(Op.int, "ClearState") + TealOp(OnComplete.ClearState, Op.int, "ClearState") ]) assert OnComplete.UpdateApplication.__teal__()[0] == TealSimpleBlock([ - TealOp(Op.int, "UpdateApplication") + TealOp(OnComplete.UpdateApplication, Op.int, "UpdateApplication") ]) assert OnComplete.DeleteApplication.__teal__()[0] == TealSimpleBlock([ - TealOp(Op.int, "DeleteApplication") + TealOp(OnComplete.DeleteApplication, Op.int, "DeleteApplication") ]) def test_app_id(): expr = App.id() assert expr.type_of() == TealType.uint64 - assert expr.__teal__()[0] == Global.current_application_id().__teal__()[0] + with TealComponent.Context.ignoreExprEquality(): + assert expr.__teal__()[0] == Global.current_application_id().__teal__()[0] def test_opted_in(): - expr = App.optedIn(Int(1), Int(12)) + args = [Int(1), Int(12)] + expr = App.optedIn(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 12), - TealOp(Op.app_opted_in) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 12), + TealOp(expr, Op.app_opted_in) ]) actual, _ = expr.__teal__() @@ -49,13 +51,14 @@ def test_opted_in(): assert actual == expected def test_local_get(): - expr = App.localGet(Int(0), Bytes("key")) + args = [Int(0), Bytes("key")] + expr = App.localGet(args[0], args[1]) assert expr.type_of() == TealType.anytype expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.byte, "\"key\""), - TealOp(Op.app_local_get) + TealOp(args[0], Op.int, 0), + TealOp(args[1], Op.byte, "\"key\""), + TealOp(expr, Op.app_local_get) ]) actual, _ = expr.__teal__() @@ -72,24 +75,26 @@ def test_local_get_invalid(): App.localGet(Int(0), Int(1)) def test_local_get_ex(): - expr = App.localGetEx(Int(0), Int(6), Bytes("key")) + args = [Int(0), Int(6), Bytes("key")] + expr = App.localGetEx(args[0], args[1], args[2]) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.anytype expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.int, 6), - TealOp(Op.byte, "\"key\""), - TealOp(Op.app_local_get_ex), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(args[0], Op.int, 0), + TealOp(args[1], Op.int, 6), + TealOp(args[2], Op.byte, "\"key\""), + TealOp(expr, Op.app_local_get_ex), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_local_get_ex_invalid(): with pytest.raises(TealTypeError): @@ -102,12 +107,13 @@ def test_local_get_ex_invalid(): App.localGetEx(Int(0), Int(0), Int(1)) def test_global_get(): - expr = App.globalGet(Bytes("key")) + arg = Bytes("key") + expr = App.globalGet(arg) assert expr.type_of() == TealType.anytype expected = TealSimpleBlock([ - TealOp(Op.byte, "\"key\""), - TealOp(Op.app_global_get) + TealOp(arg, Op.byte, "\"key\""), + TealOp(expr, Op.app_global_get) ]) actual, _ = expr.__teal__() @@ -121,23 +127,25 @@ def test_global_get_invalid(): App.globalGet(Int(7)) def test_global_get_ex(): - expr = App.globalGetEx(Int(6), Bytes("key")) + args = [Int(6), Bytes("key")] + expr = App.globalGetEx(args[0], args[1]) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.anytype expected = TealSimpleBlock([ - TealOp(Op.int, 6), - TealOp(Op.byte, "\"key\""), - TealOp(Op.app_global_get_ex), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(args[0], Op.int, 6), + TealOp(args[1], Op.byte, "\"key\""), + TealOp(expr, Op.app_global_get_ex), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_global_get_ex_invalid(): with pytest.raises(TealTypeError): @@ -147,14 +155,15 @@ def test_global_get_ex_invalid(): App.globalGetEx(Int(0), Int(1)) def test_local_put(): - expr = App.localPut(Int(0), Bytes("key"), Int(5)) + args = [Int(0), Bytes("key"), Int(5)] + expr = App.localPut(args[0], args[1], args[2]) assert expr.type_of() == TealType.none expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.byte, "\"key\""), - TealOp(Op.int, 5), - TealOp(Op.app_local_put) + TealOp(args[0], Op.int, 0), + TealOp(args[1], Op.byte, "\"key\""), + TealOp(args[2], Op.int, 5), + TealOp(expr, Op.app_local_put) ]) actual, _ = expr.__teal__() @@ -174,13 +183,14 @@ def test_local_put_invalid(): App.localPut(Int(1), Bytes("key"), Pop(Int(1))) def test_global_put(): - expr = App.globalPut(Bytes("key"), Int(5)) + args = [Bytes("key"), Int(5)] + expr = App.globalPut(args[0], args[1]) assert expr.type_of() == TealType.none expected = TealSimpleBlock([ - TealOp(Op.byte, "\"key\""), - TealOp(Op.int, 5), - TealOp(Op.app_global_put) + TealOp(args[0], Op.byte, "\"key\""), + TealOp(args[1], Op.int, 5), + TealOp(expr, Op.app_global_put) ]) actual, _ = expr.__teal__() @@ -197,13 +207,14 @@ def test_global_put_invalid(): App.globalPut(Bytes("key"), Pop(Int(1))) def test_local_del(): - expr = App.localDel(Int(0), Bytes("key")) + args = [Int(0), Bytes("key")] + expr = App.localDel(args[0], args[1]) assert expr.type_of() == TealType.none expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.byte, "\"key\""), - TealOp(Op.app_local_del) + TealOp(args[0], Op.int, 0), + TealOp(args[1], Op.byte, "\"key\""), + TealOp(expr, Op.app_local_del) ]) actual, _ = expr.__teal__() @@ -220,12 +231,13 @@ def test_local_del_invalid(): App.localDel(Int(1), Int(2)) def test_global_del(): - expr = App.globalDel(Bytes("key")) + arg = Bytes("key") + expr = App.globalDel(arg) assert expr.type_of() == TealType.none expected = TealSimpleBlock([ - TealOp(Op.byte, "\"key\""), - TealOp(Op.app_global_del) + TealOp(arg, Op.byte, "\"key\""), + TealOp(expr, Op.app_global_del) ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/arg.py b/pyteal/ast/arg.py index 923095d46..731318cac 100644 --- a/pyteal/ast/arg.py +++ b/pyteal/ast/arg.py @@ -15,6 +15,8 @@ def __init__(self, index: int) -> None: Args: index: The integer index of the argument to get. Must be between 0 and 255 inclusive. """ + super().__init__() + if type(index) is not int: raise TealInputError("invalid arg input type {}".format(type(index))) @@ -24,7 +26,7 @@ def __init__(self, index: int) -> None: self.index = index def __teal__(self): - op = TealOp(Op.arg, self.index) + op = TealOp(self, Op.arg, self.index) return TealBlock.FromOp(op) def __str__(self): diff --git a/pyteal/ast/arg_test.py b/pyteal/ast/arg_test.py index fa240a18b..1ec2bb16a 100644 --- a/pyteal/ast/arg_test.py +++ b/pyteal/ast/arg_test.py @@ -6,7 +6,7 @@ def test_arg(): expr = Arg(0) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.arg, 0) + TealOp(expr, Op.arg, 0) ]) actual, _ = expr.__teal__() assert actual == expected diff --git a/pyteal/ast/assert_.py b/pyteal/ast/assert_.py index ffe5f05ee..aa8d75973 100644 --- a/pyteal/ast/assert_.py +++ b/pyteal/ast/assert_.py @@ -1,6 +1,5 @@ from ..types import TealType, require_type from ..ir import TealOp, Op, TealSimpleBlock, TealConditionalBlock -from ..util import new_label from .expr import Expr class Assert(Expr): @@ -12,6 +11,7 @@ def __init__(self, cond: Expr) -> None: Args: cond: The condition to check. Must evaluate to a uint64. """ + super().__init__() require_type(cond.type_of(), TealType.uint64) self.cond = cond @@ -19,7 +19,7 @@ def __teal__(self): condStart, condEnd = self.cond.__teal__() end = TealSimpleBlock([]) - errBlock = TealSimpleBlock([TealOp(Op.err)]) + errBlock = TealSimpleBlock([TealOp(self, Op.err)]) branchBlock = TealConditionalBlock([]) branchBlock.setTrueBlock(end) diff --git a/pyteal/ast/assert_test.py b/pyteal/ast/assert_test.py index 42c89f245..2ec861621 100644 --- a/pyteal/ast/assert_test.py +++ b/pyteal/ast/assert_test.py @@ -14,7 +14,8 @@ def test_assert(): actual, _ = expr.__teal__() - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_assert_invalid(): with pytest.raises(TealTypeError): diff --git a/pyteal/ast/asset.py b/pyteal/ast/asset.py index 7f8fdffde..21020ca1a 100644 --- a/pyteal/ast/asset.py +++ b/pyteal/ast/asset.py @@ -1,7 +1,7 @@ from enum import Enum from ..types import TealType, require_type -from ..ir import TealOp, Op +from ..ir import Op from .expr import Expr from .leafexpr import LeafExpr from .maybe import MaybeValue diff --git a/pyteal/ast/asset_test.py b/pyteal/ast/asset_test.py index 624df518d..6250bcbee 100644 --- a/pyteal/ast/asset_test.py +++ b/pyteal/ast/asset_test.py @@ -3,23 +3,25 @@ from .. import * def test_asset_holding_balance(): - expr = AssetHolding.balance(Int(0), Int(17)) + args = Int(0), Int(17) + expr = AssetHolding.balance(args[0], args[1]) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.int, 17), - TealOp(Op.asset_holding_get, "AssetBalance"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(args[0], Op.int, 0), + TealOp(args[1], Op.int, 17), + TealOp(expr, Op.asset_holding_get, "AssetBalance"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_holding_balance_invalid(): with pytest.raises(TealTypeError): @@ -29,23 +31,25 @@ def test_asset_holding_balance_invalid(): AssetHolding.balance(Int(0), Txn.receiver()) def test_asset_holding_frozen(): - expr = AssetHolding.frozen(Int(0), Int(17)) + args = [Int(0), Int(17)] + expr = AssetHolding.frozen(args[0], args[1]) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.int, 17), - TealOp(Op.asset_holding_get, "AssetFrozen"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(args[0], Op.int, 0), + TealOp(args[1], Op.int, 17), + TealOp(expr, Op.asset_holding_get, "AssetFrozen"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_holding_frozen_invalid(): with pytest.raises(TealTypeError): @@ -55,242 +59,264 @@ def test_asset_holding_frozen_invalid(): AssetHolding.frozen(Int(0), Txn.receiver()) def test_asset_param_total(): - expr = AssetParam.total(Int(0)) + arg = Int(0) + expr = AssetParam.total(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetTotal"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetTotal"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_total_invalid(): with pytest.raises(TealTypeError): AssetParam.total(Txn.sender()) def test_asset_param_decimals(): - expr = AssetParam.decimals(Int(0)) + arg = Int(0) + expr = AssetParam.decimals(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetDecimals"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetDecimals"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_decimals_invalid(): with pytest.raises(TealTypeError): AssetParam.decimals(Txn.sender()) def test_asset_param_default_frozen(): - expr = AssetParam.defaultFrozen(Int(0)) + arg = Int(0) + expr = AssetParam.defaultFrozen(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetDefaultFrozen"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetDefaultFrozen"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_default_frozen_invalid(): with pytest.raises(TealTypeError): AssetParam.defaultFrozen(Txn.sender()) def test_asset_param_unit_name(): - expr = AssetParam.unitName(Int(0)) + arg = Int(0) + expr = AssetParam.unitName(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetUnitName"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetUnitName"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_unit_name_invalid(): with pytest.raises(TealTypeError): AssetParam.unitName(Txn.sender()) def test_asset_param_name(): - expr = AssetParam.name(Int(0)) + arg = Int(0) + expr = AssetParam.name(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetName"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetName"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_name_invalid(): with pytest.raises(TealTypeError): AssetParam.name(Txn.sender()) def test_asset_param_url(): - expr = AssetParam.url(Int(0)) + arg = Int(0) + expr = AssetParam.url(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetURL"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetURL"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_url_invalid(): with pytest.raises(TealTypeError): AssetParam.url(Txn.sender()) def test_asset_param_metadata_hash(): - expr = AssetParam.metadataHash(Int(0)) + arg = Int(0) + expr = AssetParam.metadataHash(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetMetadataHash"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetMetadataHash"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_metadata_hash_invalid(): with pytest.raises(TealTypeError): AssetParam.metadataHash(Txn.sender()) def test_asset_param_manager(): - expr = AssetParam.manager(Int(0)) + arg = Int(0) + expr = AssetParam.manager(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetManager"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetManager"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_manager_invalid(): with pytest.raises(TealTypeError): AssetParam.manager(Txn.sender()) def test_asset_param_reserve(): - expr = AssetParam.reserve(Int(2)) + arg = Int(2) + expr = AssetParam.reserve(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.asset_params_get, "AssetReserve"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 2), + TealOp(expr, Op.asset_params_get, "AssetReserve"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_reserve_invalid(): with pytest.raises(TealTypeError): AssetParam.reserve(Txn.sender()) def test_asset_param_freeze(): - expr = AssetParam.freeze(Int(0)) + arg = Int(0) + expr = AssetParam.freeze(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.asset_params_get, "AssetFreeze"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.asset_params_get, "AssetFreeze"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_freeze_invalid(): with pytest.raises(TealTypeError): AssetParam.freeze(Txn.sender()) def test_asset_param_clawback(): - expr = AssetParam.clawback(Int(1)) + arg = Int(1) + expr = AssetParam.clawback(arg) assert expr.type_of() == TealType.none assert expr.value().type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.asset_params_get, "AssetClawback"), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(arg, Op.int, 1), + TealOp(expr, Op.asset_params_get, "AssetClawback"), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_asset_param_clawback_invalid(): with pytest.raises(TealTypeError): diff --git a/pyteal/ast/binaryexpr.py b/pyteal/ast/binaryexpr.py index 7664a4fca..5b54783d1 100644 --- a/pyteal/ast/binaryexpr.py +++ b/pyteal/ast/binaryexpr.py @@ -6,6 +6,7 @@ class BinaryExpr(Expr): """An expression with two arguments.""" def __init__(self, op: Op, inputType: TealType, outputType: TealType, argLeft: Expr, argRight: Expr) -> None: + super().__init__() require_type(argLeft.type_of(), inputType) require_type(argRight.type_of(), inputType) self.op = op @@ -14,7 +15,7 @@ def __init__(self, op: Op, inputType: TealType, outputType: TealType, argLeft: E self.argRight = argRight def __teal__(self): - return TealBlock.FromOp(TealOp(self.op), self.argLeft, self.argRight) + return TealBlock.FromOp(TealOp(self, self.op), self.argLeft, self.argRight) def __str__(self): return "({} {} {})".format(self.op, self.argLeft, self.argRight) diff --git a/pyteal/ast/binaryexpr_test.py b/pyteal/ast/binaryexpr_test.py index 26135a7ed..d3cc03e58 100644 --- a/pyteal/ast/binaryexpr_test.py +++ b/pyteal/ast/binaryexpr_test.py @@ -3,13 +3,14 @@ from .. import * def test_add(): - expr = Add(Int(2), Int(3)) + args = [Int(2), Int(3)] + expr = Add(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.add) + TealOp(args[0], Op.int, 2), + TealOp(args[1], Op.int, 3), + TealOp(expr, Op.add) ]) actual, _ = expr.__teal__() @@ -19,22 +20,24 @@ def test_add(): assert actual == expected def test_add_overload(): - expr = Int(2) + Int(3) + Int(4) + args = [Int(2), Int(3), Int(4)] + expr = args[0] + args[1] + args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.add), - TealOp(Op.int, 4), - TealOp(Op.add) + TealOp(args[0], Op.int, 2), + TealOp(args[1], Op.int, 3), + TealOp(None, Op.add), + TealOp(args[2], Op.int, 4), + TealOp(None, Op.add) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_add_invalid(): with pytest.raises(TealTypeError): @@ -44,13 +47,14 @@ def test_add_invalid(): Add(Txn.sender(), Int(2)) def test_minus(): - expr = Minus(Int(5), Int(6)) + args = [Int(5), Int(6)] + expr = Minus(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 5), - TealOp(Op.int, 6), - TealOp(Op.minus) + TealOp(args[0], Op.int, 5), + TealOp(args[1], Op.int, 6), + TealOp(expr, Op.minus) ]) actual, _ = expr.__teal__() @@ -60,22 +64,24 @@ def test_minus(): assert actual == expected def test_minus_overload(): - expr = Int(10) - Int(1) - Int(2) + args = [Int(10), Int(1), Int(2)] + expr = args[0] - args[1] - args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 10), - TealOp(Op.int, 1), - TealOp(Op.minus), - TealOp(Op.int, 2), - TealOp(Op.minus) + TealOp(args[0], Op.int, 10), + TealOp(args[1], Op.int, 1), + TealOp(None, Op.minus), + TealOp(args[2], Op.int, 2), + TealOp(None, Op.minus) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_minus_invalid(): with pytest.raises(TealTypeError): @@ -85,13 +91,14 @@ def test_minus_invalid(): Minus(Txn.sender(), Int(2)) def test_mul(): - expr = Mul(Int(3), Int(8)) + args = [Int(3), Int(8)] + expr = Mul(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 3), - TealOp(Op.int, 8), - TealOp(Op.mul) + TealOp(args[0], Op.int, 3), + TealOp(args[1], Op.int, 8), + TealOp(expr, Op.mul) ]) actual, _ = expr.__teal__() @@ -101,22 +108,24 @@ def test_mul(): assert actual == expected def test_mul_overload(): - expr = Int(3) * Int(8) * Int(10) + args = [Int(3), Int(8), Int(10)] + expr = args[0] * args[1] * args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 3), - TealOp(Op.int, 8), - TealOp(Op.mul), - TealOp(Op.int, 10), - TealOp(Op.mul) + TealOp(args[0], Op.int, 3), + TealOp(args[1], Op.int, 8), + TealOp(None, Op.mul), + TealOp(args[2], Op.int, 10), + TealOp(None, Op.mul) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_mul_invalid(): with pytest.raises(TealTypeError): @@ -126,13 +135,14 @@ def test_mul_invalid(): Mul(Txn.sender(), Int(2)) def test_div(): - expr = Div(Int(9), Int(3)) + args = [Int(9), Int(3)] + expr = Div(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 9), - TealOp(Op.int, 3), - TealOp(Op.div) + TealOp(args[0], Op.int, 9), + TealOp(args[1], Op.int, 3), + TealOp(expr, Op.div) ]) actual, _ = expr.__teal__() @@ -142,22 +152,24 @@ def test_div(): assert actual == expected def test_div_overload(): - expr = Int(9) / Int(3) / Int(3) + args = [Int(9), Int(3), Int(3)] + expr = args[0] / args[1] / args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 9), - TealOp(Op.int, 3), - TealOp(Op.div), - TealOp(Op.int, 3), - TealOp(Op.div), + TealOp(args[0], Op.int, 9), + TealOp(args[1], Op.int, 3), + TealOp(None, Op.div), + TealOp(args[2], Op.int, 3), + TealOp(None, Op.div), ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_div_invalid(): with pytest.raises(TealTypeError): @@ -167,13 +179,14 @@ def test_div_invalid(): Div(Txn.sender(), Int(2)) def test_mod(): - expr = Mod(Int(10), Int(9)) + args = [Int(10), Int(9)] + expr = Mod(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 10), - TealOp(Op.int, 9), - TealOp(Op.mod) + TealOp(args[0], Op.int, 10), + TealOp(args[1], Op.int, 9), + TealOp(expr, Op.mod) ]) actual, _ = expr.__teal__() @@ -183,22 +196,24 @@ def test_mod(): assert actual == expected def test_mod_overload(): - expr = Int(10) % Int(9) % Int(100) + args = [Int(10), Int(9), Int(100)] + expr = args[0] % args[1] % args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 10), - TealOp(Op.int, 9), - TealOp(Op.mod), - TealOp(Op.int, 100), - TealOp(Op.mod) + TealOp(args[0], Op.int, 10), + TealOp(args[1], Op.int, 9), + TealOp(None, Op.mod), + TealOp(args[2], Op.int, 100), + TealOp(None, Op.mod) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_mod_invalid(): with pytest.raises(TealTypeError): @@ -208,37 +223,40 @@ def test_mod_invalid(): Mod(Int(2), Txn.sender()) def test_arithmetic(): - v = ((Int(2) + Int(3))/((Int(5) - Int(6)) * Int(8))) % Int(9) + args = [Int(2), Int(3), Int(5), Int(6), Int(8), Int(9)] + v = ((args[0] + args[1])/((args[2] - args[3]) * args[4])) % args[5] assert v.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.add), - TealOp(Op.int, 5), - TealOp(Op.int, 6), - TealOp(Op.minus), - TealOp(Op.int, 8), - TealOp(Op.mul), - TealOp(Op.div), - TealOp(Op.int, 9), - TealOp(Op.mod) + TealOp(args[0], Op.int, 2), + TealOp(args[1], Op.int, 3), + TealOp(None, Op.add), + TealOp(args[2], Op.int, 5), + TealOp(args[3], Op.int, 6), + TealOp(None, Op.minus), + TealOp(args[4], Op.int, 8), + TealOp(None, Op.mul), + TealOp(None, Op.div), + TealOp(args[5], Op.int, 9), + TealOp(None, Op.mod) ]) actual, _ = v.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_bitwise_and(): - expr = BitwiseAnd(Int(1), Int(2)) + args = [Int(1), Int(2)] + expr = BitwiseAnd(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.bitwise_and) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(expr, Op.bitwise_and) ]) actual, _ = expr.__teal__() @@ -248,22 +266,24 @@ def test_bitwise_and(): assert actual == expected def test_bitwise_and_overload(): - expr = Int(1) & Int(2) & Int(4) + args = [Int(1), Int(2), Int(4)] + expr = args[0] & args[1] & args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.bitwise_and), - TealOp(Op.int, 4), - TealOp(Op.bitwise_and) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(None, Op.bitwise_and), + TealOp(args[2], Op.int, 4), + TealOp(None, Op.bitwise_and) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_bitwise_and_invalid(): with pytest.raises(TealTypeError): @@ -273,13 +293,14 @@ def test_bitwise_and_invalid(): BitwiseAnd(Txn.sender(), Int(2)) def test_bitwise_or(): - expr = BitwiseOr(Int(1), Int(2)) + args = [Int(1), Int(2)] + expr = BitwiseOr(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.bitwise_or) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(expr, Op.bitwise_or) ]) actual, _ = expr.__teal__() @@ -289,22 +310,24 @@ def test_bitwise_or(): assert actual == expected def test_bitwise_or_overload(): - expr = Int(1) | Int(2) | Int(4) + args = [Int(1), Int(2), Int(4)] + expr = args[0] | args[1] | args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.bitwise_or), - TealOp(Op.int, 4), - TealOp(Op.bitwise_or) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(None, Op.bitwise_or), + TealOp(args[2], Op.int, 4), + TealOp(None, Op.bitwise_or) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_bitwise_or_invalid(): with pytest.raises(TealTypeError): @@ -314,13 +337,14 @@ def test_bitwise_or_invalid(): BitwiseOr(Txn.sender(), Int(2)) def test_bitwise_xor(): - expr = BitwiseXor(Int(1), Int(3)) + args = [Int(1), Int(3)] + expr = BitwiseXor(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 3), - TealOp(Op.bitwise_xor) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 3), + TealOp(expr, Op.bitwise_xor) ]) actual, _ = expr.__teal__() @@ -330,22 +354,24 @@ def test_bitwise_xor(): assert actual == expected def test_bitwise_xor_overload(): - expr = Int(1) ^ Int(3) ^ Int(5) + args = [Int(1), Int(3), Int(5)] + expr = args[0] ^ args[1] ^ args[2] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 3), - TealOp(Op.bitwise_xor), - TealOp(Op.int, 5), - TealOp(Op.bitwise_xor) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 3), + TealOp(None, Op.bitwise_xor), + TealOp(args[2], Op.int, 5), + TealOp(None, Op.bitwise_xor) ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_bitwise_xor_invalid(): with pytest.raises(TealTypeError): @@ -355,13 +381,14 @@ def test_bitwise_xor_invalid(): BitwiseXor(Txn.sender(), Int(2)) def test_eq(): - expr_int = Eq(Int(2), Int(3)) + args_int = [Int(2), Int(3)] + expr_int = Eq(args_int[0], args_int[1]) assert expr_int.type_of() == TealType.uint64 expected_int = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.eq) + TealOp(args_int[0], Op.int, 2), + TealOp(args_int[1], Op.int, 3), + TealOp(expr_int, Op.eq) ]) actual_int, _ = expr_int.__teal__() @@ -370,13 +397,14 @@ def test_eq(): assert actual_int == expected_int - expr_bytes = Eq(Txn.receiver(), Txn.sender()) + args_bytes = [Txn.receiver(), Txn.sender()] + expr_bytes = Eq(args_bytes[0], args_bytes[1]) assert expr_bytes.type_of() == TealType.uint64 expected_bytes = TealSimpleBlock([ - TealOp(Op.txn, "Receiver"), - TealOp(Op.txn, "Sender"), - TealOp(Op.eq) + TealOp(args_bytes[0], Op.txn, "Receiver"), + TealOp(args_bytes[1], Op.txn, "Sender"), + TealOp(expr_bytes, Op.eq) ]) actual_bytes, _ = expr_bytes.__teal__() @@ -386,13 +414,14 @@ def test_eq(): assert actual_bytes == expected_bytes def test_eq_overload(): - expr_int = Int(2) == Int(3) + args_int = [Int(2), Int(3)] + expr_int = args_int[0] == args_int[1] assert expr_int.type_of() == TealType.uint64 expected_int = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.eq) + TealOp(args_int[0], Op.int, 2), + TealOp(args_int[1], Op.int, 3), + TealOp(expr_int, Op.eq) ]) actual_int, _ = expr_int.__teal__() @@ -401,13 +430,14 @@ def test_eq_overload(): assert actual_int == expected_int - expr_bytes = Txn.receiver() == Txn.sender() + args_bytes = [Txn.receiver(), Txn.sender()] + expr_bytes = args_bytes[0] == args_bytes[1] assert expr_bytes.type_of() == TealType.uint64 expected_bytes = TealSimpleBlock([ - TealOp(Op.txn, "Receiver"), - TealOp(Op.txn, "Sender"), - TealOp(Op.eq) + TealOp(args_bytes[0], Op.txn, "Receiver"), + TealOp(args_bytes[1], Op.txn, "Sender"), + TealOp(expr_bytes, Op.eq) ]) actual_bytes, _ = expr_bytes.__teal__() @@ -424,13 +454,14 @@ def test_eq_invalid(): Eq(Txn.sender(), Int(7)) def test_neq(): - expr_int = Neq(Int(2), Int(3)) + args_int = [Int(2), Int(3)] + expr_int = Neq(args_int[0], args_int[1]) assert expr_int.type_of() == TealType.uint64 expected_int = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.neq) + TealOp(args_int[0], Op.int, 2), + TealOp(args_int[1], Op.int, 3), + TealOp(expr_int, Op.neq) ]) actual_int, _ = expr_int.__teal__() @@ -439,13 +470,14 @@ def test_neq(): assert actual_int == expected_int - expr_bytes = Neq(Txn.receiver(), Txn.sender()) + args_bytes = [Txn.receiver(), Txn.sender()] + expr_bytes = Neq(args_bytes[0], args_bytes[1]) assert expr_bytes.type_of() == TealType.uint64 expected_bytes = TealSimpleBlock([ - TealOp(Op.txn, "Receiver"), - TealOp(Op.txn, "Sender"), - TealOp(Op.neq) + TealOp(args_bytes[0], Op.txn, "Receiver"), + TealOp(args_bytes[1], Op.txn, "Sender"), + TealOp(expr_bytes, Op.neq) ]) actual_bytes, _ = expr_bytes.__teal__() @@ -455,13 +487,14 @@ def test_neq(): assert actual_bytes == expected_bytes def test_neq_overload(): - expr_int = Int(2) != Int(3) + args_int = [Int(2), Int(3)] + expr_int = args_int[0] != args_int[1] assert expr_int.type_of() == TealType.uint64 expected_int = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.neq) + TealOp(args_int[0], Op.int, 2), + TealOp(args_int[1], Op.int, 3), + TealOp(expr_int, Op.neq) ]) actual_int, _ = expr_int.__teal__() @@ -470,13 +503,14 @@ def test_neq_overload(): assert actual_int == expected_int - expr_bytes = Txn.receiver() != Txn.sender() + args_bytes = [Txn.receiver(), Txn.sender()] + expr_bytes = args_bytes[0] != args_bytes[1] assert expr_bytes.type_of() == TealType.uint64 expected_bytes = TealSimpleBlock([ - TealOp(Op.txn, "Receiver"), - TealOp(Op.txn, "Sender"), - TealOp(Op.neq) + TealOp(args_bytes[0], Op.txn, "Receiver"), + TealOp(args_bytes[1], Op.txn, "Sender"), + TealOp(expr_bytes, Op.neq) ]) actual_bytes, _ = expr_bytes.__teal__() @@ -493,13 +527,14 @@ def test_neq_invalid(): Neq(Txn.sender(), Int(7)) def test_lt(): - expr = Lt(Int(2), Int(3)) + args = [Int(2), Int(3)] + expr = Lt(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.lt) + TealOp(args[0], Op.int, 2), + TealOp(args[1], Op.int, 3), + TealOp(expr, Op.lt) ]) actual, _ = expr.__teal__() @@ -509,13 +544,14 @@ def test_lt(): assert actual == expected def test_lt_overload(): - expr = Int(2) < Int(3) + args = [Int(2), Int(3)] + expr = args[0] < args[1] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.lt) + TealOp(args[0], Op.int, 2), + TealOp(args[1], Op.int, 3), + TealOp(expr, Op.lt) ]) actual, _ = expr.__teal__() @@ -532,13 +568,14 @@ def test_lt_invalid(): Lt(Txn.sender(), Int(7)) def test_le(): - expr = Le(Int(1), Int(2)) + args = [Int(1), Int(2)] + expr = Le(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.le) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(expr, Op.le) ]) actual, _ = expr.__teal__() @@ -548,13 +585,14 @@ def test_le(): assert actual == expected def test_le_overload(): - expr = Int(1) <= Int(2) + args = [Int(1), Int(2)] + expr = args[0] <= args[1] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.le) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(expr, Op.le) ]) actual, _ = expr.__teal__() @@ -571,13 +609,14 @@ def test_le_invalid(): Le(Txn.sender(), Int(1)) def test_gt(): - expr = Gt(Int(2), Int(3)) + args = [Int(2), Int(3)] + expr = Gt(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.gt) + TealOp(args[0], Op.int, 2), + TealOp(args[1], Op.int, 3), + TealOp(expr, Op.gt) ]) actual, _ = expr.__teal__() @@ -587,13 +626,14 @@ def test_gt(): assert actual == expected def test_gt_overload(): - expr = Int(2) > Int(3) + args = [Int(2), Int(3)] + expr = args[0] > args[1] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.gt) + TealOp(args[0], Op.int, 2), + TealOp(args[1], Op.int, 3), + TealOp(expr, Op.gt) ]) actual, _ = expr.__teal__() @@ -610,13 +650,14 @@ def test_gt_invalid(): Gt(Txn.receiver(), Int(1)) def test_ge(): - expr = Ge(Int(1), Int(10)) + args = [Int(1), Int(10)] + expr = Ge(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 10), - TealOp(Op.ge) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 10), + TealOp(expr, Op.ge) ]) actual, _ = expr.__teal__() @@ -626,13 +667,14 @@ def test_ge(): assert actual == expected def test_ge_overload(): - expr = Int(1) >= Int(10) + args = [Int(1), Int(10)] + expr = args[0] >= args[1] assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 10), - TealOp(Op.ge) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 10), + TealOp(expr, Op.ge) ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/bytes.py b/pyteal/ast/bytes.py index 1022b5b65..57b9237fc 100644 --- a/pyteal/ast/bytes.py +++ b/pyteal/ast/bytes.py @@ -22,6 +22,7 @@ def __init__(self, *args: str) -> None: The prefix "0x" may be present in a base16 byte string. For example, ``Bytes("base16", "0x636F6E74656E74")``. """ + super().__init__() if len(args) == 1: self.base = "utf8" self.byte_str = escapeStr(args[0]) @@ -51,7 +52,7 @@ def __teal__(self): payload = "0x" + self.byte_str else: payload = "{}({})".format(self.base, self.byte_str) - op = TealOp(Op.byte, payload) + op = TealOp(self, Op.byte, payload) return TealBlock.FromOp(op) def __str__(self): diff --git a/pyteal/ast/bytes_test.py b/pyteal/ast/bytes_test.py index 1053fff9b..3e418ad21 100644 --- a/pyteal/ast/bytes_test.py +++ b/pyteal/ast/bytes_test.py @@ -7,7 +7,7 @@ def test_bytes_base32_no_padding(): expr = Bytes("base32", s) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "base32(" + s + ")") + TealOp(expr, Op.byte, "base32(" + s + ")") ]) actual, _ = expr.__teal__() assert actual == expected @@ -17,7 +17,7 @@ def test_bytes_base32_padding(): expr = Bytes("base32", s) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "base32(" + s + ")") + TealOp(expr, Op.byte, "base32(" + s + ")") ]) actual, _ = expr.__teal__() assert actual == expected @@ -26,7 +26,7 @@ def test_bytes_base32_empty(): expr = Bytes("base32", "") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "base32()") + TealOp(expr, Op.byte, "base32()") ]) actual, _ = expr.__teal__() assert actual == expected @@ -35,7 +35,7 @@ def test_bytes_base64(): expr = Bytes("base64", "Zm9vYmE=") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "base64(Zm9vYmE=)") + TealOp(expr, Op.byte, "base64(Zm9vYmE=)") ]) actual, _ = expr.__teal__() assert actual == expected @@ -44,7 +44,7 @@ def test_bytes_base64_empty(): expr = Bytes("base64", "") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "base64()") + TealOp(expr, Op.byte, "base64()") ]) actual, _ = expr.__teal__() assert actual == expected @@ -53,7 +53,7 @@ def test_bytes_base16(): expr = Bytes("base16", "A21212EF") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "0xA21212EF") + TealOp(expr, Op.byte, "0xA21212EF") ]) actual, _ = expr.__teal__() assert actual == expected @@ -62,7 +62,7 @@ def test_bytes_base16_prefix(): expr = Bytes("base16", "0xA21212EF") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "0xA21212EF") + TealOp(expr, Op.byte, "0xA21212EF") ]) actual, _ = expr.__teal__() assert actual == expected @@ -71,7 +71,7 @@ def test_bytes_base16_empty(): expr = Bytes("base16", "") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "0x") + TealOp(expr, Op.byte, "0x") ]) actual, _ = expr.__teal__() assert actual == expected @@ -80,7 +80,7 @@ def test_bytes_utf8(): expr = Bytes("hello world") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "\"hello world\"") + TealOp(expr, Op.byte, "\"hello world\"") ]) actual, _ = expr.__teal__() assert actual == expected @@ -89,7 +89,7 @@ def test_bytes_utf8_special_chars(): expr = Bytes("\t \n \r\n \\ \" \' 😀") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "\"\\t \\n \\r\\n \\\\ \\\" \' \\xf0\\x9f\\x98\\x80\"") + TealOp(expr, Op.byte, "\"\\t \\n \\r\\n \\\\ \\\" \' \\xf0\\x9f\\x98\\x80\"") ]) actual, _ = expr.__teal__() assert actual == expected @@ -98,7 +98,7 @@ def test_bytes_utf8_empty(): expr = Bytes("") assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "\"\"") + TealOp(expr, Op.byte, "\"\"") ]) actual, _ = expr.__teal__() assert actual == expected diff --git a/pyteal/ast/cond.py b/pyteal/ast/cond.py index 294f2d627..51e7a2f1d 100644 --- a/pyteal/ast/cond.py +++ b/pyteal/ast/cond.py @@ -3,7 +3,6 @@ from ..types import TealType, require_type from ..ir import TealOp, Op, TealSimpleBlock, TealConditionalBlock from ..errors import TealInputError -from ..util import new_label from .expr import Expr from .err import Err from .if_ import If @@ -29,6 +28,7 @@ def __init__(self, *argv: List[Expr]): [Global.group_size() == Int(4), redeem], [Global.group_size() == Int(1), wrapup]) """ + super().__init__() if len(argv) < 1: raise TealInputError("Cond requires at least one [condition, value]") @@ -71,7 +71,7 @@ def __teal__(self): prevBranch.setFalseBlock(condStart) prevBranch = branchBlock - errBlock = TealSimpleBlock([TealOp(Op.err)]) + errBlock = TealSimpleBlock([TealOp(self, Op.err)]) prevBranch.setFalseBlock(errBlock) return start, end diff --git a/pyteal/ast/cond_test.py b/pyteal/ast/cond_test.py index 8e6e0d77e..34501917b 100644 --- a/pyteal/ast/cond_test.py +++ b/pyteal/ast/cond_test.py @@ -17,7 +17,8 @@ def test_cond_one_pred(): actual, _ = expr.__teal__() - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_cond_two_pred(): expr = Cond([Int(1), Bytes("one")], [Int(0), Bytes("zero")]) @@ -45,7 +46,8 @@ def test_cond_two_pred(): actual, _ = expr.__teal__() - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_cond_three_pred(): expr = Cond([Int(1), Int(2)], [Int(3), Int(4)], [Int(5), Int(6)]) @@ -81,7 +83,8 @@ def test_cond_three_pred(): actual, _ = expr.__teal__() - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected def test_cond_invalid(): with pytest.raises(TealInputError): diff --git a/pyteal/ast/ed25519verify.py b/pyteal/ast/ed25519verify.py index c620e889b..066b81e80 100644 --- a/pyteal/ast/ed25519verify.py +++ b/pyteal/ast/ed25519verify.py @@ -14,6 +14,8 @@ def __init__(self, data: Expr, sig: Expr, key: Expr) -> None: evalute to bytes. key: The 32 byte public key that produced the signature. Must evaluate to bytes. """ + super().__init__() + require_type(data.type_of(), TealType.bytes) require_type(sig.type_of(), TealType.bytes) require_type(key.type_of(), TealType.bytes) @@ -23,7 +25,7 @@ def __init__(self, data: Expr, sig: Expr, key: Expr) -> None: self.key = key def __teal__(self): - return TealBlock.FromOp(TealOp(Op.ed25519verify), self.data, self.sig, self.key) + return TealBlock.FromOp(TealOp(self, Op.ed25519verify), self.data, self.sig, self.key) def __str__(self): return "(ed25519verify {} {} {})".format(self.data, self.sig, self.key) diff --git a/pyteal/ast/ed25519verify_test.py b/pyteal/ast/ed25519verify_test.py index e8c716e4d..d3f6b58ab 100644 --- a/pyteal/ast/ed25519verify_test.py +++ b/pyteal/ast/ed25519verify_test.py @@ -3,14 +3,15 @@ from .. import * def test_ed25519verify(): - expr = Ed25519Verify(Bytes("data"), Bytes("sig"), Bytes("key")) + args = [Bytes("data"), Bytes("sig"), Bytes("key")] + expr = Ed25519Verify(args[0], args[1], args[2]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.byte, "\"data\""), - TealOp(Op.byte, "\"sig\""), - TealOp(Op.byte, "\"key\""), - TealOp(Op.ed25519verify) + TealOp(args[0], Op.byte, "\"data\""), + TealOp(args[1], Op.byte, "\"sig\""), + TealOp(args[2], Op.byte, "\"key\""), + TealOp(expr, Op.ed25519verify) ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/err.py b/pyteal/ast/err.py index 96bfa791a..84bf31399 100644 --- a/pyteal/ast/err.py +++ b/pyteal/ast/err.py @@ -5,11 +5,8 @@ class Err(LeafExpr): """Expression that causes the program to immediately fail when executed.""" - def __init__(self): - pass - def __teal__(self): - op = TealOp(Op.err) + op = TealOp(self, Op.err) return TealBlock.FromOp(op) def __str__(self): diff --git a/pyteal/ast/err_test.py b/pyteal/ast/err_test.py index e52afc3c2..0b55fe542 100644 --- a/pyteal/ast/err_test.py +++ b/pyteal/ast/err_test.py @@ -6,7 +6,7 @@ def test_err(): expr = Err() assert expr.type_of() == TealType.none expected = TealSimpleBlock([ - TealOp(Op.err) + TealOp(expr, Op.err) ]) actual, _ = expr.__teal__() assert actual == expected diff --git a/pyteal/ast/expr.py b/pyteal/ast/expr.py index c52eefcc5..62625eddb 100644 --- a/pyteal/ast/expr.py +++ b/pyteal/ast/expr.py @@ -1,6 +1,6 @@ from abc import ABC, abstractmethod from enum import Enum -from typing import Tuple +from typing import Tuple, List from ..types import TealType from ..ir import TealBlock, TealSimpleBlock @@ -8,6 +8,13 @@ class Expr(ABC): """Abstract base class for PyTeal expressions.""" + def __init__(self): + import traceback + self.trace = traceback.format_stack()[0:-1] + + def getDefinitionTrace(self) -> List[str]: + return self.trace + @abstractmethod def type_of(self) -> TealType: """Get the return type of this expression.""" diff --git a/pyteal/ast/global_.py b/pyteal/ast/global_.py index ba1978fd3..71b831e21 100644 --- a/pyteal/ast/global_.py +++ b/pyteal/ast/global_.py @@ -29,10 +29,11 @@ class Global(LeafExpr): """An expression that accesses a global property.""" def __init__(self, field: GlobalField) -> None: + super().__init__() self.field = field def __teal__(self): - op = TealOp(Op.global_, self.field.arg_name) + op = TealOp(self, Op.global_, self.field.arg_name) return TealBlock.FromOp(op) def __str__(self): diff --git a/pyteal/ast/global_test.py b/pyteal/ast/global_test.py index 374a2abac..fb8c0e14d 100644 --- a/pyteal/ast/global_test.py +++ b/pyteal/ast/global_test.py @@ -7,7 +7,7 @@ def test_global_min_txn_fee(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "MinTxnFee") + TealOp(expr, Op.global_, "MinTxnFee") ]) actual, _ = expr.__teal__() @@ -19,7 +19,7 @@ def test_global_min_balance(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "MinBalance") + TealOp(expr, Op.global_, "MinBalance") ]) actual, _ = expr.__teal__() @@ -31,7 +31,7 @@ def test_global_max_txn_life(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "MaxTxnLife") + TealOp(expr, Op.global_, "MaxTxnLife") ]) actual, _ = expr.__teal__() @@ -43,7 +43,7 @@ def test_global_zero_address(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.global_, "ZeroAddress") + TealOp(expr, Op.global_, "ZeroAddress") ]) actual, _ = expr.__teal__() @@ -55,7 +55,7 @@ def test_global_group_size(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "GroupSize") + TealOp(expr, Op.global_, "GroupSize") ]) actual, _ = expr.__teal__() @@ -67,7 +67,7 @@ def test_global_logic_sig_version(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "LogicSigVersion") + TealOp(expr, Op.global_, "LogicSigVersion") ]) actual, _ = expr.__teal__() @@ -79,7 +79,7 @@ def test_global_round(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "Round") + TealOp(expr, Op.global_, "Round") ]) actual, _ = expr.__teal__() @@ -91,7 +91,7 @@ def test_global_latest_timestamp(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "LatestTimestamp") + TealOp(expr, Op.global_, "LatestTimestamp") ]) actual, _ = expr.__teal__() @@ -103,7 +103,7 @@ def test_global_current_application_id(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.global_, "CurrentApplicationID") + TealOp(expr, Op.global_, "CurrentApplicationID") ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/gtxn.py b/pyteal/ast/gtxn.py index c42d8e7ae..7cefc0266 100644 --- a/pyteal/ast/gtxn.py +++ b/pyteal/ast/gtxn.py @@ -18,7 +18,7 @@ def __str__(self): return "(Gtxn {} {})".format(self.txnIndex, self.field.arg_name) def __teal__(self): - op = TealOp(Op.gtxn, self.txnIndex, self.field.arg_name) + op = TealOp(self, Op.gtxn, self.txnIndex, self.field.arg_name) return TealBlock.FromOp(op) GtxnExpr.__module__ = "pyteal" @@ -34,7 +34,7 @@ def __str__(self): return "(Gtxna {} {} {})".format(self.index, self.field.arg_name, self.index) def __teal__(self): - op = TealOp(Op.gtxna, self.txnIndex, self.field.arg_name, self.index) + op = TealOp(self, Op.gtxna, self.txnIndex, self.field.arg_name, self.index) return TealBlock.FromOp(op) GtxnaExpr.__module__ = "pyteal" diff --git a/pyteal/ast/gtxn_test.py b/pyteal/ast/gtxn_test.py index 300a29fce..fc50b4d2f 100644 --- a/pyteal/ast/gtxn_test.py +++ b/pyteal/ast/gtxn_test.py @@ -19,7 +19,7 @@ def test_gtxn_sender(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "Sender") + TealOp(expr, Op.gtxn, i, "Sender") ]) actual, _ = expr.__teal__() @@ -32,7 +32,7 @@ def test_gtxn_fee(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "Fee") + TealOp(expr, Op.gtxn, i, "Fee") ]) actual, _ = expr.__teal__() @@ -45,7 +45,7 @@ def test_gtxn_first_valid(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "FirstValid") + TealOp(expr, Op.gtxn, i, "FirstValid") ]) actual, _ = expr.__teal__() @@ -58,7 +58,7 @@ def test_gtxn_last_valid(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "LastValid") + TealOp(expr, Op.gtxn, i, "LastValid") ]) actual, _ = expr.__teal__() @@ -71,7 +71,7 @@ def test_gtxn_note(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "Note") + TealOp(expr, Op.gtxn, i, "Note") ]) actual, _ = expr.__teal__() @@ -84,7 +84,7 @@ def test_gtxn_lease(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "Lease") + TealOp(expr, Op.gtxn, i, "Lease") ]) actual, _ = expr.__teal__() @@ -97,7 +97,7 @@ def test_gtxn_receiver(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "Receiver") + TealOp(expr, Op.gtxn, i, "Receiver") ]) actual, _ = expr.__teal__() @@ -110,7 +110,7 @@ def test_gtxn_amount(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "Amount") + TealOp(expr, Op.gtxn, i, "Amount") ]) actual, _ = expr.__teal__() @@ -123,7 +123,7 @@ def test_gtxn_close_remainder_to(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "CloseRemainderTo") + TealOp(expr, Op.gtxn, i, "CloseRemainderTo") ]) actual, _ = expr.__teal__() @@ -136,7 +136,7 @@ def test_gtxn_vote_pk(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "VotePK") + TealOp(expr, Op.gtxn, i, "VotePK") ]) actual, _ = expr.__teal__() @@ -149,7 +149,7 @@ def test_gtxn_selection_pk(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "SelectionPK") + TealOp(expr, Op.gtxn, i, "SelectionPK") ]) actual, _ = expr.__teal__() @@ -162,7 +162,7 @@ def test_gtxn_vote_first(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "VoteFirst") + TealOp(expr, Op.gtxn, i, "VoteFirst") ]) actual, _ = expr.__teal__() @@ -175,7 +175,7 @@ def test_gtxn_vote_last(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "VoteLast") + TealOp(expr, Op.gtxn, i, "VoteLast") ]) actual, _ = expr.__teal__() @@ -188,7 +188,7 @@ def test_gtxn_vote_key_dilution(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "VoteKeyDilution") + TealOp(expr, Op.gtxn, i, "VoteKeyDilution") ]) actual, _ = expr.__teal__() @@ -201,7 +201,7 @@ def test_gtxn_type(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "Type") + TealOp(expr, Op.gtxn, i, "Type") ]) actual, _ = expr.__teal__() @@ -214,7 +214,7 @@ def test_gtxn_type_enum(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "TypeEnum") + TealOp(expr, Op.gtxn, i, "TypeEnum") ]) actual, _ = expr.__teal__() @@ -227,7 +227,7 @@ def test_gtxn_xfer_asset(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "XferAsset") + TealOp(expr, Op.gtxn, i, "XferAsset") ]) actual, _ = expr.__teal__() @@ -240,7 +240,7 @@ def test_gtxn_asset_amount(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "AssetAmount") + TealOp(expr, Op.gtxn, i, "AssetAmount") ]) actual, _ = expr.__teal__() @@ -253,7 +253,7 @@ def test_gtxn_asset_sender(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "AssetSender") + TealOp(expr, Op.gtxn, i, "AssetSender") ]) actual, _ = expr.__teal__() @@ -266,7 +266,7 @@ def test_gtxn_asset_receiver(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "AssetReceiver") + TealOp(expr, Op.gtxn, i, "AssetReceiver") ]) actual, _ = expr.__teal__() @@ -279,7 +279,7 @@ def test_gtxn_asset_close_to(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "AssetCloseTo") + TealOp(expr, Op.gtxn, i, "AssetCloseTo") ]) actual, _ = expr.__teal__() @@ -292,7 +292,7 @@ def test_gtxn_group_index(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "GroupIndex") + TealOp(expr, Op.gtxn, i, "GroupIndex") ]) actual, _ = expr.__teal__() @@ -305,7 +305,7 @@ def test_gtxn_id(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "TxID") + TealOp(expr, Op.gtxn, i, "TxID") ]) actual, _ = expr.__teal__() @@ -318,7 +318,7 @@ def test_txn_application_id(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ApplicationID") + TealOp(expr, Op.gtxn, i, "ApplicationID") ]) actual, _ = expr.__teal__() @@ -331,7 +331,7 @@ def test_txn_on_completion(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "OnCompletion") + TealOp(expr, Op.gtxn, i, "OnCompletion") ]) actual, _ = expr.__teal__() @@ -345,7 +345,7 @@ def test_txn_application_args(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxna, i, "ApplicationArgs", j) + TealOp(expr, Op.gtxna, i, "ApplicationArgs", j) ]) actual, _ = expr.__teal__() @@ -358,7 +358,7 @@ def test_txn_application_args_length(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "NumAppArgs") + TealOp(expr, Op.gtxn, i, "NumAppArgs") ]) actual, _ = expr.__teal__() @@ -372,7 +372,7 @@ def test_txn_accounts(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxna, i, "Accounts", j) + TealOp(expr, Op.gtxna, i, "Accounts", j) ]) actual, _ = expr.__teal__() @@ -385,7 +385,7 @@ def test_txn_accounts_length(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "NumAccounts") + TealOp(expr, Op.gtxn, i, "NumAccounts") ]) actual, _ = expr.__teal__() @@ -398,7 +398,7 @@ def test_txn_approval_program(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ApprovalProgram") + TealOp(expr, Op.gtxn, i, "ApprovalProgram") ]) actual, _ = expr.__teal__() @@ -411,7 +411,7 @@ def test_txn_clear_state_program(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ClearStateProgram") + TealOp(expr, Op.gtxn, i, "ClearStateProgram") ]) actual, _ = expr.__teal__() @@ -424,7 +424,7 @@ def test_txn_rekey_to(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "RekeyTo") + TealOp(expr, Op.gtxn, i, "RekeyTo") ]) actual, _ = expr.__teal__() @@ -437,7 +437,7 @@ def test_txn_config_asset(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAsset") + TealOp(expr, Op.gtxn, i, "ConfigAsset") ]) actual, _ = expr.__teal__() @@ -450,7 +450,7 @@ def test_txn_config_asset_total(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetTotal") + TealOp(expr, Op.gtxn, i, "ConfigAssetTotal") ]) actual, _ = expr.__teal__() @@ -463,7 +463,7 @@ def test_txn_config_asset_decimals(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetDecimals") + TealOp(expr, Op.gtxn, i, "ConfigAssetDecimals") ]) actual, _ = expr.__teal__() @@ -476,7 +476,7 @@ def test_txn_config_asset_default_frozen(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetDefaultFrozen") + TealOp(expr, Op.gtxn, i, "ConfigAssetDefaultFrozen") ]) actual, _ = expr.__teal__() @@ -489,7 +489,7 @@ def test_txn_config_asset_unit_name(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetUnitName") + TealOp(expr, Op.gtxn, i, "ConfigAssetUnitName") ]) actual, _ = expr.__teal__() @@ -502,7 +502,7 @@ def test_txn_config_asset_name(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetName") + TealOp(expr, Op.gtxn, i, "ConfigAssetName") ]) actual, _ = expr.__teal__() @@ -515,7 +515,7 @@ def test_txn_config_asset_url(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetURL") + TealOp(expr, Op.gtxn, i, "ConfigAssetURL") ]) actual, _ = expr.__teal__() @@ -528,7 +528,7 @@ def test_txn_config_asset_metadata_hash(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetMetadataHash") + TealOp(expr, Op.gtxn, i, "ConfigAssetMetadataHash") ]) actual, _ = expr.__teal__() @@ -541,7 +541,7 @@ def test_txn_config_asset_manager(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetManager") + TealOp(expr, Op.gtxn, i, "ConfigAssetManager") ]) actual, _ = expr.__teal__() @@ -554,7 +554,7 @@ def test_txn_config_asset_reserve(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetReserve") + TealOp(expr, Op.gtxn, i, "ConfigAssetReserve") ]) actual, _ = expr.__teal__() @@ -567,7 +567,7 @@ def test_txn_config_asset_freeze(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetFreeze") + TealOp(expr, Op.gtxn, i, "ConfigAssetFreeze") ]) actual, _ = expr.__teal__() @@ -580,7 +580,7 @@ def test_txn_config_asset_clawback(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "ConfigAssetClawback") + TealOp(expr, Op.gtxn, i, "ConfigAssetClawback") ]) actual, _ = expr.__teal__() @@ -593,7 +593,7 @@ def test_txn_freeze_asset(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "FreezeAsset") + TealOp(expr, Op.gtxn, i, "FreezeAsset") ]) actual, _ = expr.__teal__() @@ -606,7 +606,7 @@ def test_txn_freeze_asset_account(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "FreezeAssetAccount") + TealOp(expr, Op.gtxn, i, "FreezeAssetAccount") ]) actual, _ = expr.__teal__() @@ -619,7 +619,7 @@ def test_txn_freeze_asset_frozen(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.gtxn, i, "FreezeAssetFrozen") + TealOp(expr, Op.gtxn, i, "FreezeAssetFrozen") ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/if_.py b/pyteal/ast/if_.py index c936a9d5c..4755ad9f5 100644 --- a/pyteal/ast/if_.py +++ b/pyteal/ast/if_.py @@ -1,6 +1,5 @@ from ..types import TealType, require_type, types_match from ..ir import TealSimpleBlock, TealConditionalBlock -from ..util import new_label from .expr import Expr class If(Expr): @@ -19,6 +18,7 @@ def __init__(self, cond: Expr, thenBranch: Expr, elseBranch: Expr = None) -> Non elseBranch (optional): Expression to evaluate if the condition is false. Must evaluate to the same type as thenBranch, if provided. Defaults to None. """ + super().__init__() require_type(cond.type_of(), TealType.uint64) if elseBranch is None: diff --git a/pyteal/ast/if_test.py b/pyteal/ast/if_test.py index 18496b418..565c51fb5 100644 --- a/pyteal/ast/if_test.py +++ b/pyteal/ast/if_test.py @@ -3,12 +3,13 @@ from .. import * def test_if_int(): - expr = If(Int(0), Int(1), Int(2)) + args = [Int(0), Int(1), Int(2)] + expr = If(args[0], args[1], args[2]) assert expr.type_of() == TealType.uint64 - expected, _ = Int(0).__teal__() - thenBlock, _ = Int(1).__teal__() - elseBlock, _ = Int(2).__teal__() + expected, _ = args[0].__teal__() + thenBlock, _ = args[1].__teal__() + elseBlock, _ = args[2].__teal__() expectedBranch = TealConditionalBlock([]) expectedBranch.setTrueBlock(thenBlock) expectedBranch.setFalseBlock(elseBlock) @@ -22,12 +23,13 @@ def test_if_int(): assert actual == expected def test_if_bytes(): - expr = If(Int(1), Txn.sender(), Txn.receiver()) + args = [Int(1), Txn.sender(), Txn.receiver()] + expr = If(args[0], args[1], args[2]) assert expr.type_of() == TealType.bytes - expected, _ = Int(1).__teal__() - thenBlock, _ = Txn.sender().__teal__() - elseBlock, _ = Txn.receiver().__teal__() + expected, _ = args[0].__teal__() + thenBlock, _ = args[1].__teal__() + elseBlock, _ = args[2].__teal__() expectedBranch = TealConditionalBlock([]) expectedBranch.setTrueBlock(thenBlock) expectedBranch.setFalseBlock(elseBlock) @@ -41,12 +43,13 @@ def test_if_bytes(): assert actual == expected def test_if_none(): - expr = If(Int(0), Pop(Txn.sender()), Pop(Txn.receiver())) + args = [Int(0), Pop(Txn.sender()), Pop(Txn.receiver())] + expr = If(args[0], args[1], args[2]) assert expr.type_of() == TealType.none - expected, _ = Int(0).__teal__() - thenBlockStart, thenBlockEnd = Pop(Txn.sender()).__teal__() - elseBlockStart, elseBlockEnd = Pop(Txn.receiver()).__teal__() + expected, _ = args[0].__teal__() + thenBlockStart, thenBlockEnd = args[1].__teal__() + elseBlockStart, elseBlockEnd = args[2].__teal__() expectedBranch = TealConditionalBlock([]) expectedBranch.setTrueBlock(thenBlockStart) expectedBranch.setFalseBlock(elseBlockStart) @@ -60,11 +63,12 @@ def test_if_none(): assert actual == expected def test_if_single(): - expr = If(Int(1), Pop(Int(1))) + args = [Int(1), Pop(Int(1))] + expr = If(args[0], args[1]) assert expr.type_of() == TealType.none - expected, _ = Int(1).__teal__() - thenBlockStart, thenBlockEnd = Pop(Int(1)).__teal__() + expected, _ = args[0].__teal__() + thenBlockStart, thenBlockEnd = args[1].__teal__() end = TealSimpleBlock([]) expectedBranch = TealConditionalBlock([]) expectedBranch.setTrueBlock(thenBlockStart) diff --git a/pyteal/ast/int.py b/pyteal/ast/int.py index 0319e5aa7..3d9803a25 100644 --- a/pyteal/ast/int.py +++ b/pyteal/ast/int.py @@ -15,6 +15,8 @@ def __init__(self, value: int) -> None: value: The integer value this uint64 will represent. Must be a positive value less than 2**64. """ + super().__init__() + if type(value) is not int: raise TealInputError("invalid input type {} to Int".format(type(value))) elif value >= 0 and value < 2 ** 64: @@ -23,7 +25,7 @@ def __init__(self, value: int) -> None: raise TealInputError("Int {} is out of range".format(value)) def __teal__(self): - op = TealOp(Op.int, self.value) + op = TealOp(self, Op.int, self.value) return TealBlock.FromOp(op) def __str__(self): @@ -43,10 +45,11 @@ def __init__(self, name: str) -> None: Args: name: The name of the enum value. """ + super().__init__() self.name = name def __teal__(self): - op = TealOp(Op.int, self.name) + op = TealOp(self, Op.int, self.name) return TealBlock.FromOp(op) def __str__(self): diff --git a/pyteal/ast/int_test.py b/pyteal/ast/int_test.py index 4fbb7fa00..a294bb4bf 100644 --- a/pyteal/ast/int_test.py +++ b/pyteal/ast/int_test.py @@ -10,7 +10,7 @@ def test_int(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, value) + TealOp(expr, Op.int, value) ]) actual, _ = expr.__teal__() @@ -35,7 +35,7 @@ def test_enum_int(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, "OptIn") + TealOp(expr, Op.int, "OptIn") ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/maybe.py b/pyteal/ast/maybe.py index 4ae68c919..0efd18729 100644 --- a/pyteal/ast/maybe.py +++ b/pyteal/ast/maybe.py @@ -18,6 +18,7 @@ def __init__(self, op: Op, type: TealType, *, immediate_args: List[Union[int, st immediate_args (optional): Immediate arguments for the op. Defaults to None. args (optional): Stack arguments for the op. Defaults to None. """ + super().__init__() self.op = op self.type = type if immediate_args != None: @@ -63,7 +64,7 @@ def __str__(self): return ret_str def __teal__(self): - callStart, callEnd = TealBlock.FromOp(TealOp(self.op, *self.immediate_args), *self.args) + callStart, callEnd = TealBlock.FromOp(TealOp(self, self.op, *self.immediate_args), *self.args) storeOk = self.slotOk.store() storeValue = self.slotValue.store() diff --git a/pyteal/ast/maybe_test.py b/pyteal/ast/maybe_test.py index 9d950d2b5..28cf907b1 100644 --- a/pyteal/ast/maybe_test.py +++ b/pyteal/ast/maybe_test.py @@ -17,17 +17,19 @@ def test_maybe_value(): assert expr.slotOk != expr.slotValue assert expr.hasValue().type_of() == TealType.uint64 - assert expr.hasValue().__teal__() == ScratchLoad(expr.slotOk).__teal__() + with TealComponent.Context.ignoreExprEquality(): + assert expr.hasValue().__teal__() == ScratchLoad(expr.slotOk).__teal__() assert expr.value().type_of() == type - assert expr.value().__teal__() == ScratchLoad(expr.slotValue).__teal__() + with TealComponent.Context.ignoreExprEquality(): + assert expr.value().__teal__() == ScratchLoad(expr.slotValue).__teal__() assert expr.type_of() == TealType.none expected_call = TealSimpleBlock([ - TealOp(op, *iargs), - TealOp(Op.store, expr.slotOk), - TealOp(Op.store, expr.slotValue) + TealOp(expr, op, *iargs), + TealOp(None, Op.store, expr.slotOk), + TealOp(None, Op.store, expr.slotValue) ]) if len(args) == 0: @@ -48,4 +50,5 @@ def test_maybe_value(): actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - assert actual == expected + with TealComponent.Context.ignoreExprEquality(): + assert actual == expected diff --git a/pyteal/ast/naryexpr.py b/pyteal/ast/naryexpr.py index 1a4bb92e2..423926dbc 100644 --- a/pyteal/ast/naryexpr.py +++ b/pyteal/ast/naryexpr.py @@ -12,6 +12,7 @@ class NaryExpr(Expr): """ def __init__(self, op: Op, inputType: TealType, outputType: TealType, args: Sequence[Expr]): + super().__init__() if len(args) < 2: raise TealInputError("NaryExpr requires at least two children.") for arg in args: @@ -32,7 +33,7 @@ def __teal__(self): end = argEnd else: end.setNextBlock(argStart) - opBlock = TealSimpleBlock([TealOp(self.op)]) + opBlock = TealSimpleBlock([TealOp(self, self.op)]) argEnd.setNextBlock(opBlock) end = opBlock diff --git a/pyteal/ast/naryexpr_test.py b/pyteal/ast/naryexpr_test.py index 2d74e77f0..5979be116 100644 --- a/pyteal/ast/naryexpr_test.py +++ b/pyteal/ast/naryexpr_test.py @@ -3,13 +3,14 @@ from .. import * def test_and_two(): - expr = And(Int(1), Int(2)) + args = [Int(1), Int(2)] + expr = And(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.logic_and) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(expr, Op.logic_and) ]) actual, _ = expr.__teal__() @@ -19,15 +20,16 @@ def test_and_two(): assert actual == expected def test_and_three(): - expr = And(Int(1), Int(2), Int(3)) + args = [Int(1), Int(2), Int(3)] + expr = And(args[0], args[1], args[2]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.logic_and), - TealOp(Op.int, 3), - TealOp(Op.logic_and) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(expr, Op.logic_and), + TealOp(args[2], Op.int, 3), + TealOp(expr, Op.logic_and) ]) actual, _ = expr.__teal__() @@ -37,13 +39,14 @@ def test_and_three(): assert actual == expected def test_and_overload(): - expr = Int(1).And(Int(2)) + args = [Int(1), Int(2)] + expr = args[0].And(args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.logic_and) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 2), + TealOp(expr, Op.logic_and) ]) actual, _ = expr.__teal__() @@ -69,13 +72,14 @@ def test_and_invalid(): And(Txn.receiver(), Txn.receiver()) def test_or_two(): - expr = Or(Int(1), Int(0)) + args = [Int(1), Int(0)] + expr = Or(args[0], args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 0), - TealOp(Op.logic_or) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 0), + TealOp(expr, Op.logic_or) ]) actual, _ = expr.__teal__() @@ -85,15 +89,16 @@ def test_or_two(): assert actual == expected def test_or_three(): - expr = Or(Int(0), Int(1), Int(2)) + args = [Int(0), Int(1), Int(2)] + expr = Or(args[0], args[1], args[2]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.int, 1), - TealOp(Op.logic_or), - TealOp(Op.int, 2), - TealOp(Op.logic_or) + TealOp(args[0], Op.int, 0), + TealOp(args[1], Op.int, 1), + TealOp(expr, Op.logic_or), + TealOp(args[2], Op.int, 2), + TealOp(expr, Op.logic_or) ]) actual, _ = expr.__teal__() @@ -103,13 +108,14 @@ def test_or_three(): assert actual == expected def test_or_overload(): - expr = Int(1).Or(Int(0)) + args = [Int(1), Int(0)] + expr = args[0].Or(args[1]) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 0), - TealOp(Op.logic_or) + TealOp(args[0], Op.int, 1), + TealOp(args[1], Op.int, 0), + TealOp(expr, Op.logic_or) ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/nonce.py b/pyteal/ast/nonce.py index 62a9e70f4..a55841b04 100644 --- a/pyteal/ast/nonce.py +++ b/pyteal/ast/nonce.py @@ -1,6 +1,5 @@ from ..errors import TealInputError from .expr import Expr -from ..ir import TealOp, Op from .seq import Seq from .bytes import Bytes from .unaryexpr import Pop @@ -20,6 +19,8 @@ def __init__(self, base: str, nonce: str, child: Expr) -> None: nonce: An arbitrary nonce string that conforms to base. child: The expression to wrap. """ + super().__init__() + if base not in ("utf8", "base16", "base32", "base64"): raise TealInputError("Invalid base: {}".format(base)) @@ -28,15 +29,17 @@ def __init__(self, base: str, nonce: str, child: Expr) -> None: self.nonce_bytes = Bytes(nonce) else: self.nonce_bytes = Bytes(base, nonce) - - def __teal__(self): - return Seq([ + + self.seq = Seq([ Pop(self.nonce_bytes), self.child - ]).__teal__() + ]) + + def __teal__(self): + return self.seq.__teal__() def __str__(self): - return "(nonce: {}) {}".format(self.nonce_bytes.__str__(), self.child.__str__()) + return "(nonce: {}) {}".format(self.nonce_bytes, self.child) def type_of(self): return self.child.type_of() diff --git a/pyteal/ast/nonce_test.py b/pyteal/ast/nonce_test.py index 917e78042..62c054f28 100644 --- a/pyteal/ast/nonce_test.py +++ b/pyteal/ast/nonce_test.py @@ -3,115 +3,129 @@ from .. import * def test_nonce_base32(): - expr = Nonce("base32", "7Z5PWO2C6LFNQFGHWKSK5H47IQP5OJW2M3HA2QPXTY3WTNP5NU2MHBW27M", Int(1)) + arg = Int(1) + expr = Nonce("base32", "7Z5PWO2C6LFNQFGHWKSK5H47IQP5OJW2M3HA2QPXTY3WTNP5NU2MHBW27M", arg) assert expr.type_of() == TealType.uint64 - expected = TealSimpleBlock([ - TealOp(Op.byte, "base32(7Z5PWO2C6LFNQFGHWKSK5H47IQP5OJW2M3HA2QPXTY3WTNP5NU2MHBW27M)"), - TealOp(Op.pop), - TealOp(Op.int, 1) - ]) - actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) + # copying expr from actual.ops[0] and actual.ops[1] because they can't be determined from outside code. + expected = TealSimpleBlock([ + TealOp(actual.ops[0].expr, Op.byte, "base32(7Z5PWO2C6LFNQFGHWKSK5H47IQP5OJW2M3HA2QPXTY3WTNP5NU2MHBW27M)"), + TealOp(actual.ops[1].expr, Op.pop), + TealOp(arg, Op.int, 1) + ]) + assert actual == expected def test_nonce_base32_empty(): - expr = Nonce("base32", "", Int(1)) + arg = Int(1) + expr = Nonce("base32", "", arg) assert expr.type_of() == TealType.uint64 - expected = TealSimpleBlock([ - TealOp(Op.byte, "base32()"), - TealOp(Op.pop), - TealOp(Op.int, 1) - ]) - actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) + # copying expr from actual.ops[0] and actual.ops[1] because they can't be determined from outside code. + expected = TealSimpleBlock([ + TealOp(actual.ops[0].expr, Op.byte, "base32()"), + TealOp(actual.ops[1].expr, Op.pop), + TealOp(arg, Op.int, 1) + ]) + assert actual == expected def test_nonce_base64(): - expr = Nonce("base64", "Zm9vYmE=", Txn.sender()) + arg = Txn.sender() + expr = Nonce("base64", "Zm9vYmE=", arg) assert expr.type_of() == TealType.bytes - expected = TealSimpleBlock([ - TealOp(Op.byte, "base64(Zm9vYmE=)"), - TealOp(Op.pop), - TealOp(Op.txn, "Sender") - ]) - actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) + # copying expr from actual.ops[0] and actual.ops[1] because they can't be determined from outside code. + expected = TealSimpleBlock([ + TealOp(actual.ops[0].expr, Op.byte, "base64(Zm9vYmE=)"), + TealOp(actual.ops[1].expr, Op.pop), + TealOp(arg, Op.txn, "Sender") + ]) + assert actual == expected def test_nonce_base64_empty(): - expr = Nonce("base64", "", Int(1)) + arg = Int(1) + expr = Nonce("base64", "", arg) assert expr.type_of() == TealType.uint64 - expected = TealSimpleBlock([ - TealOp(Op.byte, "base64()"), - TealOp(Op.pop), - TealOp(Op.int, 1) - ]) - actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) + # copying expr from actual.ops[0] and actual.ops[1] because they can't be determined from outside code. + expected = TealSimpleBlock([ + TealOp(actual.ops[0].expr, Op.byte, "base64()"), + TealOp(actual.ops[1].expr, Op.pop), + TealOp(arg, Op.int, 1) + ]) + assert actual == expected def test_nonce_base16(): - expr = Nonce("base16", "A21212EF", Int(1)) + arg = Int(1) + expr = Nonce("base16", "A21212EF", arg) assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([ - TealOp(Op.byte, "0xA21212EF"), - TealOp(Op.pop), - TealOp(Op.int, 1) - ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) + # copying expr from actual.ops[0] and actual.ops[1] because they can't be determined from outside code. + expected = TealSimpleBlock([ + TealOp(actual.ops[0].expr, Op.byte, "0xA21212EF"), + TealOp(actual.ops[1].expr, Op.pop), + TealOp(arg, Op.int, 1) + ]) + assert actual == expected def test_nonce_base16_prefix(): - expr = Nonce("base16", "0xA21212EF", Int(1)) + arg = Int(1) + expr = Nonce("base16", "0xA21212EF", arg) assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([ - TealOp(Op.byte, "0xA21212EF"), - TealOp(Op.pop), - TealOp(Op.int, 1) - ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) + # copying expr from actual.ops[0] and actual.ops[1] because they can't be determined from outside code. + expected = TealSimpleBlock([ + TealOp(actual.ops[0].expr, Op.byte, "0xA21212EF"), + TealOp(actual.ops[1].expr, Op.pop), + TealOp(arg, Op.int, 1) + ]) + assert actual == expected def test_nonce_base16_empty(): - expr = Nonce("base16", "", Int(6)) + arg = Int(6) + expr = Nonce("base16", "", arg) assert expr.type_of() == TealType.uint64 - - expected = TealSimpleBlock([ - TealOp(Op.byte, "0x"), - TealOp(Op.pop), - TealOp(Op.int, 6) - ]) actual, _ = expr.__teal__() actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) + # copying expr from actual.ops[0] and actual.ops[1] because they can't be determined from outside code. + expected = TealSimpleBlock([ + TealOp(actual.ops[0].expr, Op.byte, "0x"), + TealOp(actual.ops[1].expr, Op.pop), + TealOp(arg, Op.int, 6) + ]) + assert actual == expected def test_nonce_invalid(): diff --git a/pyteal/ast/scratch.py b/pyteal/ast/scratch.py index 6b8795c9b..c370de692 100644 --- a/pyteal/ast/scratch.py +++ b/pyteal/ast/scratch.py @@ -55,15 +55,16 @@ def __init__(self, slot: ScratchSlot, type: TealType = TealType.anytype): type (optional): The type being loaded from this slot, if known. Defaults to TealType.anytype. """ + super().__init__() self.slot = slot self.type = type def __str__(self): - return "(Load {})".format(self.slot.__str__()) + return "(Load {})".format(self.slot) def __teal__(self): from ..ir import TealOp, Op, TealBlock - op = TealOp(Op.load, self.slot) + op = TealOp(self, Op.load, self.slot) return TealBlock.FromOp(op) def type_of(self): @@ -81,15 +82,16 @@ def __init__(self, slot: ScratchSlot, value: Expr): slot: The slot to store the value in. value: The value to store. """ + super().__init__() self.slot = slot self.value = value def __str__(self): - return "(Store {} {})".format(str(self.slot), str(self.value)) + return "(Store {} {})".format(self.slot, self.value) def __teal__(self): from ..ir import TealOp, Op, TealBlock - op = TealOp(Op.store, self.slot) + op = TealOp(self, Op.store, self.slot) return TealBlock.FromOp(op, self.value) def type_of(self): @@ -110,14 +112,15 @@ def __init__(self, slot: ScratchSlot): Args: slot: The slot to store the value in. """ + super().__init__() self.slot = slot def __str__(self): - return "(StackStore {})".format(str(self.slot)) + return "(StackStore {})".format(self.slot) def __teal__(self): from ..ir import TealOp, Op, TealBlock - op = TealOp(Op.store, self.slot) + op = TealOp(self, Op.store, self.slot) return TealBlock.FromOp(op) def type_of(self): diff --git a/pyteal/ast/scratch_test.py b/pyteal/ast/scratch_test.py index 10a65ce2d..5dadd5f26 100644 --- a/pyteal/ast/scratch_test.py +++ b/pyteal/ast/scratch_test.py @@ -8,12 +8,13 @@ def test_scratch_slot(): assert slot.__hash__() == slot.__hash__() assert slot != ScratchSlot() - assert slot.store().__teal__()[0] == ScratchStackStore(slot).__teal__()[0] - assert slot.store(Int(1)).__teal__()[0] == ScratchStore(slot, Int(1)).__teal__()[0] + with TealComponent.Context.ignoreExprEquality(): + assert slot.store().__teal__()[0] == ScratchStackStore(slot).__teal__()[0] + assert slot.store(Int(1)).__teal__()[0] == ScratchStore(slot, Int(1)).__teal__()[0] - assert slot.load().type_of() == TealType.anytype - assert slot.load(TealType.uint64).type_of() == TealType.uint64 - assert slot.load().__teal__()[0] == ScratchLoad(slot).__teal__()[0] + assert slot.load().type_of() == TealType.anytype + assert slot.load(TealType.uint64).type_of() == TealType.uint64 + assert slot.load().__teal__()[0] == ScratchLoad(slot).__teal__()[0] def test_scratch_load_default(): slot = ScratchSlot() @@ -21,7 +22,7 @@ def test_scratch_load_default(): assert expr.type_of() == TealType.anytype expected = TealSimpleBlock([ - TealOp(Op.load, slot) + TealOp(expr, Op.load, slot) ]) actual, _ = expr.__teal__() @@ -35,7 +36,7 @@ def test_scratch_load_type(): assert expr.type_of() == type expected = TealSimpleBlock([ - TealOp(Op.load, slot) + TealOp(expr, Op.load, slot) ]) actual, _ = expr.__teal__() @@ -50,7 +51,7 @@ def test_scratch_store(): expected, valueEnd = value.__teal__() storeBlock = TealSimpleBlock([ - TealOp(Op.store, slot) + TealOp(expr, Op.store, slot) ]) valueEnd.setNextBlock(storeBlock) @@ -64,7 +65,7 @@ def test_scratch_stack_store(): assert expr.type_of() == TealType.none expected = TealSimpleBlock([ - TealOp(Op.store, slot) + TealOp(expr, Op.store, slot) ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/scratchvar_test.py b/pyteal/ast/scratchvar_test.py index f4fa03d31..4579096b5 100644 --- a/pyteal/ast/scratchvar_test.py +++ b/pyteal/ast/scratchvar_test.py @@ -36,11 +36,12 @@ def test_scratchvar_type(): def test_scratchvar_store(): myvar = ScratchVar(TealType.bytes) - expr = myvar.store(Bytes("value")) + arg = Bytes("value") + expr = myvar.store(arg) expected = TealSimpleBlock([ - TealOp(Op.byte, "\"value\""), - TealOp(Op.store, myvar.slot), + TealOp(arg, Op.byte, "\"value\""), + TealOp(expr, Op.store, myvar.slot), ]) actual, _ = expr.__teal__() @@ -54,7 +55,7 @@ def test_scratchvar_load(): expr = myvar.load() expected = TealSimpleBlock([ - TealOp(Op.load, myvar.slot) + TealOp(expr, Op.load, myvar.slot) ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/seq.py b/pyteal/ast/seq.py index 34d31e861..de262cfd2 100644 --- a/pyteal/ast/seq.py +++ b/pyteal/ast/seq.py @@ -24,6 +24,8 @@ def __init__(self, exprs: List[Expr]): Int(1) ]) """ + super().__init__() + if len(exprs) == 0: raise TealInputError("Seq requires children.") for i, expr in enumerate(exprs): diff --git a/pyteal/ast/seq_test.py b/pyteal/ast/seq_test.py index 86ce01aa2..6dacb878f 100644 --- a/pyteal/ast/seq_test.py +++ b/pyteal/ast/seq_test.py @@ -3,10 +3,11 @@ from .. import * def test_seq_one(): - expr = Seq([Int(0)]) + items = [Int(0)] + expr = Seq(items) assert expr.type_of() == TealType.uint64 - expected, _ = Int(0).__teal__() + expected, _ = items[0].__teal__() actual, _ = expr.__teal__() diff --git a/pyteal/ast/substring.py b/pyteal/ast/substring.py index 5c2e38f99..28f2a5e67 100644 --- a/pyteal/ast/substring.py +++ b/pyteal/ast/substring.py @@ -16,6 +16,8 @@ def __init__(self, string: Expr, start: Expr, end: Expr) -> None: start: The starting index for the substring. end: The ending index for the substring. """ + super().__init__() + require_type(string.type_of(), TealType.bytes) require_type(start.type_of(), TealType.uint64) require_type(end.type_of(), TealType.uint64) @@ -25,7 +27,7 @@ def __init__(self, string: Expr, start: Expr, end: Expr) -> None: self.end = end def __teal__(self): - return TealBlock.FromOp(TealOp(Op.substring3), self.string, self.start, self.end) + return TealBlock.FromOp(TealOp(self, Op.substring3), self.string, self.start, self.end) def __str__(self): return "(substring {} {} {})".format(self.string, self.start, self.end) diff --git a/pyteal/ast/substring_test.py b/pyteal/ast/substring_test.py index f08f46356..0594aea02 100644 --- a/pyteal/ast/substring_test.py +++ b/pyteal/ast/substring_test.py @@ -3,14 +3,15 @@ from .. import * def test_substring(): - expr = Substring(Bytes("my string"), Int(0), Int(2)) + args = [Bytes("my string"), Int(0), Int(2)] + expr = Substring(args[0], args[1], args[2]) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "\"my string\""), - TealOp(Op.int, 0), - TealOp(Op.int, 2), - TealOp(Op.substring3) + TealOp(args[0], Op.byte, "\"my string\""), + TealOp(args[1], Op.int, 0), + TealOp(args[2], Op.int, 2), + TealOp(expr, Op.substring3) ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/tmpl.py b/pyteal/ast/tmpl.py index 635be0dd6..f8be717a5 100644 --- a/pyteal/ast/tmpl.py +++ b/pyteal/ast/tmpl.py @@ -7,6 +7,7 @@ class Tmpl(LeafExpr): """Template expression for creating placeholder values.""" def __init__(self, op: Op, type: TealType, name: str) -> None: + super().__init__() valid_tmpl(name) self.op = op self.type = type @@ -16,7 +17,7 @@ def __str__(self): return "(Tmpl {} {})".format(self.op, self.name) def __teal__(self): - op = TealOp(self.op, self.name) + op = TealOp(self, self.op, self.name) return TealBlock.FromOp(op) def type_of(self): diff --git a/pyteal/ast/tmpl_test.py b/pyteal/ast/tmpl_test.py index 0f90f7a84..612007a17 100644 --- a/pyteal/ast/tmpl_test.py +++ b/pyteal/ast/tmpl_test.py @@ -7,7 +7,7 @@ def test_tmpl_int(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, "TMPL_AMNT") + TealOp(expr, Op.int, "TMPL_AMNT") ]) actual, _ = expr.__teal__() @@ -23,7 +23,7 @@ def test_tmpl_bytes(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.byte, "TMPL_NOTE") + TealOp(expr, Op.byte, "TMPL_NOTE") ]) actual, _ = expr.__teal__() @@ -39,7 +39,7 @@ def test_tmpl_addr(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.addr, "TMPL_RECEIVER0") + TealOp(expr, Op.addr, "TMPL_RECEIVER0") ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index 4fc1b1276..74752d681 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -84,13 +84,14 @@ class TxnExpr(LeafExpr): """An expression that accesses a transaction field from the current transaction.""" def __init__(self, field: TxnField) -> None: + super().__init__() self.field = field def __str__(self): return "(Txn {})".format(self.field.arg_name) def __teal__(self): - op = TealOp(Op.txn, self.field.arg_name) + op = TealOp(self, Op.txn, self.field.arg_name) return TealBlock.FromOp(op) def type_of(self): @@ -102,6 +103,7 @@ class TxnaExpr(LeafExpr): """An expression that accesses a transaction array field from the current transaction.""" def __init__(self, field: TxnField, index: int) -> None: + super().__init__() self.field = field self.index = index @@ -109,7 +111,7 @@ def __str__(self): return "(Txna {} {})".format(self.field.arg_name, self.index) def __teal__(self): - op = TealOp(Op.txna, self.field.arg_name, self.index) + op = TealOp(self, Op.txna, self.field.arg_name, self.index) return TealBlock.FromOp(op) def type_of(self): diff --git a/pyteal/ast/txn_test.py b/pyteal/ast/txn_test.py index f09f00db4..fafef740c 100644 --- a/pyteal/ast/txn_test.py +++ b/pyteal/ast/txn_test.py @@ -7,7 +7,7 @@ def test_txn_sender(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "Sender") + TealOp(expr, Op.txn, "Sender") ]) actual, _ = expr.__teal__() @@ -19,7 +19,7 @@ def test_txn_fee(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "Fee") + TealOp(expr, Op.txn, "Fee") ]) actual, _ = expr.__teal__() @@ -31,7 +31,7 @@ def test_txn_first_valid(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "FirstValid") + TealOp(expr, Op.txn, "FirstValid") ]) actual, _ = expr.__teal__() @@ -43,7 +43,7 @@ def test_txn_last_valid(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "LastValid") + TealOp(expr, Op.txn, "LastValid") ]) actual, _ = expr.__teal__() @@ -55,7 +55,7 @@ def test_txn_note(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "Note") + TealOp(expr, Op.txn, "Note") ]) actual, _ = expr.__teal__() @@ -67,7 +67,7 @@ def test_txn_lease(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "Lease") + TealOp(expr, Op.txn, "Lease") ]) actual, _ = expr.__teal__() @@ -79,7 +79,7 @@ def test_txn_receiver(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "Receiver") + TealOp(expr, Op.txn, "Receiver") ]) actual, _ = expr.__teal__() @@ -91,7 +91,7 @@ def test_txn_amount(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "Amount") + TealOp(expr, Op.txn, "Amount") ]) actual, _ = expr.__teal__() @@ -103,7 +103,7 @@ def test_txn_close_remainder_to(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "CloseRemainderTo") + TealOp(expr, Op.txn, "CloseRemainderTo") ]) actual, _ = expr.__teal__() @@ -115,7 +115,7 @@ def test_txn_vote_pk(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "VotePK") + TealOp(expr, Op.txn, "VotePK") ]) actual, _ = expr.__teal__() @@ -127,7 +127,7 @@ def test_txn_selection_pk(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "SelectionPK") + TealOp(expr, Op.txn, "SelectionPK") ]) actual, _ = expr.__teal__() @@ -139,7 +139,7 @@ def test_txn_vote_first(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "VoteFirst") + TealOp(expr, Op.txn, "VoteFirst") ]) actual, _ = expr.__teal__() @@ -151,7 +151,7 @@ def test_txn_vote_last(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "VoteLast") + TealOp(expr, Op.txn, "VoteLast") ]) actual, _ = expr.__teal__() @@ -163,7 +163,7 @@ def test_txn_vote_key_dilution(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "VoteKeyDilution") + TealOp(expr, Op.txn, "VoteKeyDilution") ]) actual, _ = expr.__teal__() @@ -175,7 +175,7 @@ def test_txn_type(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "Type") + TealOp(expr, Op.txn, "Type") ]) actual, _ = expr.__teal__() @@ -187,7 +187,7 @@ def test_txn_type_enum(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "TypeEnum") + TealOp(expr, Op.txn, "TypeEnum") ]) actual, _ = expr.__teal__() @@ -199,7 +199,7 @@ def test_txn_xfer_asset(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "XferAsset") + TealOp(expr, Op.txn, "XferAsset") ]) actual, _ = expr.__teal__() @@ -211,7 +211,7 @@ def test_txn_asset_amount(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "AssetAmount") + TealOp(expr, Op.txn, "AssetAmount") ]) actual, _ = expr.__teal__() @@ -223,7 +223,7 @@ def test_txn_asset_sender(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "AssetSender") + TealOp(expr, Op.txn, "AssetSender") ]) actual, _ = expr.__teal__() @@ -235,7 +235,7 @@ def test_txn_asset_receiver(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "AssetReceiver") + TealOp(expr, Op.txn, "AssetReceiver") ]) actual, _ = expr.__teal__() @@ -247,7 +247,7 @@ def test_txn_asset_close_to(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "AssetCloseTo") + TealOp(expr, Op.txn, "AssetCloseTo") ]) actual, _ = expr.__teal__() @@ -259,7 +259,7 @@ def test_txn_group_index(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "GroupIndex") + TealOp(expr, Op.txn, "GroupIndex") ]) actual, _ = expr.__teal__() @@ -271,7 +271,7 @@ def test_txn_id(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "TxID") + TealOp(expr, Op.txn, "TxID") ]) actual, _ = expr.__teal__() @@ -283,7 +283,7 @@ def test_txn_application_id(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "ApplicationID") + TealOp(expr, Op.txn, "ApplicationID") ]) actual, _ = expr.__teal__() @@ -295,7 +295,7 @@ def test_txn_on_completion(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "OnCompletion") + TealOp(expr, Op.txn, "OnCompletion") ]) actual, _ = expr.__teal__() @@ -308,7 +308,7 @@ def test_txn_application_args(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txna, "ApplicationArgs", i) + TealOp(expr, Op.txna, "ApplicationArgs", i) ]) actual, _ = expr.__teal__() @@ -320,7 +320,7 @@ def test_txn_application_args_length(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "NumAppArgs") + TealOp(expr, Op.txn, "NumAppArgs") ]) actual, _ = expr.__teal__() @@ -333,7 +333,7 @@ def test_txn_accounts(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txna, "Accounts", i) + TealOp(expr, Op.txna, "Accounts", i) ]) actual, _ = expr.__teal__() @@ -345,7 +345,7 @@ def test_txn_accounts_length(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "NumAccounts") + TealOp(expr, Op.txn, "NumAccounts") ]) actual, _ = expr.__teal__() @@ -357,7 +357,7 @@ def test_txn_approval_program(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ApprovalProgram") + TealOp(expr, Op.txn, "ApprovalProgram") ]) actual, _ = expr.__teal__() @@ -369,7 +369,7 @@ def test_txn_clear_state_program(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ClearStateProgram") + TealOp(expr, Op.txn, "ClearStateProgram") ]) actual, _ = expr.__teal__() @@ -381,7 +381,7 @@ def test_txn_rekey_to(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "RekeyTo") + TealOp(expr, Op.txn, "RekeyTo") ]) actual, _ = expr.__teal__() @@ -393,7 +393,7 @@ def test_txn_config_asset(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAsset") + TealOp(expr, Op.txn, "ConfigAsset") ]) actual, _ = expr.__teal__() @@ -405,7 +405,7 @@ def test_txn_config_asset_total(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetTotal") + TealOp(expr, Op.txn, "ConfigAssetTotal") ]) actual, _ = expr.__teal__() @@ -417,7 +417,7 @@ def test_txn_config_asset_decimals(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetDecimals") + TealOp(expr, Op.txn, "ConfigAssetDecimals") ]) actual, _ = expr.__teal__() @@ -429,7 +429,7 @@ def test_txn_config_asset_default_frozen(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetDefaultFrozen") + TealOp(expr, Op.txn, "ConfigAssetDefaultFrozen") ]) actual, _ = expr.__teal__() @@ -441,7 +441,7 @@ def test_txn_config_asset_unit_name(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetUnitName") + TealOp(expr, Op.txn, "ConfigAssetUnitName") ]) actual, _ = expr.__teal__() @@ -453,7 +453,7 @@ def test_txn_config_asset_name(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetName") + TealOp(expr, Op.txn, "ConfigAssetName") ]) actual, _ = expr.__teal__() @@ -465,7 +465,7 @@ def test_txn_config_asset_url(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetURL") + TealOp(expr, Op.txn, "ConfigAssetURL") ]) actual, _ = expr.__teal__() @@ -477,7 +477,7 @@ def test_txn_config_asset_metadata_hash(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetMetadataHash") + TealOp(expr, Op.txn, "ConfigAssetMetadataHash") ]) actual, _ = expr.__teal__() @@ -489,7 +489,7 @@ def test_txn_config_asset_manager(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetManager") + TealOp(expr, Op.txn, "ConfigAssetManager") ]) actual, _ = expr.__teal__() @@ -501,7 +501,7 @@ def test_txn_config_asset_reserve(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetReserve") + TealOp(expr, Op.txn, "ConfigAssetReserve") ]) actual, _ = expr.__teal__() @@ -513,7 +513,7 @@ def test_txn_config_asset_freeze(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetFreeze") + TealOp(expr, Op.txn, "ConfigAssetFreeze") ]) actual, _ = expr.__teal__() @@ -525,7 +525,7 @@ def test_txn_config_asset_clawback(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "ConfigAssetClawback") + TealOp(expr, Op.txn, "ConfigAssetClawback") ]) actual, _ = expr.__teal__() @@ -537,7 +537,7 @@ def test_txn_freeze_asset(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "FreezeAsset") + TealOp(expr, Op.txn, "FreezeAsset") ]) actual, _ = expr.__teal__() @@ -549,7 +549,7 @@ def test_txn_freeze_asset_account(): assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.txn, "FreezeAssetAccount") + TealOp(expr, Op.txn, "FreezeAssetAccount") ]) actual, _ = expr.__teal__() @@ -561,7 +561,7 @@ def test_txn_freeze_asset_frozen(): assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "FreezeAssetFrozen") + TealOp(expr, Op.txn, "FreezeAssetFrozen") ]) actual, _ = expr.__teal__() diff --git a/pyteal/ast/unaryexpr.py b/pyteal/ast/unaryexpr.py index faa171bf8..837fbe405 100644 --- a/pyteal/ast/unaryexpr.py +++ b/pyteal/ast/unaryexpr.py @@ -6,13 +6,14 @@ class UnaryExpr(Expr): """An expression with a single argument.""" def __init__(self, op: Op, inputType: TealType, outputType: TealType, arg: Expr) -> None: + super().__init__() require_type(arg.type_of(), inputType) self.op = op self.outputType = outputType self.arg = arg def __teal__(self): - return TealBlock.FromOp(TealOp(self.op), self.arg) + return TealBlock.FromOp(TealOp(self, self.op), self.arg) def __str__(self): return "({} {})".format(self.op, self.arg) diff --git a/pyteal/ast/unaryexpr_test.py b/pyteal/ast/unaryexpr_test.py index 780d25919..d31ffaa4c 100644 --- a/pyteal/ast/unaryexpr_test.py +++ b/pyteal/ast/unaryexpr_test.py @@ -3,12 +3,13 @@ from .. import * def test_btoi(): - expr = Btoi(Arg(1)) + arg = Arg(1) + expr = Btoi(arg) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.arg, 1), - TealOp(Op.btoi) + TealOp(arg, Op.arg, 1), + TealOp(expr, Op.btoi) ]) actual, _ = expr.__teal__() @@ -22,12 +23,13 @@ def test_btoi_invalid(): Btoi(Int(1)) def test_itob(): - expr = Itob(Int(1)) + arg = Int(1) + expr = Itob(arg) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.itob) + TealOp(arg, Op.int, 1), + TealOp(expr, Op.itob) ]) actual, _ = expr.__teal__() @@ -41,12 +43,13 @@ def test_itob_invalid(): Itob(Arg(1)) def test_len(): - expr = Len(Txn.receiver()) + arg = Txn.receiver() + expr = Len(arg) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.txn, "Receiver"), - TealOp(Op.len) + TealOp(arg, Op.txn, "Receiver"), + TealOp(expr, Op.len) ]) actual, _ = expr.__teal__() @@ -60,12 +63,13 @@ def test_len_invalid(): Len(Int(1)) def test_sha256(): - expr = Sha256(Arg(0)) + arg = Arg(0) + expr = Sha256(arg) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.arg, 0), - TealOp(Op.sha256) + TealOp(arg, Op.arg, 0), + TealOp(expr, Op.sha256) ]) actual, _ = expr.__teal__() @@ -79,12 +83,13 @@ def test_sha256_invalid(): Sha256(Int(1)) def test_sha512_256(): - expr = Sha512_256(Arg(0)) + arg = Arg(0) + expr = Sha512_256(arg) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.arg, 0), - TealOp(Op.sha512_256) + TealOp(arg, Op.arg, 0), + TealOp(expr, Op.sha512_256) ]) actual, _ = expr.__teal__() @@ -98,12 +103,13 @@ def test_sha512_256_invalid(): Sha512_256(Int(1)) def test_keccak256(): - expr = Keccak256(Arg(0)) + arg = Arg(0) + expr = Keccak256(arg) assert expr.type_of() == TealType.bytes expected = TealSimpleBlock([ - TealOp(Op.arg, 0), - TealOp(Op.keccak256) + TealOp(arg, Op.arg, 0), + TealOp(expr, Op.keccak256) ]) actual, _ = expr.__teal__() @@ -117,12 +123,13 @@ def test_keccak256_invalid(): Keccak256(Int(1)) def test_not(): - expr = Not(Int(1)) + arg = Int(1) + expr = Not(arg) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.logic_not) + TealOp(arg, Op.int, 1), + TealOp(expr, Op.logic_not) ]) actual, _ = expr.__teal__() @@ -136,12 +143,13 @@ def test_not_invalid(): Not(Txn.receiver()) def test_bitwise_not(): - expr = BitwiseNot(Int(2)) + arg = Int(2) + expr = BitwiseNot(arg) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 2), - TealOp(Op.bitwise_not) + TealOp(arg, Op.int, 2), + TealOp(expr, Op.bitwise_not) ]) actual, _ = expr.__teal__() @@ -151,12 +159,13 @@ def test_bitwise_not(): assert actual == expected def test_bitwise_not_overload(): - expr = ~Int(10) + arg = Int(10) + expr = ~arg assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 10), - TealOp(Op.bitwise_not) + TealOp(arg, Op.int, 10), + TealOp(expr, Op.bitwise_not) ]) actual, _ = expr.__teal__() @@ -170,12 +179,13 @@ def test_bitwise_not_invalid(): BitwiseNot(Txn.receiver()) def test_pop(): - expr_int = Pop(Int(3)) + arg_int = Int(3) + expr_int = Pop(arg_int) assert expr_int.type_of() == TealType.none expected_int = TealSimpleBlock([ - TealOp(Op.int, 3), - TealOp(Op.pop) + TealOp(arg_int, Op.int, 3), + TealOp(expr_int, Op.pop) ]) actual_int, _ = expr_int.__teal__() @@ -184,12 +194,13 @@ def test_pop(): assert actual_int == expected_int - expr_bytes = Pop(Txn.receiver()) + arg_bytes = Txn.receiver() + expr_bytes = Pop(arg_bytes) assert expr_bytes.type_of() == TealType.none expected_bytes = TealSimpleBlock([ - TealOp(Op.txn, "Receiver"), - TealOp(Op.pop) + TealOp(arg_bytes, Op.txn, "Receiver"), + TealOp(expr_bytes, Op.pop) ]) actual_bytes, _ = expr_bytes.__teal__() @@ -204,12 +215,13 @@ def test_pop_invalid(): Pop(expr) def test_return(): - expr = Return(Int(1)) + arg = Int(1) + expr = Return(arg) assert expr.type_of() == TealType.none expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.return_) + TealOp(arg, Op.int, 1), + TealOp(expr, Op.return_) ]) actual, _ = expr.__teal__() @@ -223,12 +235,13 @@ def test_return_invalid(): Return(Txn.receiver()) def test_balance(): - expr = Balance(Int(0)) + arg = Int(0) + expr = Balance(arg) assert expr.type_of() == TealType.uint64 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.balance) + TealOp(arg, Op.int, 0), + TealOp(expr, Op.balance) ]) actual, _ = expr.__teal__() diff --git a/pyteal/compiler.py b/pyteal/compiler.py index 174ae9bf4..c754d8b94 100644 --- a/pyteal/compiler.py +++ b/pyteal/compiler.py @@ -64,7 +64,7 @@ def flattenBlocks(blocks: List[TealBlock]) -> List[TealComponent]: nextIndex = blocks.index(simpleBlock.nextBlock, i+1) if nextIndex != i + 1: references[nextIndex] += 1 - code.append(TealOp(Op.b, indexToLabel(nextIndex))) + code.append(TealOp(None, Op.b, indexToLabel(nextIndex))) elif type(block) is TealConditionalBlock: conditionalBlock = cast(TealConditionalBlock, block) assert conditionalBlock.trueBlock is not None @@ -75,26 +75,26 @@ def flattenBlocks(blocks: List[TealBlock]) -> List[TealComponent]: if falseIndex == i + 1: references[trueIndex] += 1 - code.append(TealOp(Op.bnz, indexToLabel(trueIndex))) + code.append(TealOp(None, Op.bnz, indexToLabel(trueIndex))) continue if trueIndex == i + 1: references[falseIndex] += 1 - code.append(TealOp(Op.bz, indexToLabel(falseIndex))) + code.append(TealOp(None, Op.bz, indexToLabel(falseIndex))) continue references[trueIndex] += 1 - code.append(TealOp(Op.bnz, indexToLabel(trueIndex))) + code.append(TealOp(None, Op.bnz, indexToLabel(trueIndex))) references[falseIndex] += 1 - code.append(TealOp(Op.b, indexToLabel(falseIndex))) + code.append(TealOp(None, Op.b, indexToLabel(falseIndex))) else: raise TealInternalError("Unrecognized block type: {}".format(type(block))) teal: List[TealComponent] = [] for i, code in enumerate(codeblocks): if references[i] != 0: - teal.append(TealLabel(indexToLabel(i))) + teal.append(TealLabel(None, indexToLabel(i))) teal += code return teal @@ -145,17 +145,22 @@ def compileTeal(ast: Expr, mode: Mode, version: int = DEFAULT_TEAL_VERSION) -> s A TEAL assembly program compiled from the input expression. Raises: - TealInputError: if an operation in ast is not supported by the supplied mode. + TealInputError: if an operation in ast is not supported by the supplied mode and version. """ - if not (MIN_TEAL_VERSION <= version <= MAX_TEAL_VERSION): - raise TealInputError("Unsupported TEAL version: {}. Excepted a number in the range [{}, {}]".format(version, MIN_TEAL_VERSION, MAX_TEAL_VERSION)) + if not (MIN_TEAL_VERSION <= version <= MAX_TEAL_VERSION) or type(version) != int: + raise TealInputError("Unsupported TEAL version: {}. Excepted an integer in the range [{}, {}]".format(version, MIN_TEAL_VERSION, MAX_TEAL_VERSION)) start, _ = ast.__teal__() start.addIncoming() - start.validate() + start.validateTree() start = TealBlock.NormalizeBlocks(start) - start.validate() + start.validateTree() + + errors = start.validateSlots() + if len(errors) > 0: + msg = 'Encountered {} error{} during compilation'.format(len(errors), 's' if len(errors) != 1 else '') + raise TealInternalError(msg) from errors[0] order = sortBlocks(start) teal = flattenBlocks(order) @@ -170,15 +175,11 @@ def compileTeal(ast: Expr, mode: Mode, version: int = DEFAULT_TEAL_VERSION) -> s if len(slots) > NUM_SLOTS: # TODO: identify which slots can be reused - raise TealInternalError("Too many slots in use: {}, maximum is {}".format(slots, NUM_SLOTS)) + raise TealInternalError("Too many slots in use: {}, maximum is {}".format(len(slots), NUM_SLOTS)) - # TODO: convert slots to a list with a defined order so that generated code is deterministic - location = 0 - while len(slots) > 0: - slot = slots.pop() + for index, slot in enumerate(sorted(slots, key=lambda slot: slot.id)): for stmt in teal: - stmt.assignSlot(slot, location) - location += 1 + stmt.assignSlot(slot, index) lines = ["#pragma version {}".format(version)] lines += [i.assemble() for i in teal] diff --git a/pyteal/compiler_test.py b/pyteal/compiler_test.py index 52b6c1a46..abb75417d 100644 --- a/pyteal/compiler_test.py +++ b/pyteal/compiler_test.py @@ -4,9 +4,9 @@ from .compiler import sortBlocks, flattenBlocks def test_sort_single(): - block = TealSimpleBlock([TealOp(Op.int, 1)]) + block = TealSimpleBlock([TealOp(None, Op.int, 1)]) block.addIncoming() - block.validate() + block.validateTree() expected = [block] actual = sortBlocks(block) @@ -14,17 +14,17 @@ def test_sort_single(): assert actual == expected def test_sort_sequence(): - block5 = TealSimpleBlock([TealOp(Op.int, 5)]) - block4 = TealSimpleBlock([TealOp(Op.int, 4)]) + block5 = TealSimpleBlock([TealOp(None, Op.int, 5)]) + block4 = TealSimpleBlock([TealOp(None, Op.int, 4)]) block4.setNextBlock(block5) - block3 = TealSimpleBlock([TealOp(Op.int, 3)]) + block3 = TealSimpleBlock([TealOp(None, Op.int, 3)]) block3.setNextBlock(block4) - block2 = TealSimpleBlock([TealOp(Op.int, 2)]) + block2 = TealSimpleBlock([TealOp(None, Op.int, 2)]) block2.setNextBlock(block3) - block1 = TealSimpleBlock([TealOp(Op.int, 1)]) + block1 = TealSimpleBlock([TealOp(None, Op.int, 1)]) block1.setNextBlock(block2) block1.addIncoming() - block1.validate() + block1.validateTree() expected = [block1, block2, block3, block4, block5] actual = sortBlocks(block1) @@ -32,13 +32,13 @@ def test_sort_sequence(): assert actual == expected def test_sort_branch(): - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) block.addIncoming() - block.validate() + block.validateTree() expected = [block, blockFalse, blockTrue] actual = sortBlocks(block) @@ -46,19 +46,19 @@ def test_sort_branch(): assert actual == expected def test_sort_multiple_branch(): - blockTrueTrue = TealSimpleBlock([TealOp(Op.byte, "\"true true\"")]) - blockTrueFalse = TealSimpleBlock([TealOp(Op.byte, "\"true false\"")]) + blockTrueTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true true\"")]) + blockTrueFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"true false\"")]) blockTrueBranch = TealConditionalBlock([]) blockTrueBranch.setTrueBlock(blockTrueTrue) blockTrueBranch.setFalseBlock(blockTrueFalse) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockTrueBranch) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) block.addIncoming() - block.validate() + block.validateTree() expected = [block, blockFalse, blockTrue, blockTrueBranch, blockTrueFalse, blockTrueTrue] actual = sortBlocks(block) @@ -66,16 +66,16 @@ def test_sort_multiple_branch(): assert actual == expected def test_sort_branch_converge(): - blockEnd = TealSimpleBlock([TealOp(Op.return_)]) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockEnd = TealSimpleBlock([TealOp(None, Op.return_)]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockEnd) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) blockFalse.setNextBlock(blockEnd) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) block.addIncoming() - block.validate() + block.validateTree() expected = [block, blockFalse, blockTrue, blockEnd] actual = sortBlocks(block) @@ -102,10 +102,10 @@ def test_flatten_single_empty(): def test_flatten_single_one(): blocks = [ - TealSimpleBlock([TealOp(Op.int, 1)]) + TealSimpleBlock([TealOp(None, Op.int, 1)]) ] - expected = [TealOp(Op.int, 1)] + expected = [TealOp(None, Op.int, 1)] actual = flattenBlocks(blocks) assert actual == expected @@ -113,167 +113,167 @@ def test_flatten_single_one(): def test_flatten_single_many(): blocks = [ TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.add), - TealOp(Op.add) + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 3), + TealOp(None, Op.add), + TealOp(None, Op.add) ]) ] expected = [ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.add), - TealOp(Op.add) + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 3), + TealOp(None, Op.add), + TealOp(None, Op.add) ] actual = flattenBlocks(blocks) assert actual == expected def test_flatten_sequence(): - block5 = TealSimpleBlock([TealOp(Op.int, 5)]) - block4 = TealSimpleBlock([TealOp(Op.int, 4)]) + block5 = TealSimpleBlock([TealOp(None, Op.int, 5)]) + block4 = TealSimpleBlock([TealOp(None, Op.int, 4)]) block4.setNextBlock(block5) - block3 = TealSimpleBlock([TealOp(Op.int, 3)]) + block3 = TealSimpleBlock([TealOp(None, Op.int, 3)]) block3.setNextBlock(block4) - block2 = TealSimpleBlock([TealOp(Op.int, 2)]) + block2 = TealSimpleBlock([TealOp(None, Op.int, 2)]) block2.setNextBlock(block3) - block1 = TealSimpleBlock([TealOp(Op.int, 1)]) + block1 = TealSimpleBlock([TealOp(None, Op.int, 1)]) block1.setNextBlock(block2) block1.addIncoming() - block1.validate() + block1.validateTree() blocks = [block1, block2, block3, block4, block5] expected = [ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.int, 4), - TealOp(Op.int, 5) + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 3), + TealOp(None, Op.int, 4), + TealOp(None, Op.int, 5) ] actual = flattenBlocks(blocks) assert actual == expected def test_flatten_branch(): - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\""), TealOp(Op.return_)]) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\""), TealOp(Op.return_)]) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\""), TealOp(None, Op.return_)]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\""), TealOp(None, Op.return_)]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) block.addIncoming() - block.validate() + block.validateTree() blocks = [block, blockFalse, blockTrue] expected = [ - TealOp(Op.int, 1), - TealOp(Op.bnz, "l2"), - TealOp(Op.byte, "\"false\""), - TealOp(Op.return_), - TealLabel("l2"), - TealOp(Op.byte, "\"true\""), - TealOp(Op.return_) + TealOp(None, Op.int, 1), + TealOp(None, Op.bnz, "l2"), + TealOp(None, Op.byte, "\"false\""), + TealOp(None, Op.return_), + TealLabel(None, "l2"), + TealOp(None, Op.byte, "\"true\""), + TealOp(None, Op.return_) ] actual = flattenBlocks(blocks) assert actual == expected def test_flatten_branch_converge(): - blockEnd = TealSimpleBlock([TealOp(Op.return_)]) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockEnd = TealSimpleBlock([TealOp(None, Op.return_)]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockEnd) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) blockFalse.setNextBlock(blockEnd) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) block.addIncoming() - block.validate() + block.validateTree() blocks = [block, blockFalse, blockTrue, blockEnd] expected = [ - TealOp(Op.int, 1), - TealOp(Op.bnz, "l2"), - TealOp(Op.byte, "\"false\""), - TealOp(Op.b, "l3"), - TealLabel("l2"), - TealOp(Op.byte, "\"true\""), - TealLabel("l3"), - TealOp(Op.return_) + TealOp(None, Op.int, 1), + TealOp(None, Op.bnz, "l2"), + TealOp(None, Op.byte, "\"false\""), + TealOp(None, Op.b, "l3"), + TealLabel(None, "l2"), + TealOp(None, Op.byte, "\"true\""), + TealLabel(None, "l3"), + TealOp(None, Op.return_) ] actual = flattenBlocks(blocks) assert actual == expected def test_flatten_multiple_branch(): - blockTrueTrue = TealSimpleBlock([TealOp(Op.byte, "\"true true\""), TealOp(Op.return_)]) - blockTrueFalse = TealSimpleBlock([TealOp(Op.byte, "\"true false\""), TealOp(Op.err)]) + blockTrueTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true true\""), TealOp(None, Op.return_)]) + blockTrueFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"true false\""), TealOp(None, Op.err)]) blockTrueBranch = TealConditionalBlock([]) blockTrueBranch.setTrueBlock(blockTrueTrue) blockTrueBranch.setFalseBlock(blockTrueFalse) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockTrueBranch) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\""), TealOp(Op.return_)]) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\""), TealOp(None, Op.return_)]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) block.addIncoming() - block.validate() + block.validateTree() blocks = [block, blockFalse, blockTrue, blockTrueBranch, blockTrueFalse, blockTrueTrue] expected = [ - TealOp(Op.int, 1), - TealOp(Op.bnz, "l2"), - TealOp(Op.byte, "\"false\""), - TealOp(Op.return_), - TealLabel("l2"), - TealOp(Op.byte, "\"true\""), - TealOp(Op.bnz, "l5"), - TealOp(Op.byte, "\"true false\""), - TealOp(Op.err), - TealLabel("l5"), - TealOp(Op.byte, "\"true true\""), - TealOp(Op.return_) + TealOp(None, Op.int, 1), + TealOp(None, Op.bnz, "l2"), + TealOp(None, Op.byte, "\"false\""), + TealOp(None, Op.return_), + TealLabel(None, "l2"), + TealOp(None, Op.byte, "\"true\""), + TealOp(None, Op.bnz, "l5"), + TealOp(None, Op.byte, "\"true false\""), + TealOp(None, Op.err), + TealLabel(None, "l5"), + TealOp(None, Op.byte, "\"true true\""), + TealOp(None, Op.return_) ] actual = flattenBlocks(blocks) assert actual == expected def test_flatten_multiple_branch_converge(): - blockEnd = TealSimpleBlock([TealOp(Op.return_)]) - blockTrueTrue = TealSimpleBlock([TealOp(Op.byte, "\"true true\"")]) + blockEnd = TealSimpleBlock([TealOp(None, Op.return_)]) + blockTrueTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true true\"")]) blockTrueTrue.setNextBlock(blockEnd) - blockTrueFalse = TealSimpleBlock([TealOp(Op.byte, "\"true false\""), TealOp(Op.err)]) + blockTrueFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"true false\""), TealOp(None, Op.err)]) blockTrueBranch = TealConditionalBlock([]) blockTrueBranch.setTrueBlock(blockTrueTrue) blockTrueBranch.setFalseBlock(blockTrueFalse) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockTrueBranch) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) blockFalse.setNextBlock(blockEnd) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) block.addIncoming() - block.validate() + block.validateTree() blocks = [block, blockFalse, blockTrue, blockTrueBranch, blockTrueFalse, blockTrueTrue, blockEnd] expected = [ - TealOp(Op.int, 1), - TealOp(Op.bnz, "l2"), - TealOp(Op.byte, "\"false\""), - TealOp(Op.b, "l6"), - TealLabel("l2"), - TealOp(Op.byte, "\"true\""), - TealOp(Op.bnz, "l5"), - TealOp(Op.byte, "\"true false\""), - TealOp(Op.err), - TealLabel("l5"), - TealOp(Op.byte, "\"true true\""), - TealLabel("l6"), - TealOp(Op.return_) + TealOp(None, Op.int, 1), + TealOp(None, Op.bnz, "l2"), + TealOp(None, Op.byte, "\"false\""), + TealOp(None, Op.b, "l6"), + TealLabel(None, "l2"), + TealOp(None, Op.byte, "\"true\""), + TealOp(None, Op.bnz, "l5"), + TealOp(None, Op.byte, "\"true false\""), + TealOp(None, Op.err), + TealLabel(None, "l5"), + TealOp(None, Op.byte, "\"true true\""), + TealLabel(None, "l6"), + TealOp(None, Op.return_) ] actual = flattenBlocks(blocks) @@ -361,5 +361,30 @@ def test_compile_version(): actual_default = compileTeal(expr, Mode.Signature) assert actual_default == expected_version_2 + with pytest.raises(TealInputError): + compileTeal(expr, Mode.Signature, 2.0) + with pytest.raises(TealInputError): compileTeal(expr, Mode.Signature, 3) + +def test_slot_load_before_store(): + + program = AssetHolding.balance(Int(0), Int(0)).value() + with pytest.raises(TealInternalError): + compileTeal(program, Mode.Application, 2) + + program = AssetHolding.balance(Int(0), Int(0)).hasValue() + with pytest.raises(TealInternalError): + compileTeal(program, Mode.Application, 2) + + program = App.globalGetEx(Int(0), Bytes("key")).value() + with pytest.raises(TealInternalError): + compileTeal(program, Mode.Application, 2) + + program = App.globalGetEx(Int(0), Bytes("key")).hasValue() + with pytest.raises(TealInternalError): + compileTeal(program, Mode.Application, 2) + + program = ScratchVar().load() + with pytest.raises(TealInternalError): + compileTeal(program, Mode.Application, 2) diff --git a/pyteal/errors.py b/pyteal/errors.py index 36591cf65..10b25d519 100644 --- a/pyteal/errors.py +++ b/pyteal/errors.py @@ -1,8 +1,12 @@ +from typing import List, Optional, TYPE_CHECKING + +if TYPE_CHECKING: + from .ast import Expr class TealInternalError(Exception): - def __init__(self, message:str) -> None: - self.message = "Internal Error: {}".format(message) + def __init__(self, message: str) -> None: + self.message = message def __str__(self): return self.message @@ -11,20 +15,39 @@ def __str__(self): class TealTypeError(Exception): - def __init__(self, actual, expected): - self.message = "Type error: {} while expected {} ".format(actual, expected) + def __init__(self, actual, expected) -> None: + self.message = "{} while expected {} ".format(actual, expected) - def __str__(self): + def __str__(self) -> str: return self.message TealTypeError.__module__ = "pyteal" class TealInputError(Exception): - def __init__(self, msg): - self.message = "Input error: {}".format(msg) + def __init__(self, msg: str) -> None: + self.message = msg - def __str__(self): + def __str__(self) -> str: return self.message TealInputError.__module__ = "pyteal" + +class TealCompileError(Exception): + + def __init__(self, msg: str, sourceExpr: Optional['Expr']) -> None: + self.msg = msg + self.sourceExpr = sourceExpr + + def __str__(self) -> str: + if self.sourceExpr is None: + return self.msg + trace = self.sourceExpr.getDefinitionTrace() + return self.msg + "\nTraceback of origin expression (most recent call last):\n" + "".join(trace) + + def __eq__(self, other) -> bool: + if not isinstance(other, TealCompileError): + return False + return self.msg == other.msg and self.sourceExpr is other.sourceExpr + +TealCompileError.__module__ = "pyteal" diff --git a/pyteal/ir/tealblock.py b/pyteal/ir/tealblock.py index b01676953..b546217a7 100644 --- a/pyteal/ir/tealblock.py +++ b/pyteal/ir/tealblock.py @@ -1,9 +1,10 @@ from abc import ABC, abstractmethod -from typing import Optional, List, Tuple, Iterator, cast, TYPE_CHECKING +from typing import Optional, List, Tuple, Set, Iterator, cast, TYPE_CHECKING from .tealop import TealOp, Op +from ..errors import TealCompileError if TYPE_CHECKING: - from ..ast import Expr + from ..ast import Expr, ScratchSlot from .tealsimpleblock import TealSimpleBlock class TealBlock(ABC): @@ -30,7 +31,7 @@ def isTerminal(self) -> bool: return True return len(self.getOutgoing()) == 0 - def validate(self, parent: 'TealBlock' = None) -> None: + def validateTree(self, parent: 'TealBlock' = None) -> None: """Check that this block and its children have valid parent pointers. Args: @@ -44,7 +45,7 @@ def validate(self, parent: 'TealBlock' = None) -> None: assert count == 1 for block in self.getOutgoing(): - block.validate(self) + block.validateTree(self) def addIncoming(self, block: 'TealBlock' = None) -> None: """Calculate the parent blocks for this block and its children. @@ -58,6 +59,42 @@ def addIncoming(self, block: 'TealBlock' = None) -> None: for block in self.getOutgoing(): block.addIncoming(self) + def validateSlots(self, slotsInUse: Set['ScratchSlot'] = None, visited: Set[Tuple[int, ...]] = None) -> List[TealCompileError]: + import traceback + + if visited is None: + visited = set() + + if slotsInUse is None: + slotsInUse = set() + + currentSlotsInUse = set(slotsInUse) + errors = [] + + for op in self.ops: + if op.getOp() == Op.store: + for slot in op.getSlots(): + currentSlotsInUse.add(slot) + + if op.getOp() == Op.load: + for slot in op.getSlots(): + if slot not in currentSlotsInUse: + e = TealCompileError("Scratch slot load occurs before store", op.expr) + errors.append(e) + + if not self.isTerminal(): + sortedSlots = sorted(slot.id for slot in currentSlotsInUse) + for block in self.getOutgoing(): + visitedKey = (id(block), *sortedSlots) + if visitedKey in visited: + continue + for error in block.validateSlots(currentSlotsInUse, visited): + if error not in errors: + errors.append(error) + visited.add(visitedKey) + + return errors + @abstractmethod def __repr__(self) -> str: pass diff --git a/pyteal/ir/tealblock_test.py b/pyteal/ir/tealblock_test.py index 8dc341076..de71a83c4 100644 --- a/pyteal/ir/tealblock_test.py +++ b/pyteal/ir/tealblock_test.py @@ -1,7 +1,7 @@ from .. import * def test_from_op_no_args(): - op = TealOp(Op.int, 1) + op = TealOp(None, Op.int, 1) expected = TealSimpleBlock([op]) @@ -10,64 +10,66 @@ def test_from_op_no_args(): assert actual == expected def test_from_op_1_arg(): - op = TealOp(Op.pop) + op = TealOp(None, Op.pop) arg_1 = Bytes("message") expected = TealSimpleBlock([ - TealOp(Op.byte, "\"message\""), + TealOp(arg_1, Op.byte, "\"message\""), op ]) actual, _ = TealBlock.FromOp(op, arg_1) actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - actual.validate() + actual.validateTree() assert actual == expected def test_from_op_2_args(): - op = TealOp(Op.app_global_put) + op = TealOp(None, Op.app_global_put) arg_1 = Bytes("key") arg_2 = Int(5) expected = TealSimpleBlock([ - TealOp(Op.byte, "\"key\""), - TealOp(Op.int, 5), + TealOp(arg_1, Op.byte, "\"key\""), + TealOp(arg_2, Op.int, 5), op ]) actual, _ = TealBlock.FromOp(op, arg_1, arg_2) actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - actual.validate() + actual.validateTree() assert actual == expected def test_from_op_3_args(): - op = TealOp(Op.app_local_put) + op = TealOp(None, Op.app_local_put) arg_1 = Int(0) arg_2 = Bytes("key") - arg_3 = Int(1) + Int(2) + arg_3 = Int(1) + arg_4 = Int(2) + arg_3_plus_4 = arg_3 + arg_4 expected = TealSimpleBlock([ - TealOp(Op.int, 0), - TealOp(Op.byte, "\"key\""), - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.add), + TealOp(arg_1, Op.int, 0), + TealOp(arg_2, Op.byte, "\"key\""), + TealOp(arg_3, Op.int, 1), + TealOp(arg_4, Op.int, 2), + TealOp(arg_3_plus_4, Op.add), op ]) - actual, _ = TealBlock.FromOp(op, arg_1, arg_2, arg_3) + actual, _ = TealBlock.FromOp(op, arg_1, arg_2, arg_3_plus_4) actual.addIncoming() actual = TealBlock.NormalizeBlocks(actual) - actual.validate() + actual.validateTree() assert actual == expected def test_iterate_single(): block = TealSimpleBlock([ - TealOp(Op.int, 1) + TealOp(None, Op.int, 1) ]) blocks = list(TealBlock.Iterate(block)) @@ -75,14 +77,14 @@ def test_iterate_single(): assert blocks == [block] def test_iterate_sequence(): - block5 = TealSimpleBlock([TealOp(Op.int, 5)]) - block4 = TealSimpleBlock([TealOp(Op.int, 4)]) + block5 = TealSimpleBlock([TealOp(None, Op.int, 5)]) + block4 = TealSimpleBlock([TealOp(None, Op.int, 4)]) block4.setNextBlock(block5) - block3 = TealSimpleBlock([TealOp(Op.int, 3)]) + block3 = TealSimpleBlock([TealOp(None, Op.int, 3)]) block3.setNextBlock(block4) - block2 = TealSimpleBlock([TealOp(Op.int, 2)]) + block2 = TealSimpleBlock([TealOp(None, Op.int, 2)]) block2.setNextBlock(block3) - block1 = TealSimpleBlock([TealOp(Op.int, 1)]) + block1 = TealSimpleBlock([TealOp(None, Op.int, 1)]) block1.setNextBlock(block2) blocks = list(TealBlock.Iterate(block1)) @@ -90,9 +92,9 @@ def test_iterate_sequence(): assert blocks == [block1, block2, block3, block4, block5] def test_iterate_branch(): - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) @@ -101,15 +103,15 @@ def test_iterate_branch(): assert blocks == [block, blockTrue, blockFalse] def test_iterate_multiple_branch(): - blockTrueTrue = TealSimpleBlock([TealOp(Op.byte, "\"true true\"")]) - blockTrueFalse = TealSimpleBlock([TealOp(Op.byte, "\"true false\"")]) + blockTrueTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true true\"")]) + blockTrueFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"true false\"")]) blockTrueBranch = TealConditionalBlock([]) blockTrueBranch.setTrueBlock(blockTrueTrue) blockTrueBranch.setFalseBlock(blockTrueFalse) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockTrueBranch) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) @@ -118,12 +120,12 @@ def test_iterate_multiple_branch(): assert blocks == [block, blockTrue, blockFalse, blockTrueBranch, blockTrueTrue, blockTrueFalse] def test_iterate_branch_converge(): - blockEnd = TealSimpleBlock([TealOp(Op.return_)]) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockEnd = TealSimpleBlock([TealOp(None, Op.return_)]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockEnd) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) blockFalse.setNextBlock(blockEnd) - block = TealConditionalBlock([TealOp(Op.int, 1)]) + block = TealConditionalBlock([TealOp(None, Op.int, 1)]) block.setTrueBlock(blockTrue) block.setFalseBlock(blockFalse) @@ -133,83 +135,83 @@ def test_iterate_branch_converge(): def test_normalize_single(): original = TealSimpleBlock([ - TealOp(Op.int, 1) + TealOp(None, Op.int, 1) ]) expected = TealSimpleBlock([ - TealOp(Op.int, 1) + TealOp(None, Op.int, 1) ]) original.addIncoming() actual = TealBlock.NormalizeBlocks(original) - actual.validate() + actual.validateTree() assert actual == expected def test_normalize_sequence(): block6 = TealSimpleBlock([]) - block5 = TealSimpleBlock([TealOp(Op.int, 5)]) + block5 = TealSimpleBlock([TealOp(None, Op.int, 5)]) block5.setNextBlock(block6) - block4 = TealSimpleBlock([TealOp(Op.int, 4)]) + block4 = TealSimpleBlock([TealOp(None, Op.int, 4)]) block4.setNextBlock(block5) - block3 = TealSimpleBlock([TealOp(Op.int, 3)]) + block3 = TealSimpleBlock([TealOp(None, Op.int, 3)]) block3.setNextBlock(block4) - block2 = TealSimpleBlock([TealOp(Op.int, 2)]) + block2 = TealSimpleBlock([TealOp(None, Op.int, 2)]) block2.setNextBlock(block3) - block1 = TealSimpleBlock([TealOp(Op.int, 1)]) + block1 = TealSimpleBlock([TealOp(None, Op.int, 1)]) block1.setNextBlock(block2) expected = TealSimpleBlock([ - TealOp(Op.int, 1), - TealOp(Op.int, 2), - TealOp(Op.int, 3), - TealOp(Op.int, 4), - TealOp(Op.int, 5), + TealOp(None, Op.int, 1), + TealOp(None, Op.int, 2), + TealOp(None, Op.int, 3), + TealOp(None, Op.int, 4), + TealOp(None, Op.int, 5), ]) block1.addIncoming() actual = TealBlock.NormalizeBlocks(block1) - actual.validate() + actual.validateTree() assert actual == expected def test_normalize_branch(): - blockTrueNext = TealSimpleBlock([TealOp(Op.int, 4)]) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockTrueNext = TealSimpleBlock([TealOp(None, Op.int, 4)]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockTrueNext) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) - blockBranch = TealConditionalBlock([TealOp(Op.int, 1)]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) + blockBranch = TealConditionalBlock([TealOp(None, Op.int, 1)]) blockBranch.setTrueBlock(blockTrue) blockBranch.setFalseBlock(blockFalse) original = TealSimpleBlock([]) original.setNextBlock(blockBranch) expectedTrue = TealSimpleBlock([ - TealOp(Op.byte, "\"true\""), - TealOp(Op.int, 4) + TealOp(None, Op.byte, "\"true\""), + TealOp(None, Op.int, 4) ]) expectedFalse = TealSimpleBlock([ - TealOp(Op.byte, "\"false\"") + TealOp(None, Op.byte, "\"false\"") ]) - expected = TealConditionalBlock([TealOp(Op.int, 1)]) + expected = TealConditionalBlock([TealOp(None, Op.int, 1)]) expected.setTrueBlock(expectedTrue) expected.setFalseBlock(expectedFalse) original.addIncoming() actual = TealBlock.NormalizeBlocks(original) - actual.validate() + actual.validateTree() assert actual == expected def test_normalize_branch_converge(): blockEnd = TealSimpleBlock([]) - blockTrueNext = TealSimpleBlock([TealOp(Op.int, 4)]) + blockTrueNext = TealSimpleBlock([TealOp(None, Op.int, 4)]) blockTrueNext.setNextBlock(blockEnd) - blockTrue = TealSimpleBlock([TealOp(Op.byte, "\"true\"")]) + blockTrue = TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]) blockTrue.setNextBlock(blockTrueNext) - blockFalse = TealSimpleBlock([TealOp(Op.byte, "\"false\"")]) + blockFalse = TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")]) blockFalse.setNextBlock(blockEnd) - blockBranch = TealConditionalBlock([TealOp(Op.int, 1)]) + blockBranch = TealConditionalBlock([TealOp(None, Op.int, 1)]) blockBranch.setTrueBlock(blockTrue) blockBranch.setFalseBlock(blockFalse) original = TealSimpleBlock([]) @@ -217,20 +219,20 @@ def test_normalize_branch_converge(): expectedEnd = TealSimpleBlock([]) expectedTrue = TealSimpleBlock([ - TealOp(Op.byte, "\"true\""), - TealOp(Op.int, 4) + TealOp(None, Op.byte, "\"true\""), + TealOp(None, Op.int, 4) ]) expectedTrue.setNextBlock(expectedEnd) expectedFalse = TealSimpleBlock([ - TealOp(Op.byte, "\"false\"") + TealOp(None, Op.byte, "\"false\"") ]) expectedFalse.setNextBlock(expectedEnd) - expected = TealConditionalBlock([TealOp(Op.int, 1)]) + expected = TealConditionalBlock([TealOp(None, Op.int, 1)]) expected.setTrueBlock(expectedTrue) expected.setFalseBlock(expectedFalse) original.addIncoming() actual = TealBlock.NormalizeBlocks(original) - actual.validate() + actual.validateTree() assert actual == expected diff --git a/pyteal/ir/tealcomponent.py b/pyteal/ir/tealcomponent.py index 30ec691b5..6d5dd951d 100644 --- a/pyteal/ir/tealcomponent.py +++ b/pyteal/ir/tealcomponent.py @@ -1,11 +1,15 @@ from abc import ABC, abstractmethod -from typing import List, TYPE_CHECKING +from typing import List, Optional, TYPE_CHECKING +from contextlib import AbstractContextManager if TYPE_CHECKING: - from ..ast import ScratchSlot + from ..ast import Expr, ScratchSlot class TealComponent(ABC): + def __init__(self, expr: Optional['Expr']): + self.expr = expr + def getSlots(self) -> List['ScratchSlot']: return [] @@ -28,4 +32,21 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: pass + class Context: + + checkExpr = True + + class EqualityContext(AbstractContextManager): + def __enter__(self): + TealComponent.Context.checkExpr = False + return self + + def __exit__(self, *args): + TealComponent.Context.checkExpr = True + return None + + @classmethod + def ignoreExprEquality(cls): + return cls.EqualityContext() + TealComponent.__module__ = "pyteal" diff --git a/pyteal/ir/tealcomponent_test.py b/pyteal/ir/tealcomponent_test.py new file mode 100644 index 000000000..5b5e6f1f9 --- /dev/null +++ b/pyteal/ir/tealcomponent_test.py @@ -0,0 +1,21 @@ +import pytest + +from .. import * + +def test_EqualityContext(): + expr1 = Int(1) + expr2 = Int(1) + + op1 = TealOp(expr1, Op.int, 1) + op2 = TealOp(expr2, Op.int, 1) + + assert op1 == op1 + assert op2 == op2 + assert op1 != op2 + assert op2 != op1 + + with TealComponent.Context.ignoreExprEquality(): + assert op1 == op1 + assert op2 == op2 + assert op1 == op2 + assert op2 == op1 diff --git a/pyteal/ir/tealconditionalblock_test.py b/pyteal/ir/tealconditionalblock_test.py index 96f3a6e4e..381a55045 100644 --- a/pyteal/ir/tealconditionalblock_test.py +++ b/pyteal/ir/tealconditionalblock_test.py @@ -6,35 +6,35 @@ def test_constructor(): assert block1.trueBlock is None assert block1.falseBlock is None - block2 = TealConditionalBlock([TealOp(Op.int, 1)]) - assert block2.ops == [TealOp(Op.int, 1)] + block2 = TealConditionalBlock([TealOp(None, Op.int, 1)]) + assert block2.ops == [TealOp(None, Op.int, 1)] assert block2.trueBlock is None assert block2.falseBlock is None def test_true_block(): block = TealConditionalBlock([]) - block.setTrueBlock(TealSimpleBlock([TealOp(Op.substring3)])) - assert block.trueBlock == TealSimpleBlock([TealOp(Op.substring3)]) - assert block.getOutgoing() == [TealSimpleBlock([TealOp(Op.substring3)])] + block.setTrueBlock(TealSimpleBlock([TealOp(None, Op.substring3)])) + assert block.trueBlock == TealSimpleBlock([TealOp(None, Op.substring3)]) + assert block.getOutgoing() == [TealSimpleBlock([TealOp(None, Op.substring3)])] def test_false_block(): block = TealConditionalBlock([]) - block.setFalseBlock(TealSimpleBlock([TealOp(Op.substring3)])) - assert block.falseBlock == TealSimpleBlock([TealOp(Op.substring3)]) + block.setFalseBlock(TealSimpleBlock([TealOp(None, Op.substring3)])) + assert block.falseBlock == TealSimpleBlock([TealOp(None, Op.substring3)]) def test_outgoing(): emptyBlock = TealConditionalBlock([]) assert emptyBlock.getOutgoing() == [] trueBlock = TealConditionalBlock([]) - trueBlock.setTrueBlock(TealSimpleBlock([TealOp(Op.byte, "\"true\"")])) - assert trueBlock.getOutgoing() == [TealSimpleBlock([TealOp(Op.byte, "\"true\"")])] + trueBlock.setTrueBlock(TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")])) + assert trueBlock.getOutgoing() == [TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")])] falseBlock = TealConditionalBlock([]) - falseBlock.setFalseBlock(TealSimpleBlock([TealOp(Op.byte, "\"false\"")])) - assert falseBlock.getOutgoing() == [TealSimpleBlock([TealOp(Op.byte, "\"false\"")])] + falseBlock.setFalseBlock(TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")])) + assert falseBlock.getOutgoing() == [TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")])] bothBlock = TealConditionalBlock([]) - bothBlock.setTrueBlock(TealSimpleBlock([TealOp(Op.byte, "\"true\"")])) - bothBlock.setFalseBlock(TealSimpleBlock([TealOp(Op.byte, "\"false\"")])) - assert bothBlock.getOutgoing() == [TealSimpleBlock([TealOp(Op.byte, "\"true\"")]), TealSimpleBlock([TealOp(Op.byte, "\"false\"")])] + bothBlock.setTrueBlock(TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")])) + bothBlock.setFalseBlock(TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")])) + assert bothBlock.getOutgoing() == [TealSimpleBlock([TealOp(None, Op.byte, "\"true\"")]), TealSimpleBlock([TealOp(None, Op.byte, "\"false\"")])] diff --git a/pyteal/ir/teallabel.py b/pyteal/ir/teallabel.py index 534254b3d..815fff35f 100644 --- a/pyteal/ir/teallabel.py +++ b/pyteal/ir/teallabel.py @@ -1,15 +1,19 @@ -from .tealcomponent import TealComponent +from typing import Optional, TYPE_CHECKING +from .tealcomponent import TealComponent +if TYPE_CHECKING: + from ..ast import Expr class TealLabel(TealComponent): - def __init__(self, label: str) -> None: + def __init__(self, expr: Optional['Expr'], label: str) -> None: + super().__init__(expr) self.label = label def assemble(self) -> str: return self.label + ":" def __repr__(self) -> str: - return "TealLabel({})".format(repr(self.label)) + return "TealLabel({}, {})".format(self.expr, repr(self.label)) def __hash__(self) -> int: return self.label.__hash__() @@ -17,6 +21,8 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: if not isinstance(other, TealLabel): return False + if TealComponent.Context.checkExpr and self.expr is not other.expr: + return False return self.label == other.label TealLabel.__module__ = "pyteal" diff --git a/pyteal/ir/tealop.py b/pyteal/ir/tealop.py index 27c062d27..6b6146869 100644 --- a/pyteal/ir/tealop.py +++ b/pyteal/ir/tealop.py @@ -1,14 +1,15 @@ -from typing import cast, Union, List, TYPE_CHECKING +from typing import cast, Union, List, Optional, TYPE_CHECKING from .tealcomponent import TealComponent from .ops import Op from ..errors import TealInternalError if TYPE_CHECKING: - from ..ast import ScratchSlot + from ..ast import Expr, ScratchSlot class TealOp(TealComponent): - def __init__(self, op: Op, *args: Union[int, str, 'ScratchSlot']) -> None: + def __init__(self, expr: Optional['Expr'], op: Op, *args: Union[int, str, 'ScratchSlot']) -> None: + super().__init__(expr) self.op = op self.args = list(args) @@ -43,7 +44,7 @@ def __repr__(self) -> str: for a in self.args: args.append(repr(a)) - return "TealOp({})".format(", ".join(args)) + return "TealOp({}, {})".format(self.expr, ", ".join(args)) def __hash__(self) -> int: return (self.op, *self.args).__hash__() @@ -51,6 +52,8 @@ def __hash__(self) -> int: def __eq__(self, other: object) -> bool: if not isinstance(other, TealOp): return False + if TealComponent.Context.checkExpr and self.expr is not other.expr: + return False return self.op == other.op and self.args == other.args TealOp.__module__ = "pyteal" diff --git a/pyteal/ir/tealsimpleblock_test.py b/pyteal/ir/tealsimpleblock_test.py index 9648e708d..69082fc82 100644 --- a/pyteal/ir/tealsimpleblock_test.py +++ b/pyteal/ir/tealsimpleblock_test.py @@ -5,19 +5,19 @@ def test_constructor(): assert block1.ops == [] assert block1.nextBlock is None - block2 = TealSimpleBlock([TealOp(Op.int, 1)]) - assert block2.ops == [TealOp(Op.int, 1)] + block2 = TealSimpleBlock([TealOp(None, Op.int, 1)]) + assert block2.ops == [TealOp(None, Op.int, 1)] assert block2.nextBlock is None def test_next_block(): block = TealSimpleBlock([]) - block.setNextBlock(TealSimpleBlock([TealOp(Op.substring3)])) - assert block.nextBlock == TealSimpleBlock([TealOp(Op.substring3)]) + block.setNextBlock(TealSimpleBlock([TealOp(None, Op.substring3)])) + assert block.nextBlock == TealSimpleBlock([TealOp(None, Op.substring3)]) def test_outgoing(): emptyBlock = TealSimpleBlock([]) assert emptyBlock.getOutgoing() == [] block = TealSimpleBlock([]) - block.setNextBlock(TealSimpleBlock([TealOp(Op.byte, "\"nextBlock\"")])) - assert block.getOutgoing() == [TealSimpleBlock([TealOp(Op.byte, "\"nextBlock\"")])] + block.setNextBlock(TealSimpleBlock([TealOp(None, Op.byte, "\"nextBlock\"")])) + assert block.getOutgoing() == [TealSimpleBlock([TealOp(None, Op.byte, "\"nextBlock\"")])] diff --git a/pyteal/util.py b/pyteal/util.py index f8348e6c7..99dd62a73 100644 --- a/pyteal/util.py +++ b/pyteal/util.py @@ -1,33 +1,3 @@ -#!/usr/bin/env python3 -""" -Helper functions and classes -""" - -import subprocess - -label_count = 0 - -def reset_label_count(): - global label_count - label_count = 0 - -def new_label(): - global label_count - new_l = "l{}".format(label_count) - label_count += 1 - return new_l - -def execute(args): - """ Execute in bash, return stdout and stderr in string - - Arguments: - args: command and arguments to run, e.g. ['ls', '-l'] - """ - process = subprocess.Popen(args, stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout, stderr = process.communicate() - - return (stdout.decode("utf-8"), stderr.decode("utf-8")) def escapeStr(s: str) -> str: """Escape a UTF-8 string for use in TEAL assembly. diff --git a/tests/compile_test.py b/tests/compile_test.py index 7602a053d..278dcb3dd 100644 --- a/tests/compile_test.py +++ b/tests/compile_test.py @@ -1,6 +1,5 @@ import os from pyteal import * -from pyteal.util import reset_label_count def test_basic_bank(): from examples.signature.basic import bank_for_account @@ -40,7 +39,6 @@ def test_split(): target_path = os.path.join(os.path.dirname(__file__), "../examples/signature/split.teal") with open(target_path, "r") as target_file: target = "".join(target_file.readlines()).strip() - reset_label_count() assert compileTeal(program, Mode.Signature) == target def test_dutch_auction(): @@ -51,7 +49,6 @@ def test_dutch_auction(): target_path = os.path.join(os.path.dirname(__file__), "../examples/signature/dutch_auction.teal") with open(target_path, "r") as target_file: target = "".join(target_file.readlines()).strip() - reset_label_count() assert compileTeal(program, Mode.Signature) == target def test_recurring_swap(): @@ -62,7 +59,6 @@ def test_recurring_swap(): target_path = os.path.join(os.path.dirname(__file__), "../examples/signature/recurring_swap.teal") with open(target_path, "r") as target_file: target = "".join(target_file.readlines()).strip() - reset_label_count() assert compileTeal(program, Mode.Signature) == target def test_asset():