Skip to content

Commit

Permalink
Support TEAL 6 txn fields LastLog, StateProofPK and opcodes divw, itx…
Browse files Browse the repository at this point in the history
…nas, gitxnas (#174)

* adding new teal6 ops, no pyteal expressions defined for them yet

* Add opcode support for divw

* Add opcode support for divw (#192)

* 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

* Update last_log docstring

Co-authored-by: Jason Paulos <jasonpaulos@users.noreply.github.com>

* Expose state_proof_pk txn field

* Update transaction field docs to reflect TEAL v6

* Update transaction field docs to reflect TEAL v6

Co-authored-by: michaeldiamant <michaeldiamant@users.noreply.github.com>
Co-authored-by: Jason Paulos <jasonpaulos@users.noreply.github.com>
  • Loading branch information
3 people authored Feb 17, 2022
1 parent bf4047f commit cdcd0ca
Show file tree
Hide file tree
Showing 12 changed files with 267 additions and 56 deletions.
1 change: 1 addition & 0 deletions pyteal/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ __all__ = [
"Div",
"Mod",
"Exp",
"Divw",
"BitwiseAnd",
"BitwiseOr",
"BitwiseXor",
Expand Down
3 changes: 2 additions & 1 deletion pyteal/ast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -187,6 +187,7 @@
"Div",
"Mod",
"Exp",
"Divw",
"BitwiseAnd",
"BitwiseOr",
"BitwiseXor",
Expand Down
58 changes: 36 additions & 22 deletions pyteal/ast/gitxn.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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):
Expand All @@ -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,
Expand All @@ -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):
Expand All @@ -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"
Expand All @@ -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),
)


Expand Down
75 changes: 63 additions & 12 deletions pyteal/ast/gitxn_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
11 changes: 11 additions & 0 deletions pyteal/ast/gtxn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down
60 changes: 49 additions & 11 deletions pyteal/ast/gtxn_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 3 additions & 2 deletions pyteal/ast/itxn.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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"
22 changes: 22 additions & 0 deletions pyteal/ast/ternaryexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Loading

0 comments on commit cdcd0ca

Please sign in to comment.