Skip to content

Commit fdb4833

Browse files
Merge pull request #1 from michaeldiamant/more-teal6-ops_gitxnas_itxnas_validation
Move type validation to constructors for gtxn and gitxn variants
2 parents 57725e0 + 6201c46 commit fdb4833

File tree

5 files changed

+146
-47
lines changed

5 files changed

+146
-47
lines changed

pyteal/ast/gitxn.py

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ..ir import TealOp, Op, TealBlock
77
from .expr import Expr
88
from .txn import TxnExpr, TxnField, TxnObject, TxnaExpr
9+
from ..types import require_type, TealType
910

1011
if TYPE_CHECKING:
1112
from ..compiler import CompileOptions
@@ -16,6 +17,15 @@ class GitxnExpr(TxnExpr):
1617

1718
def __init__(self, txnIndex: int, field: TxnField) -> None:
1819
super().__init__(Op.gitxn, "Gitxn", field)
20+
21+
# Currently we do not have gitxns. Only gitxn with immediate transaction index supported.
22+
if type(txnIndex) is not int:
23+
raise TealInputError(
24+
"Invalid gitxn syntax with immediate transaction field {} and transaction index {}".format(
25+
field, txnIndex
26+
)
27+
)
28+
1929
self.txnIndex = txnIndex
2030

2131
def __str__(self):
@@ -24,14 +34,6 @@ def __str__(self):
2434
def __teal__(self, options: "CompileOptions"):
2535
verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)
2636

