From 690f0b270a0d43adfa4bc16251d8dccec809bf82 Mon Sep 17 00:00:00 2001 From: Janez Podhostnik Date: Mon, 20 Feb 2023 02:42:54 +0100 Subject: [PATCH] get account accepts bytes, address or hex --- examples/get_account_examples.py | 52 +++++++++++++++++--------------- flow_py_sdk/cadence/address.py | 31 ++++++++++++++++++- flow_py_sdk/client/client.py | 21 +++++++++---- flow_py_sdk/exceptions.py | 6 ++++ tests/test_address.py | 23 ++++++++++++++ 5 files changed, 101 insertions(+), 32 deletions(-) create mode 100644 tests/test_address.py diff --git a/examples/get_account_examples.py b/examples/get_account_examples.py index 4e8ea70..ebf3730 100644 --- a/examples/get_account_examples.py +++ b/examples/get_account_examples.py @@ -17,7 +17,8 @@ async def run(self, ctx: Config): host=ctx.access_node_host, port=ctx.access_node_port ) as client: account = await client.get_account( - address=ctx.service_account_address.bytes + # pass address as cadence.Address object + address=ctx.service_account_address ) self.log.info(f"Account Address: {account.address.hex()}") self.log.info(f"Account Balance: {account.balance}") @@ -42,7 +43,8 @@ async def run(self, ctx: Config): ) as client: _, _, _ = await random_account(client=client, ctx=ctx) account = await client.get_account_at_latest_block( - address=ctx.service_account_address.bytes + # pass address as hex string + address=ctx.service_account_address.hex() ) self.log.info(f"Account Address: {account.address.hex()}") self.log.info(f"Account Balance: {account.balance}") @@ -53,26 +55,26 @@ async def run(self, ctx: Config): # ------------------------------------------------------------------------- # Get an account by address at the given block height. # ------------------------------------------------------------------------- -# SKIP DUE TO EMULATOR BUG -# class GetAccountAtBlockHeightExample(Example): -# def __init__(self) -> None: -# super().__init__( -# tag="GA.3.", name="GetAccountAtBlockHeightExample", sort_order=903 -# ) -# -# async def run(self, ctx: Config): -# # First Step : Create a client to connect to the flow blockchain -# # flow_client function creates a client using the host and port -# async with flow_client( -# host=ctx.access_node_host, port=ctx.access_node_port -# ) as client: -# latest_block = await client.get_latest_block() -# _, _, _ = await random_account(client=client, ctx=ctx) -# account = await client.get_account_at_block_height( -# address=ctx.service_account_address.bytes, -# block_height=latest_block.height, -# ) -# print("Account Address: {}".format(account.address.hex())) -# print("Account Balance: {}".format(account.balance)) -# print("Account Contracts: {}".format(len(account.contracts))) -# print("Account Keys: {}".format(len(account.keys))) +class GetAccountAtBlockHeightExample(Example): + def __init__(self) -> None: + super().__init__( + tag="GA.3.", name="GetAccountAtBlockHeightExample", sort_order=903 + ) + + async def run(self, ctx: Config): + # First Step : Create a client to connect to the flow blockchain + # flow_client function creates a client using the host and port + async with flow_client( + host=ctx.access_node_host, port=ctx.access_node_port + ) as client: + latest_block = await client.get_latest_block() + _, _, _ = await random_account(client=client, ctx=ctx) + account = await client.get_account_at_block_height( + # pass address as bytes + address=ctx.service_account_address.bytes, + block_height=latest_block.height, + ) + self.log.info(f"Account Address: {account.address.hex()}") + self.log.info(f"Account Balance: {account.balance}") + self.log.info(f"Account Contracts: {len(account.contracts)}") + self.log.info(f"Account Keys: {len(account.keys)}") diff --git a/flow_py_sdk/cadence/address.py b/flow_py_sdk/cadence/address.py index c6987a5..a68eda6 100644 --- a/flow_py_sdk/cadence/address.py +++ b/flow_py_sdk/cadence/address.py @@ -1,6 +1,10 @@ +from __future__ import annotations + from flow_py_sdk.cadence.value import Value import flow_py_sdk.cadence.constants as c +from flow_py_sdk.exceptions import NotAddressError + class Address(Value): address_length = 8 @@ -35,9 +39,34 @@ def encode_value(self) -> dict: def decode(cls, value) -> "Address": addr = str(value[c.valueKey]) if addr[:2] != "0x": - raise Exception() # TODO + raise NotAddressError.from_value(addr) # TODO return Address.from_hex(addr) @classmethod def type_str(cls) -> str: return c.addressTypeStr + + @classmethod + def convert_to_bytes(cls, address: bytes | Address | str) -> bytes: + """ + Converts an address to bytes if it is not already bytes. + Parameters + ---------- + address : bytes | Address | str + The address to convert to bytes + Can be bytes, Address, or hex string + + Returns + ------- + bytes + The address as bytes + + """ + if isinstance(address, bytes): + return address + elif isinstance(address, Address): + return address.bytes + elif isinstance(address, str): + return Address.from_hex(address).bytes + else: + raise NotAddressError.from_value(address) diff --git a/flow_py_sdk/client/client.py b/flow_py_sdk/client/client.py index 36cf16e..4059cde 100644 --- a/flow_py_sdk/client/client.py +++ b/flow_py_sdk/client/client.py @@ -10,6 +10,7 @@ from grpclib.encoding.base import CodecBase, StatusDetailsCodecBase from grpclib.metadata import Deadline +from flow_py_sdk import cadence from flow_py_sdk.cadence import Value, cadence_object_hook, encode_arguments from flow_py_sdk.client import entities from flow_py_sdk.proto.flow.access import ( @@ -194,14 +195,17 @@ async def get_transaction(self, *, id: bytes = b"") -> entities.Transaction: response = await super().get_transaction(id=id) return entities.Transaction.from_proto(response.transaction) - async def get_account(self, *, address: bytes = b"") -> entities.Account: + async def get_account( + self, *, address: bytes | cadence.Address | str = b"" + ) -> entities.Account: """ Get an account using its address. Parameters ---------- - address : bytes + address : bytes | cadence.Address | str Address of requested account. + Can be a bytes, cadence.Address or hex str. Returns ------- @@ -209,19 +213,21 @@ async def get_account(self, *, address: bytes = b"") -> entities.Account: Return requested account. """ + address = cadence.Address.convert_to_bytes(address) response = await super().get_account(address=address) return entities.Account.from_proto(response.account) async def get_account_at_latest_block( - self, *, address: bytes = b"" + self, *, address: bytes | cadence.Address | str = b"" ) -> entities.Account: """ Get an account by address at the latest sealed block. Parameters ---------- - address : bytes + address : bytes | cadence.Address | str Address of requested account. + Can be a bytes, cadence.Address or hex str. Returns ------- @@ -229,19 +235,21 @@ async def get_account_at_latest_block( Return requested account. """ + address = cadence.Address.convert_to_bytes(address) response = await super().get_account_at_latest_block(address=address) return entities.Account.from_proto(response.account) async def get_account_at_block_height( - self, *, address: bytes = b"", block_height: int = 0 + self, *, address: bytes | cadence.Address | str = b"", block_height: int = 0 ) -> entities.Account: """ Get an account by address at the given block height. Parameters ---------- - address : bytes + address : bytes | cadence.Address | str Address of requested account. + Can be a bytes, cadence.Address or hex str. block_height : int Desired block height. @@ -251,6 +259,7 @@ async def get_account_at_block_height( Return requested account. """ + address = cadence.Address.convert_to_bytes(address) response = await super().get_account_at_block_height( address=address, block_height=block_height ) diff --git a/flow_py_sdk/exceptions.py b/flow_py_sdk/exceptions.py index fb2bbd2..0896c48 100644 --- a/flow_py_sdk/exceptions.py +++ b/flow_py_sdk/exceptions.py @@ -24,3 +24,9 @@ class CadenceEncodingError(PySDKError): class CadenceIncorrectTypeError(PySDKError): pass + + +class NotAddressError(PySDKError): + @classmethod + def from_value(cls, value) -> "NotAddressError": + return NotAddressError(f"Value {value} is not a cadence address.") diff --git a/tests/test_address.py b/tests/test_address.py new file mode 100644 index 0000000..42c49ae --- /dev/null +++ b/tests/test_address.py @@ -0,0 +1,23 @@ +from unittest import TestCase + +from flow_py_sdk.cadence import Address +from flow_py_sdk.exceptions import NotAddressError + + +class TestAddress(TestCase): + def test_convert_to_bytes(self): + with self.subTest(msg="Pass address as bytes"): + address = Address.from_hex("0x01") + self.assertEqual(address.bytes, Address.convert_to_bytes(address.bytes)) + + with self.subTest(msg="Pass address as Address"): + address = Address.from_hex("0x01") + self.assertEqual(address.bytes, Address.convert_to_bytes(address)) + + with self.subTest(msg="Pass address as hex string"): + address = Address.from_hex("0x01") + self.assertEqual(address.bytes, Address.convert_to_bytes(address.hex())) + + with self.subTest(msg="Pass address as unknown"): + with self.assertRaises(NotAddressError): + Address.convert_to_bytes(1)