From 295332b07ef30428b7bafe43e39a9bdcd4a30acc Mon Sep 17 00:00:00 2001 From: Ben Guidarelli Date: Fri, 28 Jan 2022 07:41:21 -0500 Subject: [PATCH 1/8] adding new teal6 ops, no pyteal expressions defined for them yet --- pyteal/ast/txn.py | 12 ++++++++++++ pyteal/ast/txn_test.py | 1 + pyteal/ir/ops.py | 3 +++ 3 files changed, 16 insertions(+) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index ed0638733..c1a6f9a14 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -108,6 +108,7 @@ class TxnField(Enum): num_logs = (59, "NumLogs", TealType.uint64, False, 5) created_asset_id = (60, "CreatedAssetID", TealType.uint64, False, 5) created_application_id = (61, "CreatedApplicationID", TealType.uint64, False, 5) + last_log = (62, "LastLog", TealType.bytes, False, 6) def __init__( self, id: int, name: str, type: TealType, is_array: bool, min_version: int @@ -695,6 +696,17 @@ def created_application_id(self) -> TxnExpr: """ return self.makeTxnExpr(TxnField.created_application_id) + def last_log(self) -> TxnExpr: + """Get the last logged message from a transaction. + + Only application calls may log a message. Returns an empty string if no messages were logged. + + Only set when :any:`type_enum()` is :any:`TxnType.ApplicationCall`. + + Requires TEAL version 6 or higher. + """ + return self.makeTxnExpr(TxnField.last_log) + @property def application_args(self) -> TxnArray: """Application call arguments array. diff --git a/pyteal/ast/txn_test.py b/pyteal/ast/txn_test.py index 8f1dff254..efe81ce08 100644 --- a/pyteal/ast/txn_test.py +++ b/pyteal/ast/txn_test.py @@ -59,6 +59,7 @@ TxnField.nonparticipation: lambda txn: txn.nonparticipation(), TxnField.created_asset_id: lambda txn: txn.created_asset_id(), TxnField.created_application_id: lambda txn: txn.created_application_id(), + TxnField.last_log: lambda txn: txn.last_log(), } arrayFieldToProperty: Dict[TxnField, Callable[[TxnObject], TxnArray]] = { diff --git a/pyteal/ir/ops.py b/pyteal/ir/ops.py index aec39652d..7d91dab42 100644 --- a/pyteal/ir/ops.py +++ b/pyteal/ir/ops.py @@ -171,9 +171,12 @@ def min_version(self) -> int: gtxnsas = OpType("gtxnsas", Mode.Signature | Mode.Application, 5) args = OpType("args", Mode.Signature, 5) bsqrt = OpType("bsqrt", Mode.Signature | Mode.Application, 6) + divw = OpType("divw", Mode.Signature | Mode.Application, 6) itxn_next = OpType("itxn_next", Mode.Application, 6) + itxnas = OpType("itxnas", Mode.Application, 6) gitxn = OpType("gitxn", Mode.Application, 6) gitxna = OpType("gitxna", Mode.Application, 6) + gitxnas = OpType("gitxnas", Mode.Application, 6) gloadss = OpType("gloadss", Mode.Application, 6) acct_params_get = OpType("acct_params_get", Mode.Application, 6) # fmt: on From 246b63363411e7e156c0714c3f24f8b67d15355f Mon Sep 17 00:00:00 2001 From: michaeldiamant Date: Fri, 11 Feb 2022 22:48:36 -0500 Subject: [PATCH 2/8] Add opcode support for divw --- pyteal/__init__.pyi | 1 + pyteal/ast/__init__.py | 3 ++- pyteal/ast/ternaryexpr.py | 22 ++++++++++++++++++++++ pyteal/ast/ternaryexpr_test.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/pyteal/__init__.pyi b/pyteal/__init__.pyi index 420f674aa..13de4d9a8 100644 --- a/pyteal/__init__.pyi +++ b/pyteal/__init__.pyi @@ -78,6 +78,7 @@ __all__ = [ "Div", "Mod", "Exp", + "Divw", "BitwiseAnd", "BitwiseOr", "BitwiseXor", diff --git a/pyteal/ast/__init__.py b/pyteal/ast/__init__.py index 03cb4daeb..7582be29b 100644 --- a/pyteal/ast/__init__.py +++ b/pyteal/ast/__init__.py @@ -92,7 +92,7 @@ ) # ternary ops -from .ternaryexpr import Ed25519Verify, SetBit, SetByte +from .ternaryexpr import Divw, Ed25519Verify, SetBit, SetByte from .substring import Substring, Extract, Suffix # more ops @@ -186,6 +186,7 @@ "Div", "Mod", "Exp", + "Divw", "BitwiseAnd", "BitwiseOr", "BitwiseXor", diff --git a/pyteal/ast/ternaryexpr.py b/pyteal/ast/ternaryexpr.py index 248378587..94776e800 100644 --- a/pyteal/ast/ternaryexpr.py +++ b/pyteal/ast/ternaryexpr.py @@ -128,3 +128,25 @@ def SetByte(value: Expr, index: Expr, newByteValue: Expr) -> TernaryExpr: index, newByteValue, ) + + +def Divw(hi: Expr, lo: Expr, y: Expr) -> TernaryExpr: + """ + Performs wide division by interpreting `hi` and `lo` as a uint128 value. + + Requires TEAL version 6 or higher. + + Args: + hi: Quotient's high 64 bits. Must evaluate to uint64. + lo: Quotient's low 64 bits. Must evaluate to uint64. + y: Divisor. Must evaluate to uint64. + + """ + return TernaryExpr( + Op.divw, + (TealType.uint64, TealType.uint64, TealType.uint64), + TealType.uint64, + hi, + lo, + y, + ) diff --git a/pyteal/ast/ternaryexpr_test.py b/pyteal/ast/ternaryexpr_test.py index 22b4817bd..48b35585e 100644 --- a/pyteal/ast/ternaryexpr_test.py +++ b/pyteal/ast/ternaryexpr_test.py @@ -9,6 +9,7 @@ teal3Options = CompileOptions(version=3) teal4Options = CompileOptions(version=4) teal5Options = CompileOptions(version=5) +teal6Options = CompileOptions(version=6) def test_ed25519verify(): @@ -138,3 +139,35 @@ def test_set_byte_invalid(): with pytest.raises(TealTypeError): SetByte(Bytes("base16", "0xFF"), Int(0), Bytes("one")) + + +def test_divw(): + args = [Int(0), Int(90), Int(30)] + expr = Divw(args[0], args[1], args[2]) + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock( + [ + TealOp(args[0], Op.int, args[0].value), + TealOp(args[1], Op.int, args[1].value), + TealOp(args[2], Op.int, args[2].value), + TealOp(expr, Op.divw), + ] + ) + + actual, _ = expr.__teal__(teal6Options) + actual.addIncoming() + actual = TealBlock.NormalizeBlocks(actual) + + assert actual == expected + + +def test_divw_invalid(): + with pytest.raises(TealTypeError): + Divw(Bytes("10"), Int(0), Int(1)) + + with pytest.raises(TealTypeError): + Divw(Int(10), Bytes("0"), Int(1)) + + with pytest.raises(TealTypeError): + Divw(Int(10), Int(0), Bytes("1")) From 74c34b3e663a763cc4fe51363d83d887b385e878 Mon Sep 17 00:00:00 2001 From: Michael Diamant Date: Mon, 14 Feb 2022 10:06:49 -0500 Subject: [PATCH 3/8] Add opcode support for divw (#192) --- pyteal/__init__.pyi | 1 + pyteal/ast/__init__.py | 3 ++- pyteal/ast/ternaryexpr.py | 22 ++++++++++++++++++++++ pyteal/ast/ternaryexpr_test.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 1 deletion(-) diff --git a/pyteal/__init__.pyi b/pyteal/__init__.pyi index 420f674aa..13de4d9a8 100644 --- a/pyteal/__init__.pyi +++ b/pyteal/__init__.pyi @@ -78,6 +78,7 @@ __all__ = [ "Div", "Mod", "Exp", + "Divw", "BitwiseAnd", "BitwiseOr", "BitwiseXor", diff --git a/pyteal/ast/__init__.py b/pyteal/ast/__init__.py index 03cb4daeb..7582be29b 100644 --- a/pyteal/ast/__init__.py +++ b/pyteal/ast/__init__.py @@ -92,7 +92,7 @@ ) # ternary ops -from .ternaryexpr import Ed25519Verify, SetBit, SetByte +from .ternaryexpr import Divw, Ed25519Verify, SetBit, SetByte from .substring import Substring, Extract, Suffix # more ops @@ -186,6 +186,7 @@ "Div", "Mod", "Exp", + "Divw", "BitwiseAnd", "BitwiseOr", "BitwiseXor", diff --git a/pyteal/ast/ternaryexpr.py b/pyteal/ast/ternaryexpr.py index 248378587..94776e800 100644 --- a/pyteal/ast/ternaryexpr.py +++ b/pyteal/ast/ternaryexpr.py @@ -128,3 +128,25 @@ def SetByte(value: Expr, index: Expr, newByteValue: Expr) -> TernaryExpr: index, newByteValue, ) + + +def Divw(hi: Expr, lo: Expr, y: Expr) -> TernaryExpr: + """ + Performs wide division by interpreting `hi` and `lo` as a uint128 value. + + Requires TEAL version 6 or higher. + + Args: + hi: Quotient's high 64 bits. Must evaluate to uint64. + lo: Quotient's low 64 bits. Must evaluate to uint64. + y: Divisor. Must evaluate to uint64. + + """ + return TernaryExpr( + Op.divw, + (TealType.uint64, TealType.uint64, TealType.uint64), + TealType.uint64, + hi, + lo, + y, + ) diff --git a/pyteal/ast/ternaryexpr_test.py b/pyteal/ast/ternaryexpr_test.py index 22b4817bd..48b35585e 100644 --- a/pyteal/ast/ternaryexpr_test.py +++ b/pyteal/ast/ternaryexpr_test.py @@ -9,6 +9,7 @@ teal3Options = CompileOptions(version=3) teal4Options = CompileOptions(version=4) teal5Options = CompileOptions(version=5) +teal6Options = CompileOptions(version=6) def test_ed25519verify(): @@ -138,3 +139,35 @@ def test_set_byte_invalid(): with pytest.raises(TealTypeError): SetByte(Bytes("base16", "0xFF"), Int(0), Bytes("one")) + + +def test_divw(): + args = [Int(0), Int(90), Int(30)] + expr = Divw(args[0], args[1], args[2]) + assert expr.type_of() == TealType.uint64 + + expected = TealSimpleBlock( + [ + TealOp(args[0], Op.int, args[0].value), + TealOp(args[1], Op.int, args[1].value), + TealOp(args[2], Op.int, args[2].value), + TealOp(expr, Op.divw), + ] + ) + + actual, _ = expr.__teal__(teal6Options) + actual.addIncoming() + actual = TealBlock.NormalizeBlocks(actual) + + assert actual == expected + + +def test_divw_invalid(): + with pytest.raises(TealTypeError): + Divw(Bytes("10"), Int(0), Int(1)) + + with pytest.raises(TealTypeError): + Divw(Int(10), Bytes("0"), Int(1)) + + with pytest.raises(TealTypeError): + Divw(Int(10), Int(0), Bytes("1")) From 2a682332bcec3677e7a3457a82c63b513ffe57c8 Mon Sep 17 00:00:00 2001 From: Michael Diamant Date: Thu, 17 Feb 2022 10:51:29 -0500 Subject: [PATCH 4/8] Add opcode support for itxnas and gitxnas (#193) * Add opcode support for itxnas and gitxnas * Update stale reference to inner transaction limit * Fix allowed types for GitxnaExpr txnIndex * Remove obsolete logic for handling GitxnaExpr.teal construction * Remove unnecessary cast and fix gitxna runtime type checking * Move type validation to constructors for gtxn and gitxn variants * Add missed tests from prior commit * Fix duplicate test case * Move index validation from subclasses to TxnaExpr * Inline validation functions per PR feedback * Remove unused imports * Refactor to isinstance tupled check * Remove TEAL v1 min version test per PR feedback * Fix constructor type checking for GtxnExpr * Refactor to remove duplicate type check function --- pyteal/ast/gitxn.py | 58 +++++++++++++++++++------------ pyteal/ast/gitxn_test.py | 75 +++++++++++++++++++++++++++++++++------- pyteal/ast/gtxn.py | 11 ++++++ pyteal/ast/gtxn_test.py | 60 ++++++++++++++++++++++++++------ pyteal/ast/itxn.py | 5 +-- pyteal/ast/txn.py | 11 ++++++ pyteal/ast/txn_test.py | 4 +-- 7 files changed, 175 insertions(+), 49 deletions(-) diff --git a/pyteal/ast/gitxn.py b/pyteal/ast/gitxn.py index 168899945..4108d86ff 100644 --- a/pyteal/ast/gitxn.py +++ b/pyteal/ast/gitxn.py @@ -1,10 +1,12 @@ -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, cast, Union from pyteal.config import MAX_GROUP_SIZE from ..errors import TealInputError, verifyFieldVersion, verifyTealVersion from ..ir import TealOp, Op, TealBlock +from .expr import Expr from .txn import TxnExpr, TxnField, TxnObject, TxnaExpr +from ..types import require_type, TealType if TYPE_CHECKING: from ..compiler import CompileOptions @@ -15,6 +17,15 @@ class GitxnExpr(TxnExpr): def __init__(self, txnIndex: int, field: TxnField) -> None: super().__init__(Op.gitxn, "Gitxn", field) + + # Currently we do not have gitxns. Only gitxn with immediate transaction index supported. + if type(txnIndex) is not int: + raise TealInputError( + "Invalid gitxn syntax with immediate transaction field {} and transaction index {}".format( + field, txnIndex + ) + ) + self.txnIndex = txnIndex def __str__(self): @@ -23,14 +34,6 @@ def __str__(self): def __teal__(self, options: "CompileOptions"): verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version) - # currently we do not have gitxns, only gitxn with immediate transaction index supported - if type(self.txnIndex) is not int: - raise TealInputError( - "Invalid gitxn syntax with immediate transaction field {} and transaction index {}".format( - self.field, self.txnIndex - ) - ) - verifyTealVersion( Op.gitxn.min_version, options.version, @@ -46,8 +49,14 @@ def __teal__(self, options: "CompileOptions"): class GitxnaExpr(TxnaExpr): """An expression that accesses an inner transaction array field from an inner transaction in the last inner group.""" - def __init__(self, txnIndex: int, field: TxnField, index: int) -> None: - super().__init__(Op.gitxna, None, "Gitxna", field, index) + def __init__(self, txnIndex: int, field: TxnField, index: Union[int, Expr]) -> None: + super().__init__(Op.gitxna, Op.gitxnas, "Gitxna", field, index) + + if type(txnIndex) is not int: + raise TealInputError( + f"Invalid txnIndex type: Expected int, but received {txnIndex}." + ) + self.txnIndex = txnIndex def __str__(self): @@ -57,18 +66,23 @@ def __str__(self): def __teal__(self, options: "CompileOptions"): verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version) - if type(self.txnIndex) is not int or type(self.index) is not int: - raise TealInputError( - "Invalid gitxna syntax with immediate transaction index {}, transaction field {}, array index {}".format( - self.txnIndex, self.field, self.index - ) - ) + + if type(self.index) is int: + opToUse = Op.gitxna + else: + opToUse = Op.gitxnas verifyTealVersion( - Op.gitxna.min_version, options.version, "TEAL version too low to use gitxna" + opToUse.min_version, + options.version, + "TEAL version too low to use op {}".format(opToUse), ) - op = TealOp(self, Op.gitxna, self.txnIndex, self.field.arg_name, self.index) - return TealBlock.FromOp(options, op) + + if type(self.index) is int: + op = TealOp(self, opToUse, self.txnIndex, self.field.arg_name, self.index) + return TealBlock.FromOp(options, op) + op = TealOp(self, opToUse, self.txnIndex, self.field.arg_name) + return TealBlock.FromOp(options, op, cast(Expr, self.index)) GitxnaExpr.__module__ = "pyteal" @@ -85,14 +99,14 @@ def __getitem__(self, txnIndex: int) -> TxnObject: if txnIndex < 0 or txnIndex >= MAX_GROUP_SIZE: raise TealInputError( - "Invalid Gtxn index {}, shoud be in [0, {})".format( + "Invalid Gtxn index {}, should be in [0, {})".format( txnIndex, MAX_GROUP_SIZE ) ) return TxnObject( lambda field: GitxnExpr(txnIndex, field), - lambda field, index: GitxnaExpr(txnIndex, field, cast(int, index)), + lambda field, index: GitxnaExpr(txnIndex, field, index), ) diff --git a/pyteal/ast/gitxn_test.py b/pyteal/ast/gitxn_test.py index 677cfc09d..ab950067b 100644 --- a/pyteal/ast/gitxn_test.py +++ b/pyteal/ast/gitxn_test.py @@ -7,24 +7,75 @@ def test_gitxn_invalid(): - with pytest.raises(TealInputError): - GitxnExpr(0, TxnField.sender).__teal__(teal5Options) + for ctor, e in [ + ( + lambda: Gitxn[MAX_GROUP_SIZE], + TealInputError, + ), + ( + lambda: Gitxn[-1], + TealInputError, + ), + ]: + with pytest.raises(e): + ctor() - with pytest.raises(TealInputError): - Gitxn[MAX_GROUP_SIZE].sender() - with pytest.raises(TealInputError): - Gitxn[-1].asset_sender() +def test_gitxn_valid(): + for i in range(MAX_GROUP_SIZE): + Gitxn[i].sender() - with pytest.raises(TealInputError): - Gitxn[Bytes("first")].sender() +def test_gitxn_expr_invalid(): + for f, e in [ + ( + lambda: GitxnExpr(Int(1), TxnField.sender), + TealInputError, + ), + ( + lambda: GitxnExpr(1, TxnField.sender).__teal__(teal5Options), + TealInputError, + ), + ]: + with pytest.raises(e): + f() -def test_gitxn_valid(): - GitxnaExpr(0, TxnField.application_args, 1).__teal__(teal6Options) - for i in range(MAX_GROUP_SIZE): - Gitxn[i].sender() +def test_gitxn_expr_valid(): + GitxnExpr(1, TxnField.sender).__teal__(teal6Options) + + +def test_gitxna_expr_invalid(): + for f, e in [ + ( + lambda: GitxnaExpr("Invalid_type", TxnField.application_args, 1), + TealInputError, + ), + ( + lambda: GitxnaExpr(1, TxnField.application_args, "Invalid_type"), + TealInputError, + ), + ( + lambda: GitxnaExpr(0, TxnField.application_args, Assert(Int(1))), + TealTypeError, + ), + ( + lambda: GitxnaExpr(0, TxnField.application_args, 0).__teal__(teal5Options), + TealInputError, + ), + ]: + with pytest.raises(e): + f() + + +def test_gitxna_valid(): + [ + e.__teal__(teal6Options) + for e in [ + GitxnaExpr(0, TxnField.application_args, 1), + GitxnaExpr(0, TxnField.application_args, Int(1)), + ] + ] # txn_test.py performs additional testing diff --git a/pyteal/ast/gtxn.py b/pyteal/ast/gtxn.py index c4b0c14f7..b12f8f9d5 100644 --- a/pyteal/ast/gtxn.py +++ b/pyteal/ast/gtxn.py @@ -11,11 +11,21 @@ from ..compiler import CompileOptions +def validate_txn_index_or_throw(txnIndex: Union[int, Expr]): + if not isinstance(txnIndex, (int, Expr)): + raise TealInputError( + f"Invalid txnIndex type: Expected int or Expr, but received {txnIndex}" + ) + if isinstance(txnIndex, Expr): + require_type(txnIndex, TealType.uint64) + + class GtxnExpr(TxnExpr): """An expression that accesses a transaction field from a transaction in the current group.""" def __init__(self, txnIndex: Union[int, Expr], field: TxnField) -> None: super().__init__(Op.gtxn, "Gtxn", field) + validate_txn_index_or_throw(txnIndex) self.txnIndex = txnIndex def __str__(self): @@ -51,6 +61,7 @@ def __init__( self, txnIndex: Union[int, Expr], field: TxnField, index: Union[int, Expr] ) -> None: super().__init__(Op.gtxna, Op.gtxnas, "Gtxna", field, index) + validate_txn_index_or_throw(txnIndex) self.txnIndex = txnIndex def __str__(self): diff --git a/pyteal/ast/gtxn_test.py b/pyteal/ast/gtxn_test.py index 168090918..9b895d100 100644 --- a/pyteal/ast/gtxn_test.py +++ b/pyteal/ast/gtxn_test.py @@ -2,19 +2,57 @@ from .. import * +teal6Options = CompileOptions(version=6) -def test_gtxn_invalid(): - with pytest.raises(TealInputError): - Gtxn[-1].fee() - - with pytest.raises(TealInputError): - Gtxn[MAX_GROUP_SIZE + 1].sender() - with pytest.raises(TealTypeError): - Gtxn[Pop(Int(0))].sender() - - with pytest.raises(TealTypeError): - Gtxn[Bytes("index")].sender() +def test_gtxn_invalid(): + for f, e in [ + (lambda: Gtxn[-1], TealInputError), + (lambda: Gtxn[MAX_GROUP_SIZE + 1], TealInputError), + (lambda: Gtxn[Pop(Int(0))], TealTypeError), + (lambda: Gtxn[Bytes("index")], TealTypeError), + ]: + with pytest.raises(e): + f() + + +def test_gtxn_expr_invalid(): + for f, e in [ + (lambda: GtxnExpr(Assert(Int(1)), TxnField.sender), TealTypeError), + ]: + with pytest.raises(e): + f() + + +def test_gtxn_expr_valid(): + [ + e.__teal__(teal6Options) + for e in [ + GtxnExpr(1, TxnField.sender), + GtxnExpr(Int(1), TxnField.sender), + ] + ] + + +def test_gtxna_expr_invalid(): + for f, e in [ + (lambda: GtxnaExpr("Invalid_type", TxnField.assets, 1), TealInputError), + (lambda: GtxnaExpr(1, TxnField.assets, "Invalid_type"), TealInputError), + (lambda: GtxnaExpr(Assert(Int(1)), TxnField.assets, 1), TealTypeError), + (lambda: GtxnaExpr(1, TxnField.assets, Assert(Int(1))), TealTypeError), + ]: + with pytest.raises(e): + f() + + +def test_gtxna_expr_valid(): + [ + e.__teal__(teal6Options) + for e in [ + GtxnaExpr(1, TxnField.assets, 1), + GtxnaExpr(Int(1), TxnField.assets, Int(1)), + ] + ] # txn_test.py performs additional testing diff --git a/pyteal/ast/itxn.py b/pyteal/ast/itxn.py index 6736c7113..ff857c808 100644 --- a/pyteal/ast/itxn.py +++ b/pyteal/ast/itxn.py @@ -120,7 +120,7 @@ def Submit(cls) -> Expr: :any:`InnerTxnBuilder.Begin` and :any:`InnerTxnBuilder.SetField` must be called before submitting an inner transaction. - This will fail fail if 16 inner transactions have already been executed, or if the + This will fail if 256 inner transactions have already been executed, or if the inner transaction itself fails. Upon failure, the current program will immediately exit and fail as well. @@ -204,7 +204,8 @@ def SetFields(cls, fields: Dict[TxnField, Union[Expr, List[Expr]]]) -> Expr: InnerTxnBuilder.__module__ = "pyteal" InnerTxn: TxnObject = TxnObject( - TxnExprBuilder(Op.itxn, "InnerTxn"), TxnaExprBuilder(Op.itxna, None, "InnerTxna") + TxnExprBuilder(Op.itxn, "InnerTxn"), + TxnaExprBuilder(Op.itxna, Op.itxnas, "InnerTxna"), ) InnerTxn.__module__ = "pyteal" diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index c1a6f9a14..abb7d3c52 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -161,6 +161,15 @@ def type_of(self): class TxnaExpr(LeafExpr): """An expression that accesses a transaction array field from the current transaction.""" + @staticmethod + def __validate_index_or_throw(index: Union[int, Expr]): + if not isinstance(index, (int, Expr)): + raise TealInputError( + f"Invalid index type: Expected int or Expr, but received {index}." + ) + if isinstance(index, Expr): + require_type(index, TealType.uint64) + def __init__( self, staticOp: Op, @@ -172,6 +181,8 @@ def __init__( super().__init__() if not field.is_array: raise TealInputError("Unexpected non-array field: {}".format(field)) + self.__validate_index_or_throw(index) + self.staticOp = staticOp self.dynamicOp = dynamicOp self.name = name diff --git a/pyteal/ast/txn_test.py b/pyteal/ast/txn_test.py index efe81ce08..458003f7d 100644 --- a/pyteal/ast/txn_test.py +++ b/pyteal/ast/txn_test.py @@ -96,9 +96,9 @@ def test_txn_fields(): [], [TealOp(dynamicGtxnArg, Op.int, 0)], ), - (InnerTxn, Op.itxn, Op.itxna, None, [], []), + (InnerTxn, Op.itxn, Op.itxna, Op.itxnas, [], []), *[ - (Gitxn[i], Op.gitxn, Op.gitxna, None, [i], []) + (Gitxn[i], Op.gitxn, Op.gitxna, Op.gitxnas, [i], []) for i in range(MAX_GROUP_SIZE) ], ] From 4daad78ce641135a86a622f4ce0c866f4eb9868c Mon Sep 17 00:00:00 2001 From: Michael Diamant Date: Thu, 17 Feb 2022 14:30:41 -0500 Subject: [PATCH 5/8] Update last_log docstring Co-authored-by: Jason Paulos --- pyteal/ast/txn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index abb7d3c52..80098d6f7 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -708,7 +708,7 @@ def created_application_id(self) -> TxnExpr: return self.makeTxnExpr(TxnField.created_application_id) def last_log(self) -> TxnExpr: - """Get the last logged message from a transaction. + """A convenience method for getting the last logged message from a transaction. Only application calls may log a message. Returns an empty string if no messages were logged. From a181b737aefd63edcf821837a9b6845c991e64c2 Mon Sep 17 00:00:00 2001 From: michaeldiamant Date: Thu, 17 Feb 2022 15:25:59 -0500 Subject: [PATCH 6/8] Expose state_proof_pk txn field --- pyteal/ast/txn.py | 8 ++++++++ pyteal/ast/txn_test.py | 1 + 2 files changed, 9 insertions(+) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index 80098d6f7..ca14da495 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -109,6 +109,7 @@ class TxnField(Enum): created_asset_id = (60, "CreatedAssetID", TealType.uint64, False, 5) created_application_id = (61, "CreatedApplicationID", TealType.uint64, False, 5) last_log = (62, "LastLog", TealType.bytes, False, 6) + state_proof_pk = (63, "StateProofPK", TealType.bytes, False, 6) def __init__( self, id: int, name: str, type: TealType, is_array: bool, min_version: int @@ -718,6 +719,13 @@ def last_log(self) -> TxnExpr: """ return self.makeTxnExpr(TxnField.last_log) + def state_proof_pk(self) -> TxnExpr: + """Get the state proof public key commitment from a transaction. + + Requires TEAL version 6 or higher. + """ + return self.makeTxnExpr(TxnField.state_proof_pk) + @property def application_args(self) -> TxnArray: """Application call arguments array. diff --git a/pyteal/ast/txn_test.py b/pyteal/ast/txn_test.py index 458003f7d..3b7f6d741 100644 --- a/pyteal/ast/txn_test.py +++ b/pyteal/ast/txn_test.py @@ -60,6 +60,7 @@ TxnField.created_asset_id: lambda txn: txn.created_asset_id(), TxnField.created_application_id: lambda txn: txn.created_application_id(), TxnField.last_log: lambda txn: txn.last_log(), + TxnField.state_proof_pk: lambda txn: txn.state_proof_pk(), } arrayFieldToProperty: Dict[TxnField, Callable[[TxnObject], TxnArray]] = { From 79a31df70423277ef7ede671a9b604bd05e7c55a Mon Sep 17 00:00:00 2001 From: michaeldiamant Date: Thu, 17 Feb 2022 15:35:24 -0500 Subject: [PATCH 7/8] Update transaction field docs to reflect TEAL v6 --- pyteal/ast/txn.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index ca14da495..5ce682e1f 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -618,11 +618,12 @@ def config_asset_clawback(self) -> TxnExpr: def created_asset_id(self) -> TxnExpr: """Get the asset ID allocated by the creation of an ASA. - Currently this only works on inner transactions. - Only set when :any:`type_enum()` is :any:`TxnType.AssetConfig` and this is an asset creation transaction. Requires TEAL version 5 or higher. + + * v5 - Only works on inner transactions. + * >= v6 - Works on all transaction types. """ return self.makeTxnExpr(TxnField.created_asset_id) @@ -700,11 +701,12 @@ def extra_program_pages(self) -> TxnExpr: def created_application_id(self) -> TxnExpr: """Get the application ID allocated by the creation of an application. - Currently this only works on inner transactions. - Only set when :any:`type_enum()` is :any:`TxnType.ApplicationCall` and this is an app creation call. Requires TEAL version 5 or higher. + + * v5 - Only works on inner transactions. + * >= v6 - Works on all transaction types. """ return self.makeTxnExpr(TxnField.created_application_id) @@ -766,11 +768,12 @@ def applications(self) -> TxnArray: def logs(self) -> TxnArray: """The log messages emitted by an application call. - Currently this only works on inner transactions. - :type: TxnArray Requires TEAL version 5 or higher. + + * v5 - Only works on inner transactions. + * >= v6 - Works with all transaction types. """ return TxnArray(self, TxnField.logs, TxnField.num_logs) From 07e434ab3eba014f4b3b07b75564fc24139f92b5 Mon Sep 17 00:00:00 2001 From: michaeldiamant Date: Thu, 17 Feb 2022 15:40:06 -0500 Subject: [PATCH 8/8] Update transaction field docs to reflect TEAL v6 --- pyteal/ast/txn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyteal/ast/txn.py b/pyteal/ast/txn.py index 5ce682e1f..d619ef0bc 100644 --- a/pyteal/ast/txn.py +++ b/pyteal/ast/txn.py @@ -623,7 +623,7 @@ def created_asset_id(self) -> TxnExpr: Requires TEAL version 5 or higher. * v5 - Only works on inner transactions. - * >= v6 - Works on all transaction types. + * >= v6 - Works on top-level and inner transactions. """ return self.makeTxnExpr(TxnField.created_asset_id) @@ -706,7 +706,7 @@ def created_application_id(self) -> TxnExpr: Requires TEAL version 5 or higher. * v5 - Only works on inner transactions. - * >= v6 - Works on all transaction types. + * >= v6 - Works on top-level and inner transactions. """ return self.makeTxnExpr(TxnField.created_application_id) @@ -773,7 +773,7 @@ def logs(self) -> TxnArray: Requires TEAL version 5 or higher. * v5 - Only works on inner transactions. - * >= v6 - Works with all transaction types. + * >= v6 - Works on top-level and inner transactions. """ return TxnArray(self, TxnField.logs, TxnField.num_logs)