27-
# currently we do not have gitxns, only gitxn with immediate transaction index supported
28-
if type(self.txnIndex) is not int:
29-
raise TealInputError(
30-
"Invalid gitxn syntax with immediate transaction field {} and transaction index {}".format(
31-
self.field, self.txnIndex
32-
)
33-
)
34-
3537
verifyTealVersion(
3638
Op.gitxn.min_version,
3739
options.version,
@@ -49,6 +51,12 @@ class GitxnaExpr(TxnaExpr):
4951

5052
def __init__(self, txnIndex: int, field: TxnField, index: Union[int, Expr]) -> None:
5153
super().__init__(Op.gitxna, Op.gitxnas, "Gitxna", field, index)
54+
55+
if type(txnIndex) is not int:
56+
raise TealInputError(
57+
f"Invalid txnIndex type: Expected int, but received {txnIndex}."
58+
)
59+
5260
self.txnIndex = txnIndex
5361

5462
def __str__(self):
@@ -59,15 +67,6 @@ def __str__(self):
5967
def __teal__(self, options: "CompileOptions"):
6068
verifyFieldVersion(self.field.arg_name, self.field.min_version, options.version)
6169

62-
if type(self.txnIndex) is not int or not (
63-
type(self.index) is int or isinstance(self.index, Expr)
64-
):
65-
raise TealInputError(
66-
"Invalid gitxna syntax with immediate transaction index {}, transaction field {}, array index {}".format(
67-
self.txnIndex, self.field, self.index
68-
)
69-
)
70-
7170
if type(self.index) is int:
7271
opToUse = Op.gitxna
7372
else:

pyteal/ast/gitxn_test.py

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,35 +7,75 @@
77

88

99
def test_gitxn_invalid():
10-
with pytest.raises(TealInputError):
11-
GitxnExpr(0, TxnField.sender).__teal__(teal5Options)
12-
13-
with pytest.raises(TealInputError):
14-
Gitxn[MAX_GROUP_SIZE].sender()
15-
16-
with pytest.raises(TealInputError):
17-
Gitxn[-1].asset_sender()
18-
19-
with pytest.raises(TealInputError):
20-
Gitxn[Bytes("first")].sender()
10+
for ctor, e in [
11+
(
12+
lambda: Gitxn[MAX_GROUP_SIZE],
13+
TealInputError,
14+
),
15+
(
16+
lambda: Gitxn[-1],
17+
TealInputError,
18+
),
19+
]:
20+
with pytest.raises(e):
21+
ctor()
2122

2223

2324
def test_gitxn_valid():
2425
for i in range(MAX_GROUP_SIZE):
2526
Gitxn[i].sender()
2627

2728

28-
def test_gitxna_invalid():
29-
with pytest.raises(TealInputError):
30-
GitxnaExpr("Invalid_type", TxnField.application_args, 1).__teal__(teal6Options)
31-
32-
with pytest.raises(TealInputError):
33-
GitxnaExpr(0, TxnField.application_args, "Invalid_type").__teal__(teal6Options)
29+
def test_gitxn_expr_invalid():
30+
for f, e in [
31+
(
32+
lambda: GitxnExpr(Int(1), TxnField.sender),
33+
TealInputError,
34+
),
35+
(
36+
lambda: GitxnExpr(1, TxnField.sender).__teal__(teal5Options),
37+
TealInputError,
38+
),
39+
]:
40+
with pytest.raises(e):
41+
f()
42+
43+
44+
def test_gitxn_expr_valid():
45+
GitxnExpr(1, TxnField.sender).__teal__(teal6Options)
46+
47+
48+
def test_gitxna_expr_invalid():
49+
for f, e in [
50+
(
51+
lambda: GitxnaExpr("Invalid_type", TxnField.application_args, 1),
52+
TealInputError,
53+
),
54+
(
55+
lambda: GitxnaExpr(1, TxnField.application_args, "Invalid_type"),
56+
TealInputError,
57+
),
58+
(
59+
lambda: GitxnaExpr(0, TxnField.application_args, Assert(Int(1))),
60+
TealTypeError,
61+
),
62+
(
63+
lambda: GitxnaExpr(0, TxnField.application_args, 0).__teal__(teal5Options),
64+
TealInputError,
65+
),
66+
]:
67+
with pytest.raises(e):
68+
f()
3469

3570

3671
def test_gitxna_valid():
37-
GitxnaExpr(0, TxnField.application_args, 1).__teal__(teal6Options)
38-
GitxnaExpr(0, TxnField.application_args, Assert(Int(1))).__teal__(teal6Options)
72+
[
73+
e.__teal__(teal6Options)
74+
for e in [
75+
GitxnaExpr(0, TxnField.application_args, 1),
76+
GitxnaExpr(0, TxnField.application_args, Int(1)),
77+
]
78+
]
3979

4080

4181
# txn_test.py performs additional testing

pyteal/ast/gtxn.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,21 @@
1111
from ..compiler import CompileOptions
1212

1313

14+
def validate_txn_index_or_throw(txnIndex: Union[int, Expr]):
15+
if not isinstance(txnIndex, (int, Expr)):
16+
raise TealInputError(
17+
f"Invalid txnIndex type: Expected int or Expr, but received {txnIndex}"
18+
)
19+
if isinstance(txnIndex, Expr):
20+
require_type(txnIndex, TealType.uint64)
21+
22+
1423
class GtxnExpr(TxnExpr):
1524
"""An expression that accesses a transaction field from a transaction in the current group."""
1625

1726
def __init__(self, txnIndex: Union[int, Expr], field: TxnField) -> None:
1827
super().__init__(Op.gtxn, "Gtxn", field)
28+
validate_txn_index_or_throw(txnIndex)
1929
self.txnIndex = txnIndex
2030

2131
def __str__(self):
@@ -51,6 +61,7 @@ def __init__(
5161
self, txnIndex: Union[int, Expr], field: TxnField, index: Union[int, Expr]
5262
) -> None:
5363
super().__init__(Op.gtxna, Op.gtxnas, "Gtxna", field, index)
64+
validate_txn_index_or_throw(txnIndex)
5465
self.txnIndex = txnIndex
5566

5667
def __str__(self):

pyteal/ast/gtxn_test.py

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,57 @@
22

33
from .. import *
44

5+
teal6Options = CompileOptions(version=6)
56

6-
def test_gtxn_invalid():
7-
with pytest.raises(TealInputError):
8-
Gtxn[-1].fee()
9-
10-
with pytest.raises(TealInputError):
11-
Gtxn[MAX_GROUP_SIZE + 1].sender()
127

13-
with pytest.raises(TealTypeError):
14-
Gtxn[Pop(Int(0))].sender()
15-
16-
with pytest.raises(TealTypeError):
17-
Gtxn[Bytes("index")].sender()
8+
def test_gtxn_invalid():
9+
for f, e in [
10+
(lambda: Gtxn[-1], TealInputError),
11+
(lambda: Gtxn[MAX_GROUP_SIZE + 1], TealInputError),
12+
(lambda: Gtxn[Pop(Int(0))], TealTypeError),
13+
(lambda: Gtxn[Bytes("index")], TealTypeError),
14+
]:
15+
with pytest.raises(e):
16+
f()
17+
18+
19+
def test_gtxn_expr_invalid():
20+
for f, e in [
21+
(lambda: GtxnExpr(Assert(Int(1)), TxnField.sender), TealTypeError),
22+
]:
23+
with pytest.raises(e):
24+
f()
25+
26+
27+
def test_gtxn_expr_valid():
28+
[
29+
e.__teal__(teal6Options)
30+
for e in [
31+
GtxnExpr(1, TxnField.sender),
32+
GtxnExpr(Int(1), TxnField.sender),
33+
]
34+
]
35+
36+
37+
def test_gtxna_expr_invalid():
38+
for f, e in [
39+
(lambda: GtxnaExpr("Invalid_type", TxnField.assets, 1), TealInputError),
40+
(lambda: GtxnaExpr(1, TxnField.assets, "Invalid_type"), TealInputError),
41+
(lambda: GtxnaExpr(Assert(Int(1)), TxnField.assets, 1), TealTypeError),
42+
(lambda: GtxnaExpr(1, TxnField.assets, Assert(Int(1))), TealTypeError),
43+
]:
44+
with pytest.raises(e):
45+
f()
46+
47+
48+
def test_gtxna_expr_valid():
49+
[
50+
e.__teal__(teal6Options)
51+
for e in [
52+
GtxnaExpr(1, TxnField.assets, 1),
53+
GtxnaExpr(Int(1), TxnField.assets, Int(1)),
54+
]
55+
]
1856

1957

2058
# txn_test.py performs additional testing

pyteal/ast/txn.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ def type_of(self):
161161
class TxnaExpr(LeafExpr):
162162
"""An expression that accesses a transaction array field from the current transaction."""
163163

164+
@staticmethod
165+
def __validate_index_or_throw(index: Union[int, Expr]):
166+
if not isinstance(index, (int, Expr)):
167+
raise TealInputError(
168+
f"Invalid index type: Expected int or Expr, but received {index}."
169+
)
170+
if isinstance(index, Expr):
171+
require_type(index, TealType.uint64)
172+
164173
def __init__(
165174
self,
166175
staticOp: Op,
@@ -172,6 +181,8 @@ def __init__(
172181
super().__init__()
173182
if not field.is_array:
174183
raise TealInputError("Unexpected non-array field: {}".format(field))
184+
self.__validate_index_or_throw(index)
185+
175186
self.staticOp = staticOp
176187
self.dynamicOp = dynamicOp
177188
self.name = name

0 commit comments

Comments
 (0)