Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FOR REVIEW ONLY: py-algorand-sdk v2.6.1 #546

Merged
merged 6 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .test-env
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Configs for testing repo download:
SDK_TESTING_URL="https://github.com/algorand/algorand-sdk-testing"
SDK_TESTING_BRANCH="V2"
SDK_TESTING_BRANCH="master"
SDK_TESTING_HARNESS="test-harness"

INSTALL_ONLY=0
Expand Down
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

# v2.6.1

<!-- Release notes generated using configuration in .github/release.yml at release/v2.6.1 -->

## What's Changed
### Bugfixes
* algod: Even in the face of urllib.error.HTTPError, return the json by @jannotti in https://github.com/algorand/py-algorand-sdk/pull/529
* Fix: Pass args to underlying `kmd_request` function, including timeout by @jasonpaulos in https://github.com/algorand/py-algorand-sdk/pull/545


**Full Changelog**: https://github.com/algorand/py-algorand-sdk/compare/v2.6.0...v2.6.1

# v2.6.0

<!-- Release notes generated using configuration in .github/release.yml at release/v2.6.0 -->
Expand Down
3 changes: 2 additions & 1 deletion algosdk/error.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,10 @@ def __init__(self, msg):


class AlgodHTTPError(Exception):
def __init__(self, msg, code=None):
def __init__(self, msg, code=None, data=None):
super().__init__(msg)
self.code = code
self.data = data


class AlgodResponseError(Exception):
Expand Down
100 changes: 60 additions & 40 deletions algosdk/kmd.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import base64
import json
from typing import Any
import urllib.error
from urllib import parse
from urllib.request import Request, urlopen
Expand Down Expand Up @@ -66,32 +67,37 @@ def kmd_request(self, method, requrl, params=None, data=None, timeout=30):
raise error.KMDHTTPError(e)
return json.loads(resp.read().decode("utf-8"))

def versions(self):
def versions(self, **kwargs: Any):
"""
Get kmd versions.

Returns:
str[]: list of versions
"""
req = "/versions"
return self.kmd_request("GET", req)["versions"]
return self.kmd_request("GET", req, **kwargs)["versions"]

def list_wallets(self):
def list_wallets(self, **kwargs: Any):
"""
List all wallets hosted on node.

Returns:
dict[]: list of dictionaries containing wallet information
"""
req = "/wallets"
res = self.kmd_request("GET", req)
res = self.kmd_request("GET", req, **kwargs)
if "wallets" in res:
return res["wallets"]
else:
return []

def create_wallet(
self, name, pswd, driver_name="sqlite", master_deriv_key=None
self,
name,
pswd,
driver_name="sqlite",
master_deriv_key=None,
**kwargs: Any
):
"""
Create a new wallet.
Expand All @@ -113,9 +119,9 @@ def create_wallet(
}
if master_deriv_key:
query["master_derivation_key"] = master_deriv_key
return self.kmd_request("POST", req, data=query)["wallet"]
return self.kmd_request("POST", req, data=query, **kwargs)["wallet"]

def get_wallet(self, handle):
def get_wallet(self, handle, **kwargs: Any):
"""
Get wallet information.

Expand All @@ -127,9 +133,11 @@ def get_wallet(self, handle):
"""
req = "/wallet/info"
query = {"wallet_handle_token": handle}
return self.kmd_request("POST", req, data=query)["wallet_handle"]
return self.kmd_request("POST", req, data=query, **kwargs)[
"wallet_handle"
]

def init_wallet_handle(self, id, password):
def init_wallet_handle(self, id, password, **kwargs: Any):
"""
Initialize a handle for the wallet.

Expand All @@ -142,9 +150,11 @@ def init_wallet_handle(self, id, password):
"""
req = "/wallet/init"
query = {"wallet_id": id, "wallet_password": password}
return self.kmd_request("POST", req, data=query)["wallet_handle_token"]
return self.kmd_request("POST", req, data=query, **kwargs)[
"wallet_handle_token"
]

def release_wallet_handle(self, handle):
def release_wallet_handle(self, handle, **kwargs: Any):
"""
Deactivate the handle for the wallet.

Expand All @@ -156,10 +166,10 @@ def release_wallet_handle(self, handle):
"""
req = "/wallet/release"
query = {"wallet_handle_token": handle}
result = self.kmd_request("POST", req, data=query)
result = self.kmd_request("POST", req, data=query, **kwargs)
return result == {}

def renew_wallet_handle(self, handle):
def renew_wallet_handle(self, handle, **kwargs: Any):
"""
Renew the wallet handle.

