Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
99a2850
starting to add box support
barnjamin May 19, 2022
4684025
Add boxes to atc
barnjamin May 20, 2022
c10cff1
Format files with black
algochoi May 20, 2022
c5fbd30
Add boxes docstring
algochoi May 20, 2022
d9381e3
Add boxes support for appl creation
algochoi May 20, 2022
bd0c41e
Update cucumber steps for appl txn encoding for boxes
algochoi May 20, 2022
8313c8e
Formatting
algochoi May 20, 2022
af73bbe
Point testing branch to box-reference (WIP)
algochoi May 20, 2022
5f39d5c
Sort imports on relevant files and format
algochoi May 23, 2022
f949e09
Add translation for foreign apps in box refs and some local unit tests
algochoi May 23, 2022
ce32d63
Add some invalid cases for box translation
algochoi May 23, 2022
3b20d42
Check for None when iterating box refs
algochoi May 23, 2022
af9f6b4
Add self app id references and tests
algochoi May 23, 2022
adbd4da
Minor changes for box support
algochoi May 23, 2022
b6f3653
Split boxref to separate file
algochoi May 24, 2022
6f15a91
Change box name type to bytes
algochoi May 24, 2022
7ddcd77
Change docs references from box string to bytes
algochoi May 24, 2022
ae3fba9
Refactor cucumber steps
algochoi May 24, 2022
1e1b5d7
Refactoring code and adding docstrings
algochoi Jun 1, 2022
afceed7
Add another comment
algochoi Jun 1, 2022
9c039f4
Change test steps to encode box args like app args
algochoi Jun 2, 2022
1dd96e9
Add safety checks for box references
algochoi Jun 2, 2022
919923c
Add some detailed errors and refactor
algochoi Jun 2, 2022
b861c44
Format unit test
algochoi Jun 2, 2022
ffe6b1c
Fix foreign index error and revise undictify method
algochoi Jun 3, 2022
ff5b7e1
Merge branch 'feature/box-storage' into box-support
algochoi Jun 3, 2022
8f48f6c
Finish merging cucumber steps
algochoi Jun 3, 2022
71bee9a
Formatting
algochoi Jun 3, 2022
935bd0a
Fix box tests again
algochoi Jun 3, 2022
a94cf7e
Encoded as bytes unit test (#344)
tzaffi Jun 3, 2022
1bc926b
Accept AttributeError for foreign apps array if it is referencing its…
algochoi Jun 6, 2022
f53f274
Add unit test for empty foreign app array
algochoi Jun 6, 2022
959ec1d
Change unit test to pass in None foreign array
algochoi Jun 6, 2022
d45c6c2
Change undictify method for boxes
algochoi Jun 6, 2022
05d5a49
Formatting
algochoi Jun 6, 2022
6960daa
Change test branch
algochoi Jun 7, 2022
485d2ad
Change type hints for atc boxes
algochoi Jun 7, 2022
9eff272
Check for int type for box reference id
algochoi Jun 8, 2022
8a26a05
Change type ignore annotation
algochoi Jun 15, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion algosdk/atomic_transaction_composer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from abc import ABC, abstractmethod
Comment thread
tzaffi marked this conversation as resolved.
import base64
import copy
from abc import ABC, abstractmethod
Comment thread
tzaffi marked this conversation as resolved.
from enum import IntEnum
from typing import Any, List, Optional, TypeVar, Union

Expand Down Expand Up @@ -173,6 +173,7 @@ def add_method_call(
note: bytes = None,
lease: bytes = None,
rekey_to: str = None,
boxes: List[transaction.BoxReference] = None,
) -> "AtomicTransactionComposer":
"""
Add a smart contract method call to this atomic group.
Expand Down Expand Up @@ -210,6 +211,7 @@ def add_method_call(
with the same sender and lease can be confirmed in this
transaction's valid rounds
rekey_to (str, optional): additionally rekey the sender to this address
boxes (list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

"""
if self.status != AtomicTransactionComposerStatus.BUILDING:
Expand Down Expand Up @@ -350,6 +352,7 @@ def add_method_call(
lease=lease,
rekey_to=rekey_to,
extra_pages=extra_pages,
boxes=boxes,
)
txn_with_signer = TransactionWithSigner(method_txn, signer)
txn_list.append(txn_with_signer)
Expand Down
64 changes: 64 additions & 0 deletions algosdk/box_reference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from collections import OrderedDict
from typing import List, Tuple

from algosdk import error


class BoxReference:
def __init__(self, app_index: int, name: bytes):
self.app_index = app_index
self.name = name

@staticmethod
def translate_box_references(
references: List[Tuple[int, bytes]],
foreign_apps: List[int],
this_app_id: int,
) -> List["BoxReference"]:
if not references:
return []

box_references = []
for ref in references:
# Try coercing reference id and name.
from algosdk.future.transaction import ApplicationCallTxn

ref_id, ref_name = int(ref[0]), ApplicationCallTxn.as_bytes(ref[1])
index = 0
try:
# Foreign apps start from index 1; index 0 is its own app ID.
index = foreign_apps.index(ref_id) + 1
except ValueError:
# Check if the app referenced is itself after checking the
# foreign apps array (in case its own app id is in its own
# foreign apps array).
if ref_id == 0 or ref_id == this_app_id:
pass
else:
raise error.InvalidForeignAppIdError(
f"Box ref with appId {ref_id} not in foreign-apps"
)
box_references.append(BoxReference(index, ref_name))
return box_references

def dictify(self):
d = dict()
Comment thread
tzaffi marked this conversation as resolved.
if self.app_index:
d["i"] = self.app_index
if self.name:
d["n"] = self.name
od = OrderedDict(sorted(d.items()))
return od

@staticmethod
def undictify(d):
args = {
"app_index": d["i"] if "i" in d else None,
"name": d["n"] if "n" in d else None,
}
return args
Comment thread
tzaffi marked this conversation as resolved.
Outdated

def __eq__(self, other):
if not isinstance(other, BoxReference):
return False
return self.app_index == other.app_index and self.name == other.name
5 changes: 5 additions & 0 deletions algosdk/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,3 +222,8 @@ def __init__(self, msg):
class AtomicTransactionComposerError(Exception):
def __init__(self, msg):
super().__init__(msg)


class InvalidForeignAppIdError(Exception):
Comment thread
tzaffi marked this conversation as resolved.
Outdated
def __init__(self, msg):
super().__init__(msg)
77 changes: 55 additions & 22 deletions algosdk/future/transaction.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
from typing import List, Union
import base64
from collections import OrderedDict
from enum import IntEnum
from typing import List, Union

import msgpack
from collections import OrderedDict
from .. import account
from .. import constants
from .. import encoding
from .. import error
from .. import logic
from .. import transaction
from ..v2client import algod, models
from nacl.signing import SigningKey, VerifyKey
from algosdk import account, constants, encoding, error, logic, transaction
from algosdk.box_reference import BoxReference
from algosdk.v2client import algod, models
from nacl.exceptions import BadSignatureError
from nacl.signing import SigningKey, VerifyKey


class SuggestedParams:
Expand Down Expand Up @@ -1572,6 +1569,7 @@ class ApplicationCallTxn(Transaction):
foreign_apps (list[int], optional): list of other applications (identified by index) involved in call
foreign_assets (list[int], optional): list of assets involved in call
extra_pages (int, optional): additional program space for supporting larger programs. A page is 1024 bytes.
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

Attributes:
sender (str)
Expand All @@ -1590,6 +1588,7 @@ class ApplicationCallTxn(Transaction):
foreign_apps (list[int])
foreign_assets (list[int])
extra_pages (int)
boxes (list[(int, bytes)])
"""

def __init__(
Expand All @@ -1610,6 +1609,7 @@ def __init__(
lease=None,
rekey_to=None,
extra_pages=0,
boxes=None,
):
Transaction.__init__(
self, sender, sp, note, lease, constants.appcall_txn, rekey_to
Expand All @@ -1625,6 +1625,7 @@ def __init__(
self.foreign_apps = self.int_list(foreign_apps)
self.foreign_assets = self.int_list(foreign_assets)
self.extra_pages = extra_pages
self.boxes = BoxReference.translate_box_references(boxes, self.foreign_apps, self.index) # type: ignore
Comment thread
algochoi marked this conversation as resolved.
if not sp.flat_fee:
self.fee = max(
self.estimate_size() * self.fee, constants.min_txn_fee
Expand All @@ -1650,23 +1651,24 @@ def teal_bytes(teal):
), "Program {} is not bytes".format(teal)
return teal

@staticmethod
Comment thread
tzaffi marked this conversation as resolved.
Outdated
def as_bytes(e):
"""Confirm or coerce element to bytes."""
if isinstance(e, (bytes, bytearray)):
return e
if isinstance(e, str):
return e.encode()
if isinstance(e, int):
# Uses 8 bytes, big endian to match TEAL's btoi
return e.to_bytes(8, "big") # raises for negative or too big
assert False, "{} is not bytes, str, or int".format(e)
Comment thread
tzaffi marked this conversation as resolved.
Outdated

@staticmethod
def bytes_list(lst):
"""Confirm or coerce list elements to bytes. Return None for empty/false lst."""

def as_bytes(e):
if isinstance(e, (bytes, bytearray)):
return e
if isinstance(e, str):
return e.encode()
if isinstance(e, int):
# Uses 8 bytes, big endian to match TEAL's btoi
return e.to_bytes(8, "big") # raises for negative or too big
assert False, "{} is not bytes, str, or int".format(e)

if not lst:
return None
return [as_bytes(elt) for elt in lst]
return [ApplicationCallTxn.as_bytes(elt) for elt in lst]

@staticmethod
def int_list(lst):
Expand Down Expand Up @@ -1701,6 +1703,8 @@ def dictify(self):
d["apas"] = self.foreign_assets
if self.extra_pages:
d["apep"] = self.extra_pages
if self.boxes:
d["apbx"] = [box.dictify() for box in self.boxes]

d.update(super(ApplicationCallTxn, self).dictify())
od = OrderedDict(sorted(d.items()))
Expand All @@ -1725,6 +1729,12 @@ def _undictify(d):
"foreign_apps": d["apfa"] if "apfa" in d else None,
"foreign_assets": d["apas"] if "apas" in d else None,
"extra_pages": d["apep"] if "apep" in d else 0,
"boxes": [
BoxReference(**BoxReference.undictify(box))
Comment thread
tzaffi marked this conversation as resolved.
Outdated
for box in d["apbx"]
]
if "apbx" in d
else None,
}
if args["accounts"]:
args["accounts"] = [
Expand All @@ -1749,6 +1759,7 @@ def __eq__(self, other):
and self.foreign_apps == other.foreign_apps
and self.foreign_assets == other.foreign_assets
and self.extra_pages == other.extra_pages
and self.boxes == other.boxes
)


Expand All @@ -1772,6 +1783,7 @@ class ApplicationCreateTxn(ApplicationCallTxn):
lease(bytes, optional): transaction lease field
rekey_to(str, optional): rekey-to field, see Transaction
extra_pages(int, optional): provides extra program size
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

Attributes:
See ApplicationCallTxn
Expand All @@ -1794,6 +1806,7 @@ def __init__(
lease=None,
rekey_to=None,
extra_pages=0,
boxes=None,
):
ApplicationCallTxn.__init__(
self,
Expand All @@ -1813,6 +1826,7 @@ def __init__(
lease=lease,
rekey_to=rekey_to,
extra_pages=extra_pages,
boxes=boxes,
)


Expand All @@ -1833,6 +1847,8 @@ class ApplicationUpdateTxn(ApplicationCallTxn):
note(bytes, optional): transaction note field
lease(bytes, optional): transaction lease field
rekey_to(str, optional): rekey-to field, see Transaction
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access


Attributes:
See ApplicationCallTxn
Expand All @@ -1852,6 +1868,7 @@ def __init__(
note=None,
lease=None,
rekey_to=None,
boxes=None,
):
ApplicationCallTxn.__init__(
self,
Expand All @@ -1868,6 +1885,7 @@ def __init__(
note=note,
lease=lease,
rekey_to=rekey_to,
boxes=boxes,
)


Expand All @@ -1886,6 +1904,7 @@ class ApplicationDeleteTxn(ApplicationCallTxn):
note(bytes, optional): transaction note field
lease(bytes, optional): transaction lease field
rekey_to(str, optional): rekey-to field, see Transaction
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

Attributes:
See ApplicationCallTxn
Expand All @@ -1903,6 +1922,7 @@ def __init__(
note=None,
lease=None,
rekey_to=None,
boxes=None,
):
ApplicationCallTxn.__init__(
self,
Expand All @@ -1917,6 +1937,7 @@ def __init__(
note=note,
lease=lease,
rekey_to=rekey_to,
boxes=boxes,
)


Expand All @@ -1935,6 +1956,7 @@ class ApplicationOptInTxn(ApplicationCallTxn):
note(bytes, optional): transaction note field
lease(bytes, optional): transaction lease field
rekey_to(str, optional): rekey-to field, see Transaction
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

Attributes:
See ApplicationCallTxn
Expand All @@ -1952,6 +1974,7 @@ def __init__(
note=None,
lease=None,
rekey_to=None,
boxes=None,
):
ApplicationCallTxn.__init__(
self,
Expand All @@ -1966,6 +1989,7 @@ def __init__(
note=note,
lease=lease,
rekey_to=rekey_to,
boxes=boxes,
)


Expand All @@ -1984,6 +2008,7 @@ class ApplicationCloseOutTxn(ApplicationCallTxn):
note(bytes, optional): transaction note field
lease(bytes, optional): transaction lease field
rekey_to(str, optional): rekey-to field, see Transaction
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

Attributes:
See ApplicationCallTxn
Expand All @@ -2001,6 +2026,7 @@ def __init__(
note=None,
lease=None,
rekey_to=None,
boxes=None,
):
ApplicationCallTxn.__init__(
self,
Expand All @@ -2015,6 +2041,7 @@ def __init__(
note=note,
lease=lease,
rekey_to=rekey_to,
boxes=boxes,
)


Expand All @@ -2033,6 +2060,7 @@ class ApplicationClearStateTxn(ApplicationCallTxn):
note(bytes, optional): transaction note field
lease(bytes, optional): transaction lease field
rekey_to(str, optional): rekey-to field, see Transaction
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

Attributes:
See ApplicationCallTxn
Expand All @@ -2050,6 +2078,7 @@ def __init__(
note=None,
lease=None,
rekey_to=None,
boxes=None,
):
ApplicationCallTxn.__init__(
self,
Expand All @@ -2064,6 +2093,7 @@ def __init__(
note=note,
lease=lease,
rekey_to=rekey_to,
boxes=boxes,
)


Expand All @@ -2083,6 +2113,7 @@ class ApplicationNoOpTxn(ApplicationCallTxn):
note(bytes, optional): transaction note field
lease(bytes, optional): transaction lease field
rekey_to(str, optional): rekey-to field, see Transaction
boxes(list[(int, bytes)], optional): list of tuples specifying app id and key for boxes the app may access

Attributes:
See ApplicationCallTxn
Expand All @@ -2100,6 +2131,7 @@ def __init__(
note=None,
lease=None,
rekey_to=None,
boxes=None,
):
ApplicationCallTxn.__init__(
self,
Expand All @@ -2114,6 +2146,7 @@ def __init__(
note=note,
lease=lease,
rekey_to=rekey_to,
boxes=boxes,
)


Expand Down
Loading