Skip to content

Commit

Permalink
Merge branch 'release/v2.6.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
Algo-devops-service committed Jun 6, 2024
2 parents bb8d4a3 + ad1e0bf commit 8e6a7f6
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 45 deletions.
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

0 comments on commit 8e6a7f6

Please sign in to comment.