Expand All @@ -171,9 +181,11 @@ def renew_wallet_handle(self, handle):
"""
req = "/wallet/renew"
query = {"wallet_handle_token": handle}
return self.kmd_request("POST", req, data=query)["wallet_handle"]
return self.kmd_request("POST", req, data=query, **kwargs)[
"wallet_handle"
]

def rename_wallet(self, id, password, new_name):
def rename_wallet(self, id, password, new_name, **kwargs: Any):
"""
Rename the wallet.

Expand All @@ -191,9 +203,9 @@ def rename_wallet(self, id, password, new_name):
"wallet_password": password,
"wallet_name": new_name,
}
return self.kmd_request("POST", req, data=query)["wallet"]
return self.kmd_request("POST", req, data=query, **kwargs)["wallet"]

def export_master_derivation_key(self, handle, password):
def export_master_derivation_key(self, handle, password, **kwargs: Any):
"""
Get the wallet's master derivation key.

Expand All @@ -206,10 +218,10 @@ def export_master_derivation_key(self, handle, password):
"""
req = "/master-key/export"
query = {"wallet_handle_token": handle, "wallet_password": password}
result = self.kmd_request("POST", req, data=query)
result = self.kmd_request("POST", req, data=query, **kwargs)
return result["master_derivation_key"]

def import_key(self, handle, private_key):
def import_key(self, handle, private_key, **kwargs: Any):
"""
Import an account into the wallet.

Expand All @@ -222,9 +234,9 @@ def import_key(self, handle, private_key):
"""
req = "/key/import"
query = {"wallet_handle_token": handle, "private_key": private_key}
return self.kmd_request("POST", req, data=query)["address"]
return self.kmd_request("POST", req, data=query, **kwargs)["address"]

def export_key(self, handle, password, address):
def export_key(self, handle, password, address, **kwargs: Any):
"""
Return an account private key.

Expand All @@ -242,9 +254,11 @@ def export_key(self, handle, password, address):
"wallet_password": password,
"address": address,
}
return self.kmd_request("POST", req, data=query)["private_key"]
return self.kmd_request("POST", req, data=query, **kwargs)[
"private_key"
]

def generate_key(self, handle, display_mnemonic=True):
def generate_key(self, handle, display_mnemonic=True, **kwargs: Any):
"""
Generate a key in the wallet.

Expand All @@ -258,9 +272,9 @@ def generate_key(self, handle, display_mnemonic=True):
"""
req = "/key"
query = {"wallet_handle_token": handle}
return self.kmd_request("POST", req, data=query)["address"]
return self.kmd_request("POST", req, data=query, **kwargs)["address"]

def delete_key(self, handle, password, address):
def delete_key(self, handle, password, address, **kwargs: Any):
"""
Delete a key in the wallet.

Expand All @@ -278,10 +292,10 @@ def delete_key(self, handle, password, address):
"wallet_password": password,
"address": address,
}
result = self.kmd_request("DELETE", req, data=query)
result = self.kmd_request("DELETE", req, data=query, **kwargs)
return result == {}

def list_keys(self, handle):
def list_keys(self, handle, **kwargs: Any):
"""
List all keys in the wallet.

Expand All @@ -294,12 +308,14 @@ def list_keys(self, handle):
req = "/key/list"
query = {"wallet_handle_token": handle}

result = self.kmd_request("POST", req, data=query)
result = self.kmd_request("POST", req, data=query, **kwargs)
if result:
return result["addresses"]
return []

def sign_transaction(self, handle, password, txn, signing_address=None):
def sign_transaction(
self, handle, password, txn, signing_address=None, **kwargs: Any
):
"""
Sign a transaction.

Expand All @@ -322,11 +338,11 @@ def sign_transaction(self, handle, password, txn, signing_address=None):
}
if signing_address:
query["public_key"] = signing_address
result = self.kmd_request("POST", req, data=query)
result = self.kmd_request("POST", req, data=query, **kwargs)
result = result["signed_transaction"]
return encoding.msgpack_decode(result)

