Skip to content

Commit

Permalink
Merge branch 'release/v1.20.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
algolucky committed Nov 2, 2022
2 parents 671e136 + c2e9b23 commit 3b231ac
Show file tree
Hide file tree
Showing 16 changed files with 630 additions and 78 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Changelog

# v1.20.0

## What's Changed
### New Features
* Boxes: Add support for Boxes by @algochoi in https://github.com/algorand/py-algorand-sdk/pull/348
* `class StateSchema`'s method `undictify()` now returns a `StateSchema` object instead of a python `dict`

**Full Changelog**: https://github.com/algorand/py-algorand-sdk/compare/v1.19.0...v1.20.0

# v1.19.0

## What's Changed
### Enhancements
* REST API: Add algod block hash endpoint, add indexer block header-only param. by @winder in https://github.com/algorand/py-algorand-sdk/pull/390

**Full Changelog**: https://github.com/algorand/py-algorand-sdk/compare/v1.18.0...v1.19.0

# v1.18.0
### Enhancements
* Deprecation: Add deprecation warnings on v1 algod API and old transaction format by @algochoi in https://github.com/algorand/py-algorand-sdk/pull/381
Expand Down
8 changes: 6 additions & 2 deletions algosdk/atomic_transaction_composer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from abc import ABC, abstractmethod
import base64
import copy
from abc import ABC, abstractmethod
from enum import IntEnum
from typing import Any, List, Optional, TypeVar, Union
from typing import Any, List, Optional, Tuple, TypeVar, Union

from algosdk import abi, error
from algosdk.abi.address_type import AddressType
Expand Down Expand Up @@ -173,6 +173,7 @@ def add_method_call(
note: bytes = None,
lease: bytes = None,
rekey_to: str = None,
boxes: List[Tuple[int, bytes]] = 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 @@ -259,6 +261,7 @@ def add_method_call(
accounts = accounts[:] if accounts else []
foreign_apps = foreign_apps[:] if foreign_apps else []
foreign_assets = foreign_assets[:] if foreign_assets else []
boxes = boxes[:] if boxes else []

app_args = []
raw_values = []
Expand Down Expand Up @@ -350,6 +353,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
94 changes: 94 additions & 0 deletions algosdk/box_reference.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from collections import OrderedDict
from typing import List, Tuple, Union

from algosdk import encoding, error


class BoxReference:
"""
Represents a box reference with a foreign app index and the box name.
Args:
app_index (int): index of the application in the foreign app array
name (bytes): key for the box in bytes
"""

def __init__(self, app_index: int, name: bytes):
if app_index < 0:
raise ValueError(
f"Box app index must be a non-negative integer: {app_index}"
)
self.app_index = app_index
self.name = name

@staticmethod
def translate_box_reference(
ref: Tuple[int, Union[bytes, bytearray, str, int]],
foreign_apps: List[int],
this_app_id: int,
) -> "BoxReference":
# Try checking reference id and name type.
ref_id, ref_name = ref[0], encoding.encode_as_bytes(ref[1])
if not isinstance(ref_id, int):
raise TypeError("Box reference ID must be an int")

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, AttributeError):
# 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 and ref_id != this_app_id:
raise error.InvalidForeignIndexError(
f"Box ref with appId {ref_id} not in foreign-apps"
)
return BoxReference(index, ref_name)

@staticmethod
def translate_box_references(
references: List[Tuple[int, Union[bytes, bytearray, str, int]]],
foreign_apps: List[int],
this_app_id: int,
) -> List["BoxReference"]:
"""
Translates a list of tuples with app IDs and names into an array of
BoxReferences with foreign indices.
Args:
references (list[(int, bytes)]): list of tuples specifying app id
and key for boxes the app may access
foreign_apps (list[int]): list of other applications in appl call
this_app_id (int): app ID of the box being references
"""
if not references:
return []

return [
BoxReference.translate_box_reference(
ref, foreign_apps, this_app_id
)
for ref in references
]

def dictify(self):
d = dict()
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):
return BoxReference(
d["i"] if "i" in d else 0,
d["n"] if "n" in d else b"",
)

def __eq__(self, other):
if not isinstance(other, BoxReference):
return False
return self.app_index == other.app_index and self.name == other.name
17 changes: 16 additions & 1 deletion algosdk/encoding.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import base64
import warnings
from collections import OrderedDict
from typing import Union

import msgpack
from Cryptodome.Hash import SHA512

from . import auction, constants, error, future, transaction
from algosdk import auction, constants, error, future, transaction


def msgpack_encode(obj):
Expand Down Expand Up @@ -246,3 +247,17 @@ def checksum(data):
chksum = SHA512.new(truncate="256")
chksum.update(data)
return chksum.digest()


def encode_as_bytes(
e: Union[bytes, bytearray, str, int]
) -> Union[bytes, bytearray]:
"""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
raise TypeError("{} is not bytes, bytearray, str, or int".format(e))
5 changes: 5 additions & 0 deletions algosdk/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,11 @@ def __init__(self, msg):
super().__init__(msg)


class InvalidForeignIndexError(Exception):
def __init__(self, msg):
super().__init__(msg)


class SourceMapVersionError(Exception):
def __init__(self, version):
Exception.__init__(
Expand Down
Loading

0 comments on commit 3b231ac

Please sign in to comment.