def list_multisig(self, handle):
def list_multisig(self, handle, **kwargs: Any):
"""
List all multisig accounts in the wallet.

Expand All @@ -338,12 +354,12 @@ def list_multisig(self, handle):
"""
req = "/multisig/list"
query = {"wallet_handle_token": handle}
result = self.kmd_request("POST", req, data=query)
result = self.kmd_request("POST", req, data=query, **kwargs)
if result == {}:
return []
return result["addresses"]

def import_multisig(self, handle, multisig):
def import_multisig(self, handle, multisig, **kwargs: Any):
"""
Import a multisig account into the wallet.

Expand All @@ -364,9 +380,9 @@ def import_multisig(self, handle, multisig):
for s in multisig.subsigs
],
}
return self.kmd_request("POST", req, data=query)["address"]
return self.kmd_request("POST", req, data=query, **kwargs)["address"]

def export_multisig(self, handle, address):
def export_multisig(self, handle, address, **kwargs: Any):
"""
Export a multisig account.

Expand All @@ -379,15 +395,15 @@ def export_multisig(self, handle, address):
"""
req = "/multisig/export"
query = {"wallet_handle_token": handle, "address": address}
result = self.kmd_request("POST", req, data=query)
result = self.kmd_request("POST", req, data=query, **kwargs)
pks = result["pks"]
pks = [encoding.encode_address(base64.b64decode(p)) for p in pks]
msig = transaction.Multisig(
result["multisig_version"], result["threshold"], pks
)
return msig

def delete_multisig(self, handle, password, address):
def delete_multisig(self, handle, password, address, **kwargs: Any):
"""
Delete a multisig account.

Expand All @@ -405,10 +421,12 @@ def delete_multisig(self, handle, password, address):
"wallet_password": password,
"address": address,
}
result = self.kmd_request("DELETE", req, data=query)
result = self.kmd_request("DELETE", req, data=query, **kwargs)
return result == {}

def sign_multisig_transaction(self, handle, password, public_key, mtx):
def sign_multisig_transaction(
self, handle, password, public_key, mtx, **kwargs: Any
):
"""
Sign a multisig transaction for the given public key.

Expand Down Expand Up @@ -439,7 +457,9 @@ def sign_multisig_transaction(self, handle, password, public_key, mtx):
signer = base64.b64encode(encoding.decode_address(mtx.auth_addr))
query["signer"] = signer.decode()

result = self.kmd_request("POST", req, data=query)["multisig"]
result = self.kmd_request("POST", req, data=query, **kwargs)[
"multisig"
]
msig = encoding.msgpack_decode(result)
mtx.multisig = msig
return mtx
7 changes: 5 additions & 2 deletions algosdk/v2client/algod.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,13 @@ def algod_request(
except urllib.error.HTTPError as e:
code = e.code
es = e.read().decode("utf-8")
m = e # If json.loads() fails, we'll return e itself
j = {}
try:
e = json.loads(es)["message"]
j = json.loads(es)
m = j["message"]
finally:
raise error.AlgodHTTPError(e, code)
raise error.AlgodHTTPError(m, code, j.get("data"))
if response_format == "json":
try:
return json.load(resp)
Expand Down
20 changes: 20 additions & 0 deletions examples/inspect-error.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from algosdk import error, transaction

from utils import get_algod_client, algod_env

algod = get_algod_client(*algod_env())

# This program is "#pragma version 5, +". It will fail because no arguments are on the stack.
lsig = transaction.LogicSigAccount(b"\x05\x08")
sender = lsig.address()
# Get suggested parameters
params = algod.suggested_params()

amount = 10000
txn = transaction.PaymentTxn(sender, params, sender, amount)
lstx = transaction.LogicSigTransaction(txn, lsig)
try:
txid = algod.send_transaction(lstx)
print("Impossible! Exception will have been thrown")
except error.AlgodHTTPError as e:
print(e.data)
17 changes: 17 additions & 0 deletions examples/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,23 @@ class SandboxAccount:
signer: AccountTransactionSigner


def algod_env():
algodata = os.environ.get("ALGORAND_DATA")
if not algodata:
return ()
try:
token = (
open(os.path.join(algodata, "algod.token"), "rt").read().strip()
)
net = (
"http://"
+ open(os.path.join(algodata, "algod.net"), "rt").read().strip()
)
return (net, token)
except FileNotFoundError:
return ()


def get_accounts(
kmd_address: str = KMD_URL,
kmd_token: str = KMD_TOKEN,
Expand Down
Loading
Loading