diff --git a/pyhmy/__init__.py b/pyhmy/__init__.py index 5aefb14..ebdebee 100644 --- a/pyhmy/__init__.py +++ b/pyhmy/__init__.py @@ -3,28 +3,16 @@ import warnings from ._version import __version__ -from .util import ( - Typgpy, - get_gopath, - get_goversion, - get_bls_build_variables, - json_load -) +from .util import Typgpy, get_gopath, get_goversion, get_bls_build_variables, json_load if sys.version_info.major < 3: warnings.simplefilter("always", DeprecationWarning) warnings.warn( - DeprecationWarning( - "`pyhmy` does not support Python 2. Please use Python 3." - ) + DeprecationWarning("`pyhmy` does not support Python 2. Please use Python 3.") ) warnings.resetwarnings() -if sys.platform.startswith('win32') or sys.platform.startswith('cygwin'): +if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"): warnings.simplefilter("always", ImportWarning) - warnings.warn( - ImportWarning( - "`pyhmy` does not work on Windows or Cygwin." - ) - ) + warnings.warn(ImportWarning("`pyhmy` does not work on Windows or Cygwin.")) warnings.resetwarnings() diff --git a/pyhmy/_version.py b/pyhmy/_version.py index 09a940e..dc1891a 100644 --- a/pyhmy/_version.py +++ b/pyhmy/_version.py @@ -7,5 +7,5 @@ Provides pyhmy version information. from incremental import Version -__version__ = Version('pyhmy', 20, 5, 20) +__version__ = Version("pyhmy", 20, 5, 20) __all__ = ["__version__"] diff --git a/pyhmy/account.py b/pyhmy/account.py index 60c0494..5c53b3b 100644 --- a/pyhmy/account.py +++ b/pyhmy/account.py @@ -1,26 +1,14 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .rpc.exceptions import ( - RPCError, - RequestsError, - RequestsTimeoutError -) +from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -from .blockchain import ( - get_sharding_structure -) +from .blockchain import get_sharding_structure -from .bech32.bech32 import ( - bech32_decode -) +from .bech32.bech32 import bech32_decode -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 _address_length = 42 @@ -40,13 +28,14 @@ def is_valid_address(address) -> bool: bool Is valid address """ - if not address.startswith('one1'): + if not address.startswith("one1"): return False hrp, _ = bech32_decode(address) if not hrp: return False return True + def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current account balance @@ -74,17 +63,20 @@ def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) - ------------- https://api.hmny.io/#da8901d2-d237-4c3b-9d7d-10af9def05c4 """ - method = 'hmyv2_getBalance' - params = [ - address - ] + method = "hmyv2_getBalance" + params = [address] try: - balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] - return int(balance) # v2 returns the result as it is - except TypeError as e: # check will work if rpc returns None + balance = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] + return int(balance) # v2 returns the result as it is + except TypeError as e: # check will work if rpc returns None raise InvalidRPCReplyError(method, endpoint) from e -def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_balance_by_block( + address, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get account balance for address at a given block number @@ -114,18 +106,20 @@ def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout https://api.hmny.io/#9aeae4b8-1a09-4ed2-956b-d7c96266dd33 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92 """ - method = 'hmyv2_getBalanceByBlockNumber' - params = [ - address, - block_num - ] + method = "hmyv2_getBalanceByBlockNumber" + params = [address, block_num] try: - balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + balance = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] return int(balance) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_account_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_account_nonce( + address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the account nonce @@ -154,24 +148,29 @@ def get_account_nonce(address, block_num='latest', endpoint=_default_endpoint, t ------------- https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L51 """ - method = 'hmyv2_getAccountNonce' - params = [ - address, - block_num - ] + method = "hmyv2_getAccountNonce" + params = [address, block_num] try: - nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] return int(nonce) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_nonce( + address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ See get_account_nonce """ return get_account_nonce(address, block_num, endpoint, timeout) -def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_transaction_count( + address, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the number of transactions the given address has sent for the given block number Legacy for apiv1. For apiv2, please use get_account_nonce/get_transactions_count/get_staking_transactions_count apis for @@ -202,18 +201,20 @@ def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeou ------------- https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L69 """ - method = 'hmyv2_getTransactionCount' - params = [ - address, - block_num - ] + method = "hmyv2_getTransactionCount" + params = [address, block_num] try: - nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] return int(nonce) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_transactions_count( + address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the number of regular transactions from genesis of input type @@ -244,18 +245,20 @@ def get_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout https://api.hmny.io/#fc97aed2-e65e-4cf4-bc01-8dadb76732c0 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114 """ - method = 'hmyv2_getTransactionsCount' - params = [ - address, - tx_type - ] + method = "hmyv2_getTransactionsCount" + params = [address, tx_type] try: - tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + tx_count = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] return int(tx_count) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_staking_transactions_count( + address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL") @@ -286,20 +289,27 @@ def get_staking_transactions_count(address, tx_type, endpoint=_default_endpoint, https://api.hmny.io/#ddc1b029-f341-4c4d-ba19-74b528d6e5e5 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134 """ - method = 'hmyv2_getStakingTransactionsCount' - params = [ - address, - tx_type - ] + method = "hmyv2_getStakingTransactionsCount" + params = [address, tx_type] try: - tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + tx_count = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] return int(tx_count) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL', - order='ASC', endpoint=_default_endpoint, timeout=_default_timeout - ) -> list: + +def get_transaction_history( + address, + page=0, + page_size=1000, + include_full_tx=False, + tx_type="ALL", + order="ASC", + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> list: """ Get list of transactions sent and/or received by the account @@ -345,24 +355,34 @@ def get_transaction_history(address, page=0, page_size=1000, include_full_tx=Fal """ params = [ { - 'address': address, - 'pageIndex': page, - 'pageSize': page_size, - 'fullTx': include_full_tx, - 'txType': tx_type, - 'order': order + "address": address, + "pageIndex": page, + "pageSize": page_size, + "fullTx": include_full_tx, + "txType": tx_type, + "order": order, } ] - method = 'hmyv2_getTransactionsHistory' + method = "hmyv2_getTransactionsHistory" try: - tx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout) - return tx_history['result']['transactions'] + tx_history = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + ) + return tx_history["result"]["transactions"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL', - order='ASC', endpoint=_default_endpoint, timeout=_default_timeout - ) -> list: + +def get_staking_transaction_history( + address, + page=0, + page_size=1000, + include_full_tx=False, + tx_type="ALL", + order="ASC", + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> list: """ Get list of staking transactions sent by the account @@ -419,23 +439,28 @@ def get_staking_transaction_history(address, page=0, page_size=1000, include_ful """ params = [ { - 'address': address, - 'pageIndex': page, - 'pageSize': page_size, - 'fullTx': include_full_tx, - 'txType': tx_type, - 'order': order + "address": address, + "pageIndex": page, + "pageSize": page_size, + "fullTx": include_full_tx, + "txType": tx_type, + "order": order, } ] # Using v2 API, because getStakingTransactionHistory not implemented in v1 - method = 'hmyv2_getStakingTransactionsHistory' + method = "hmyv2_getStakingTransactionsHistory" try: - stx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] - return stx_history['staking_transactions'] + stx_history = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] + return stx_history["staking_transactions"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_balance_on_all_shards(address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_balance_on_all_shards( + address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get current account balance in all shards & optionally report errors getting account balance for a shard @@ -467,19 +492,23 @@ def get_balance_on_all_shards(address, skip_error=True, endpoint=_default_endpoi sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout) for shard in sharding_structure: try: - balances.append({ - 'shard': shard['shardID'], - 'balance': get_balance(address, endpoint=shard['http'], timeout=timeout) - }) + balances.append( + { + "shard": shard["shardID"], + "balance": get_balance( + address, endpoint=shard["http"], timeout=timeout + ), + } + ) except (KeyError, RPCError, RequestsError, RequestsTimeoutError): if not skip_error: - balances.append({ - 'shard': shard['shardID'], - 'balance': None - }) + balances.append({"shard": shard["shardID"], "balance": None}) return balances -def get_total_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_total_balance( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get total account balance on all shards @@ -507,7 +536,9 @@ def get_total_balance(address, endpoint=_default_endpoint, timeout=_default_time get_balance_on_all_shards """ try: - balances = get_balance_on_all_shards(address, skip_error=False, endpoint=endpoint, timeout=timeout) - return sum(b['balance'] for b in balances) + balances = get_balance_on_all_shards( + address, skip_error=False, endpoint=endpoint, timeout=timeout + ) + return sum(b["balance"] for b in balances) except TypeError as e: raise RuntimeError from e diff --git a/pyhmy/bech32/bech32.py b/pyhmy/bech32/bech32.py index d450080..212705d 100644 --- a/pyhmy/bech32/bech32.py +++ b/pyhmy/bech32/bech32.py @@ -26,11 +26,11 @@ CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" def bech32_polymod(values): """Internal function that computes the Bech32 checksum.""" - generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] + generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3] chk = 1 for value in values: top = chk >> 25 - chk = (chk & 0x1ffffff) << 5 ^ value + chk = (chk & 0x1FFFFFF) << 5 ^ value for i in range(5): chk ^= generator[i] if ((top >> i) & 1) else 0 return chk @@ -56,22 +56,23 @@ def bech32_create_checksum(hrp, data): def bech32_encode(hrp, data): """Compute a Bech32 string given HRP and data values.""" combined = data + bech32_create_checksum(hrp, data) - return hrp + '1' + ''.join([CHARSET[d] for d in combined]) + return hrp + "1" + "".join([CHARSET[d] for d in combined]) def bech32_decode(bech): """Validate a Bech32 string, and determine HRP and data.""" - if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or - (bech.lower() != bech and bech.upper() != bech)): + if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or ( + bech.lower() != bech and bech.upper() != bech + ): return (None, None) bech = bech.lower() - pos = bech.rfind('1') + pos = bech.rfind("1") if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: return (None, None) - if not all(x in CHARSET for x in bech[pos+1:]): + if not all(x in CHARSET for x in bech[pos + 1 :]): return (None, None) hrp = bech[:pos] - data = [CHARSET.find(x) for x in bech[pos+1:]] + data = [CHARSET.find(x) for x in bech[pos + 1 :]] if not bech32_verify_checksum(hrp, data): return (None, None) return (hrp, data[:-6]) diff --git a/pyhmy/blockchain.py b/pyhmy/blockchain.py index 50bb3b3..9962c14 100644 --- a/pyhmy/blockchain.py +++ b/pyhmy/blockchain.py @@ -1,12 +1,8 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ############################# @@ -37,12 +33,13 @@ def get_bad_blocks(endpoint=_default_endpoint, timeout=_default_timeout) -> list ------------- https://api.hmny.io/#0ba3c7b6-6aa9-46b8-9c84-f8782e935951 """ - method = 'hmyv2_getCurrentBadBlocks' + method = "hmyv2_getCurrentBadBlocks" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Chain id of the chain @@ -67,13 +64,14 @@ def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: ------------- https://github.com/harmony-one/harmony/blob/343dbe89b3c105f8104ab877769070ba6fdd0133/rpc/blockchain.go#L44 """ - method = 'hmyv2_chainId' + method = "hmyv2_chainId" try: data = rpc_request(method, endpoint=endpoint, timeout=timeout) - return data['result'] + return data["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get config for the node @@ -143,13 +141,14 @@ def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> d https://github.com/harmony-one/harmony/blob/v1.10.2/internal/params/config.go#L233 for chain-config dict https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/node/api.go#L110 for consensus dict """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: metadata = rpc_request(method, endpoint=endpoint, timeout=timeout) - return metadata['result'] + return metadata["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get peer info for the node @@ -181,12 +180,13 @@ def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: -------- get_node_metadata """ - method = 'hmyv2_getPeerInfo' + method = "hmyv2_getPeerInfo" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the current Harmony protocol version this node supports @@ -212,13 +212,14 @@ def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> in ------------- https://api.hmny.io/#cab9fcc2-e3cd-4bc9-b62a-13e4e046e2fd """ - method = 'hmyv2_protocolVersion' + method = "hmyv2_protocolVersion" try: value = rpc_request(method, endpoint=endpoint, timeout=timeout) - return value['result'] + return value["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get number of peers connected to the node @@ -244,12 +245,15 @@ def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int: ------------- https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1 """ - method = 'net_peerCount' - try: # Number of peers represented as a hex string - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'], 16) + method = "net_peerCount" + try: # Number of peers represented as a hex string + return int( + rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get version of the EVM network (https://chainid.network/) @@ -275,12 +279,15 @@ def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: ------------- https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1 """ - method = 'net_version' + method = "net_version" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'], 16) # this is hexadecimal + return int( + rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 + ) # this is hexadecimal except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: """ Whether the shard chain is in sync or syncing (not out of sync) @@ -305,12 +312,13 @@ def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L690 """ - method = 'hmyv2_inSync' + method = "hmyv2_inSync" try: - return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: """ Whether the beacon chain is in sync or syncing (not out of sync) @@ -335,12 +343,13 @@ def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L695 """ - method = 'hmyv2_beaconInSync' + method = "hmyv2_beaconInSync" try: - return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get epoch number when blockchain switches to EPoS election @@ -370,13 +379,14 @@ def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i ------ get_node_metadata """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: - data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] - return int(data['chain-config']['staking-epoch']) + data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return int(data["chain-config"]["staking-epoch"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get epoch number when blockchain switches to allow staking features without election @@ -406,13 +416,14 @@ def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) - ------ get_node_metadata """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: - data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] - return int(data['chain-config']['prestaking-epoch']) + data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return int(data["chain-config"]["prestaking-epoch"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + ######################## # Sharding information # ######################## @@ -441,13 +452,18 @@ def get_shard(endpoint=_default_endpoint, timeout=_default_timeout) -> int: -------- get_node_metadata """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']['shard-id'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"][ + "shard-id" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_sharding_structure(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_sharding_structure( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get network sharding structure @@ -475,12 +491,13 @@ def get_sharding_structure(endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://api.hmny.io/#9669d49e-43c1-47d9-a3fd-e7786e5879df """ - method = 'hmyv2_getShardingStructure' + method = "hmyv2_getShardingStructure" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ############################# # Current status of network # ############################# @@ -509,13 +526,16 @@ def get_leader_address(endpoint=_default_endpoint, timeout=_default_timeout) -> ------------- https://api.hmny.io/#8b08d18c-017b-4b44-a3c3-356f9c12dacd """ - method = 'hmyv2_getLeader' + method = "hmyv2_getLeader" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def is_last_block(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def is_last_block( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ If the block at block_num is the last block @@ -544,13 +564,20 @@ def is_last_block(block_num, endpoint=_default_endpoint, timeout=_default_timeou params = [ block_num, ] - method = 'hmyv2_isLastBlock' + method = "hmyv2_isLastBlock" try: - return bool(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return bool( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def epoch_last_block(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def epoch_last_block( + epoch, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Returns the number of the last block in the epoch @@ -579,12 +606,17 @@ def epoch_last_block(epoch, endpoint=_default_endpoint, timeout=_default_timeout params = [ epoch, ] - method = 'hmyv2_epochLastBlock' + method = "hmyv2_epochLastBlock" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current circulation supply of tokens in ONE @@ -610,12 +642,13 @@ def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://api.hmny.io/#8398e818-ac2d-4ad8-a3b4-a00927395044 """ - method = 'hmyv2_getCirculatingSupply' + method = "hmyv2_getCirculatingSupply" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get total number of pre-mined tokens @@ -641,12 +674,13 @@ def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> in ------------- https://api.hmny.io/#3dcea518-9e9a-4a20-84f4-c7a0817b2196 """ - method = 'hmyv2_getTotalSupply' + method = "hmyv2_getTotalSupply" try: - rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current block number @@ -672,12 +706,13 @@ def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> in ------------- https://api.hmny.io/#2602b6c4-a579-4b7c-bce8-85331e0db1a7 """ - method = 'hmyv2_blockNumber' + method = "hmyv2_blockNumber" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current epoch number @@ -703,12 +738,13 @@ def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i ------------- https://api.hmny.io/#9b8e98b0-46d1-4fa0-aaa6-317ff1ddba59 """ - method = 'hmyv2_getEpoch' + method = "hmyv2_getEpoch" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get last cross shard links @@ -741,12 +777,13 @@ def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) - ------------- https://api.hmny.io/#4994cdf9-38c4-4b1d-90a8-290ddaa3040e """ - method = 'hmyv2_getLastCrossLinks' + method = "hmyv2_getLastCrossLinks" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get network gas price @@ -772,12 +809,13 @@ def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int: ------------- https://api.hmny.io/#1d53fd59-a89f-436c-a171-aec9d9623f48 """ - method = 'hmyv2_gasPrice' + method = "hmyv2_gasPrice" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + ############## # Block RPCs # ############## @@ -822,13 +860,16 @@ def get_latest_header(endpoint=_default_endpoint, timeout=_default_timeout) -> d ------------- https://api.hmny.io/#73fc9b97-b048-4b85-8a93-4d2bf1da54a6 """ - method = 'hmyv2_latestHeader' + method = "hmyv2_latestHeader" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_header_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_header_by_number( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get block header of block at block_num @@ -854,16 +895,19 @@ def get_header_by_number(block_num, endpoint=_default_endpoint, timeout=_default ------------- https://api.hmny.io/#01148e4f-72bb-426d-a123-718a161eaec0 """ - method = 'hmyv2_getHeaderByNumber' - params = [ - block_num - ] + method = "hmyv2_getHeaderByNumber" + params = [block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_latest_chain_headers(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_latest_chain_headers( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get block header of latest block for beacon chain & shard chain @@ -908,14 +952,22 @@ def get_latest_chain_headers(endpoint=_default_endpoint, timeout=_default_timeou ------------- https://api.hmny.io/#7625493d-16bf-4611-8009-9635d063b4c0 """ - method = 'hmyv2_getLatestChainHeaders' + method = "hmyv2_getLatestChainHeaders" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_by_number(block_num, full_tx=False, include_tx=False, include_staking_tx=False, - include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_block_by_number( + block_num, + full_tx=False, + include_tx=False, + include_staking_tx=False, + include_signers=False, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> dict: """ Get block by number @@ -979,20 +1031,30 @@ def get_block_by_number(block_num, full_tx=False, include_tx=False, include_stak params = [ block_num, { - 'inclTx': include_tx, - 'fullTx': full_tx, - 'inclStaking': include_staking_tx, - 'withSigners': include_signers, + "inclTx": include_tx, + "fullTx": full_tx, + "inclStaking": include_staking_tx, + "withSigners": include_signers, }, ] - method = 'hmyv2_getBlockByNumber' + method = "hmyv2_getBlockByNumber" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_by_hash(block_hash, full_tx=False, include_tx=False, include_staking_tx=False, - include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_block_by_hash( + block_hash, + full_tx=False, + include_tx=False, + include_staking_tx=False, + include_signers=False, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> dict: """ Get block by hash @@ -1027,19 +1089,24 @@ def get_block_by_hash(block_hash, full_tx=False, include_tx=False, include_staki params = [ block_hash, { - 'inclTx': include_tx, - 'fullTx': full_tx, - 'inclStaking': include_staking_tx, - 'withSigners': include_signers, + "inclTx": include_tx, + "fullTx": full_tx, + "inclStaking": include_staking_tx, + "withSigners": include_signers, }, ] - method = 'hmyv2_getBlockByHash' + method = "hmyv2_getBlockByHash" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_transaction_count_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_transaction_count_by_number( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get transaction count for specific block number @@ -1068,16 +1135,21 @@ def get_block_transaction_count_by_number(block_num, endpoint=_default_endpoint, ------------- https://api.hmny.io/#26c5adfb-d757-4595-9eb7-c6efef63df32 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockTransactionCountByNumber' + params = [block_num] + method = "hmyv2_getBlockTransactionCountByNumber" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_transaction_count_by_hash( + block_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get transaction count for specific block hash @@ -1106,16 +1178,21 @@ def get_block_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, ------------- https://api.hmny.io/#66c68844-0208-49bb-a83b-08722bc113eb """ - params = [ - block_hash - ] - method = 'hmyv2_getBlockTransactionCountByHash' + params = [block_hash] + method = "hmyv2_getBlockTransactionCountByHash" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_staking_transaction_count_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_staking_transaction_count_by_number( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get staking transaction count for specific block number @@ -1144,16 +1221,21 @@ def get_block_staking_transaction_count_by_number(block_num, endpoint=_default_e ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L494 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockStakingTransactionCountByNumber' + params = [block_num] + method = "hmyv2_getBlockStakingTransactionCountByNumber" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_staking_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_staking_transaction_count_by_hash( + block_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get staking transaction count for specific block hash @@ -1182,18 +1264,28 @@ def get_block_staking_transaction_count_by_hash(block_hash, endpoint=_default_en ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L523 """ - params = [ - block_hash - ] - method = 'hmyv2_getBlockStakingTransactionCountByHash' + params = [block_hash] + method = "hmyv2_getBlockStakingTransactionCountByHash" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_blocks(start_block, end_block, full_tx=False, include_tx=False, include_staking_tx=False, - include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout - ) -> list: + +def get_blocks( + start_block, + end_block, + full_tx=False, + include_tx=False, + include_staking_tx=False, + include_signers=False, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> list: """ Get list of blocks from a range @@ -1233,19 +1325,24 @@ def get_blocks(start_block, end_block, full_tx=False, include_tx=False, include_ start_block, end_block, { - 'withSigners': include_signers, - 'fullTx': full_tx, - 'inclStaking': include_staking_tx, - 'inclTx': include_tx + "withSigners": include_signers, + "fullTx": full_tx, + "inclStaking": include_staking_tx, + "inclTx": include_tx, }, ] - method = 'hmyv2_getBlocks' + method = "hmyv2_getBlocks" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_signers(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_block_signers( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of block signers for specific block number @@ -1272,16 +1369,19 @@ def get_block_signers(block_num, endpoint=_default_endpoint, timeout=_default_ti ------------- https://api.hmny.io/#1e4b5f41-9db6-4dea-92fb-4408db78e622 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockSigners' + params = [block_num] + method = "hmyv2_getBlockSigners" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_signers_keys(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_block_signers_keys( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of block signer public bls keys for specific block number @@ -1308,16 +1408,19 @@ def get_block_signers_keys(block_num, endpoint=_default_endpoint, timeout=_defau ------------- https://api.hmny.io/#9f9c8298-1a4e-4901-beac-f34b59ed02f1 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockSignerKeys' + params = [block_num] + method = "hmyv2_getBlockSignerKeys" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def is_block_signer(block_num, address, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def is_block_signer( + block_num, address, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ Determine if the account at address is a signer for the block at block_num @@ -1345,17 +1448,19 @@ def is_block_signer(block_num, address, endpoint=_default_endpoint, timeout=_def ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L368 """ - params = [ - block_num, - address - ] - method = 'hmyv2_isBlockSigner' + params = [block_num, address] + method = "hmyv2_isBlockSigner" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_signed_blocks(address, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def get_signed_blocks( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ The number of blocks a particular validator signed for last blocksPeriod (1 epoch) @@ -1381,15 +1486,18 @@ def get_signed_blocks(address, endpoint=_default_endpoint, timeout=_default_time ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L406 """ - params = [ - address - ] - method = 'hmyv2_getSignedBlocks' + params = [address] + method = "hmyv2_getSignedBlocks" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get list of validators for specific epoch number @@ -1420,16 +1528,19 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://api.hmny.io/#4dfe91ad-71fa-4c7d-83f3-d1c86a804da5 """ - params = [ - epoch - ] - method = 'hmyv2_getValidators' + params = [epoch] + method = "hmyv2_getValidators" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_validator_keys( + epoch, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of validator public bls keys for specific epoch number @@ -1456,11 +1567,11 @@ def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeo ------------- https://api.hmny.io/#1439b580-fa3c-4d44-a79d-303390997a8c """ - params = [ - epoch - ] - method = 'hmyv2_getValidatorKeys' + params = [epoch] + method = "hmyv2_getValidatorKeys" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e diff --git a/pyhmy/cli.py b/pyhmy/cli.py index 060eefa..fd96890 100644 --- a/pyhmy/cli.py +++ b/pyhmy/cli.py @@ -54,9 +54,21 @@ import requests from .util import get_bls_build_variables, get_gopath if sys.platform.startswith("linux"): - _libs = {"libbls384_256.so", "libcrypto.so.10", "libgmp.so.10", "libgmpxx.so.4", "libmcl.so"} + _libs = { + "libbls384_256.so", + "libcrypto.so.10", + "libgmp.so.10", + "libgmpxx.so.4", + "libmcl.so", + } else: - _libs = {"libbls384_256.dylib", "libcrypto.1.0.0.dylib", "libgmp.10.dylib", "libgmpxx.4.dylib", "libmcl.dylib"} + _libs = { + "libbls384_256.dylib", + "libcrypto.1.0.0.dylib", + "libgmp.10.dylib", + "libgmpxx.4.dylib", + "libmcl.dylib", + } _accounts = {} # Internal accounts keystore, make sure to sync when needed. _account_keystore_path = "~/.hmy/account-keys" # Internal path to account keystore, will match the current binary. _binary_path = "hmy" # Internal binary path. @@ -149,14 +161,16 @@ def _make_call_command(command): if isinstance(command, list): command_toks = command else: - all_strings = sorted(re.findall(r'"(.*?)"', command), key=lambda e: len(e), reverse=True) + all_strings = sorted( + re.findall(r'"(.*?)"', command), key=lambda e: len(e), reverse=True + ) for i, string in enumerate(all_strings): command = command.replace(string, f"{_arg_prefix}_{i}") command_toks_prefix = [el for el in command.split(" ") if el] command_toks = [] for el in command_toks_prefix: if el.startswith(f'"{_arg_prefix}_') and el.endswith(f'"'): - index = int(el.replace(f'"{_arg_prefix}_', '').replace('"', '')) + index = int(el.replace(f'"{_arg_prefix}_', "").replace('"', "")) command_toks.append(all_strings[index]) else: command_toks.append(el) @@ -183,8 +197,12 @@ def is_valid_binary(path): path = os.path.realpath(path) os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) try: - proc = subprocess.Popen([path, "version"], env=environment, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + [path, "version"], + env=environment, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out, err = proc.communicate() if not err: return False @@ -223,12 +241,18 @@ def get_version(): """ :return: The version string of the CLI binary. """ - proc = subprocess.Popen([_binary_path, "version"], env=environment, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + [_binary_path, "version"], + env=environment, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out, err = proc.communicate() if not err: - raise RuntimeError(f"Could not get version.\n" - f"\tGot exit code {proc.returncode}. Expected non-empty error message.") + raise RuntimeError( + f"Could not get version.\n" + f"\tGot exit code {proc.returncode}. Expected non-empty error message." + ) return err.decode().strip() @@ -280,8 +304,9 @@ def remove_account(name): try: shutil.rmtree(keystore_path) except (shutil.Error, FileNotFoundError) as err: - raise RuntimeError(f"Failed to delete dir: {keystore_path}\n" - f"\tException: {err}") from err + raise RuntimeError( + f"Failed to delete dir: {keystore_path}\n" f"\tException: {err}" + ) from err _sync_accounts() @@ -304,11 +329,14 @@ def single_call(command, timeout=60, error_ok=False): """ command_toks = [_binary_path] + _make_call_command(command) try: - return subprocess.check_output(command_toks, env=environment, timeout=timeout).decode() + return subprocess.check_output( + command_toks, env=environment, timeout=timeout + ).decode() except subprocess.CalledProcessError as err: if not error_ok: - raise RuntimeError(f"Bad CLI args: `{command}`\n " - f"\tException: {err}") from err + raise RuntimeError( + f"Bad CLI args: `{command}`\n " f"\tException: {err}" + ) from err return err.output.decode() @@ -321,11 +349,14 @@ def expect_call(command, timeout=60): """ command_toks = _make_call_command(command) try: - proc = pexpect.spawn(f"{_binary_path}", command_toks, env=environment, timeout=timeout) + proc = pexpect.spawn( + f"{_binary_path}", command_toks, env=environment, timeout=timeout + ) proc.delaybeforesend = None except pexpect.ExceptionPexpect as err: - raise RuntimeError(f"Bad CLI args: `{command}`\n " - f"\tException: {err}") from err + raise RuntimeError( + f"Bad CLI args: `{command}`\n " f"\tException: {err}" + ) from err return proc @@ -341,28 +372,43 @@ def download(path="./bin/hmy", replace=True, verbose=True): """ path = os.path.realpath(path) parent_dir = Path(path).parent - assert not os.path.isdir(path), f"path `{path}` must specify a file, not a directory." + assert not os.path.isdir( + path + ), f"path `{path}` must specify a file, not a directory." if not os.path.exists(path) or replace: old_cwd = os.getcwd() os.makedirs(parent_dir, exist_ok=True) os.chdir(parent_dir) hmy_script_path = os.path.join(parent_dir, "hmy.sh") - with open(hmy_script_path, 'w') as f: - f.write(requests.get("https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh") - .content.decode()) + with open(hmy_script_path, "w") as f: + f.write( + requests.get( + "https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh" + ).content.decode() + ) os.chmod(hmy_script_path, os.stat(hmy_script_path).st_mode | stat.S_IEXEC) same_name_file = False - if os.path.exists(os.path.join(parent_dir, "hmy")) and Path(path).name != "hmy": # Save same name file. + if ( + os.path.exists(os.path.join(parent_dir, "hmy")) and Path(path).name != "hmy" + ): # Save same name file. same_name_file = True - os.rename(os.path.join(parent_dir, "hmy"), os.path.join(parent_dir, ".hmy_tmp")) + os.rename( + os.path.join(parent_dir, "hmy"), os.path.join(parent_dir, ".hmy_tmp") + ) if verbose: - subprocess.call([hmy_script_path, '-d']) + subprocess.call([hmy_script_path, "-d"]) else: - subprocess.call([hmy_script_path, '-d'], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) + subprocess.call( + [hmy_script_path, "-d"], + stdout=open(os.devnull, "w"), + stderr=subprocess.STDOUT, + ) os.rename(os.path.join(parent_dir, "hmy"), path) if same_name_file: - os.rename(os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy")) + os.rename( + os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy") + ) if verbose: print(f"Saved harmony binary to: `{path}`") os.chdir(old_cwd) @@ -373,11 +419,16 @@ def download(path="./bin/hmy", replace=True, verbose=True): files_in_parent_dir = set(os.listdir(parent_dir)) if files_in_parent_dir.intersection(_libs) == _libs: env["DYLD_FALLBACK_LIBRARY_PATH"] = parent_dir - elif os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/bls") \ - and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"): + elif os.path.exists( + f"{get_gopath()}/src/github.com/harmony-one/bls" + ) and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"): env.update(get_bls_build_variables()) else: - raise RuntimeWarning(f"Could not get environment for downloaded hmy CLI at `{path}`") + raise RuntimeWarning( + f"Could not get environment for downloaded hmy CLI at `{path}`" + ) except Exception as e: - raise RuntimeWarning(f"Could not get environment for downloaded hmy CLI at `{path}`") from e + raise RuntimeWarning( + f"Could not get environment for downloaded hmy CLI at `{path}`" + ) from e return env diff --git a/pyhmy/contract.py b/pyhmy/contract.py index 66dee63..abcfa5a 100644 --- a/pyhmy/contract.py +++ b/pyhmy/contract.py @@ -1,25 +1,28 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .transaction import ( - get_transaction_receipt -) +from .transaction import get_transaction_receipt -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ######################### # Smart contract RPCs ######################### -def call(to, block_num, from_address=None, gas=None, gas_price=None, value=None, data=None, - endpoint=_default_endpoint, timeout=_default_timeout) -> str: +def call( + to, + block_num, + from_address=None, + gas=None, + gas_price=None, + value=None, + data=None, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> str: """ Execute a smart contract without saving state @@ -60,23 +63,34 @@ def call(to, block_num, from_address=None, gas=None, gas_price=None, value=None, """ params = [ { - 'to': to, - 'from': from_address, - 'gas': gas, - 'gasPrice': gas_price, - 'value': value, - 'data': data + "to": to, + "from": from_address, + "gas": gas, + "gasPrice": gas_price, + "value": value, + "data": data, }, - block_num + block_num, ] - method = 'hmyv2_call' + method = "hmyv2_call" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def estimate_gas(to, from_address=None, gas=None, gas_price=None, value=None, data=None, - endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def estimate_gas( + to, + from_address=None, + gas=None, + gas_price=None, + value=None, + data=None, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> int: """ Estimate the gas price needed for a smart contract call @@ -113,21 +127,31 @@ def estimate_gas(to, from_address=None, gas=None, gas_price=None, value=None, da ------------- https://api.hmny.io/?version=latest#b9bbfe71-8127-4dda-b26c-ff95c4c22abd """ - params = [ { - 'to': to, - 'from': from_address, - 'gas': gas, - 'gasPrice': gas_price, - 'value': value, - 'data': data - } ] - method = 'hmyv2_estimateGas' + params = [ + { + "to": to, + "from": from_address, + "gas": gas, + "gasPrice": gas_price, + "value": value, + "data": data, + } + ] + method = "hmyv2_estimateGas" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'], 16) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ], + 16, + ) except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_code(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def get_code( + address, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Get the code stored at the given address in the state for the given block number @@ -157,17 +181,19 @@ def get_code(address, block_num, endpoint=_default_endpoint, timeout=_default_ti https://api.hmny.io/?version=latest#e13e9d78-9322-4dc8-8917-f2e721a8e556 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L59 """ - params = [ - address, - block_num - ] - method = 'hmyv2_getCode' + params = [address, block_num] + method = "hmyv2_getCode" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_storage_at(address, key, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def get_storage_at( + address, key, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Get the storage from the state at the given address, the key and the block number @@ -199,18 +225,19 @@ def get_storage_at(address, key, block_num, endpoint=_default_endpoint, timeout= https://api.hmny.io/?version=latest#fa8ac8bd-952d-4149-968c-857ca76da43f https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L84 """ - params = [ - address, - key, - block_num - ] - method = 'hmyv2_getStorageAt' + params = [address, key, block_num] + method = "hmyv2_getStorageAt" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_contract_address_from_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def get_contract_address_from_hash( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Get address of the contract which was deployed in the transaction represented by tx_hash diff --git a/pyhmy/exceptions.py b/pyhmy/exceptions.py index 2238169..91a6882 100644 --- a/pyhmy/exceptions.py +++ b/pyhmy/exceptions.py @@ -1,8 +1,5 @@ -from .rpc.exceptions import ( - RPCError, - RequestsError, - RequestsTimeoutError -) +from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError + class InvalidRPCReplyError(RuntimeError): """ @@ -11,7 +8,8 @@ class InvalidRPCReplyError(RuntimeError): """ def __init__(self, method, endpoint): - super().__init__(f'Unexpected reply for {method} from {endpoint}') + super().__init__(f"Unexpected reply for {method} from {endpoint}") + class InvalidValidatorError(ValueError): """ @@ -19,11 +17,11 @@ class InvalidValidatorError(ValueError): """ errors = { - 1: 'Invalid ONE address', - 2: 'Field not initialized', - 3: 'Invalid field input', - 4: 'Error checking blockchain', - 5: 'Unable to import validator information from blockchain' + 1: "Invalid ONE address", + 2: "Field not initialized", + 3: "Invalid field input", + 4: "Error checking blockchain", + 5: "Unable to import validator information from blockchain", } def __init__(self, err_code, msg): @@ -32,7 +30,8 @@ class InvalidValidatorError(ValueError): super().__init__(msg) def __str__(self): - return f'[Errno {self.code}] {self.errors[self.code]}: {self.msg}' + return f"[Errno {self.code}] {self.errors[self.code]}: {self.msg}" + class TxConfirmationTimedoutError(AssertionError): """ @@ -41,4 +40,4 @@ class TxConfirmationTimedoutError(AssertionError): """ def __init__(self, msg): - super().__init__(f'{msg}') + super().__init__(f"{msg}") diff --git a/pyhmy/logging.py b/pyhmy/logging.py index 4b4787a..498cf70 100644 --- a/pyhmy/logging.py +++ b/pyhmy/logging.py @@ -9,8 +9,8 @@ import logging.handlers class _GZipRotator: def __call__(self, source, dest): os.rename(source, dest) - f_in = open(dest, 'rb') - f_out = gzip.open("%s.gz" % dest, 'wb') + f_in = open(dest, "rb") + f_out = gzip.open("%s.gz" % dest, "wb") f_out.writelines(f_in) f_out.close() f_in.close() @@ -27,13 +27,14 @@ class ControlledLogger: :param logger_name: The name of the logger and logfile :param log_dir: The directory in which to save this log file (can be abs or relative). """ - if log_dir.endswith('/'): + if log_dir.endswith("/"): log_dir = log_dir[:-1] log_dir = os.path.realpath(log_dir) os.makedirs(log_dir, exist_ok=True) - handler = logging.handlers.TimedRotatingFileHandler(f"{log_dir}/{logger_name}.log", 'midnight', 1, - backupCount=backup_count) - handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) + handler = logging.handlers.TimedRotatingFileHandler( + f"{log_dir}/{logger_name}.log", "midnight", 1, backupCount=backup_count + ) + handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) handler.rotator = _GZipRotator() self.filename = handler.baseFilename @@ -63,8 +64,9 @@ class ControlledLogger: :param msg: The info message to log """ self._lock.acquire() - self.info_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.info_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def debug(self, msg): @@ -72,8 +74,9 @@ class ControlledLogger: :param msg: The debug message to log """ self._lock.acquire() - self.debug_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.debug_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def warning(self, msg): @@ -81,8 +84,9 @@ class ControlledLogger: :param msg: The warning message to log """ self._lock.acquire() - self.warning_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.warning_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def error(self, msg): @@ -90,33 +94,34 @@ class ControlledLogger: :param msg: The error message to log """ self._lock.acquire() - self.error_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.error_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def print_info(self): """ Prints the current info buffer but does not flush it to log file. """ - print('\n'.join(self.info_buffer)) + print("\n".join(self.info_buffer)) def print_debug(self): """ Prints the current debug buffer but does not flush it to log file. """ - print('\n'.join(self.debug_buffer)) + print("\n".join(self.debug_buffer)) def print_warning(self): """ Prints the current warning buffer but does not flush it to log file. """ - print('\n'.join(self.warning_buffer)) + print("\n".join(self.warning_buffer)) def print_error(self): """ Prints the current error buffer but does not flush it to log file. """ - print('\n'.join(self.error_buffer)) + print("\n".join(self.error_buffer)) def write(self): """ diff --git a/pyhmy/rpc/exceptions.py b/pyhmy/rpc/exceptions.py index a14dfc6..a6ad3ac 100644 --- a/pyhmy/rpc/exceptions.py +++ b/pyhmy/rpc/exceptions.py @@ -8,7 +8,8 @@ class RPCError(RuntimeError): def __init__(self, method, endpoint, error): self.error = error - super().__init__(f'Error in reply from {endpoint}: {method} returned {error}') + super().__init__(f"Error in reply from {endpoint}: {method} returned {error}") + class RequestsError(requests.exceptions.RequestException): """ @@ -16,7 +17,8 @@ class RequestsError(requests.exceptions.RequestException): """ def __init__(self, endpoint): - super().__init__(f'Error connecting to {endpoint}') + super().__init__(f"Error connecting to {endpoint}") + class RequestsTimeoutError(requests.exceptions.Timeout): """ @@ -24,4 +26,4 @@ class RequestsTimeoutError(requests.exceptions.Timeout): """ def __init__(self, endpoint): - super().__init__(f'Error connecting to {endpoint}') + super().__init__(f"Error connecting to {endpoint}") diff --git a/pyhmy/rpc/request.py b/pyhmy/rpc/request.py index 81f32b0..be1c4e8 100644 --- a/pyhmy/rpc/request.py +++ b/pyhmy/rpc/request.py @@ -2,18 +2,16 @@ import json import requests -from .exceptions import ( - RequestsError, - RequestsTimeoutError, - RPCError -) +from .exceptions import RequestsError, RequestsTimeoutError, RPCError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 -def base_request(method, params=None, endpoint=_default_endpoint, timeout=_default_timeout) -> str: +def base_request( + method, params=None, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Basic RPC request @@ -45,21 +43,20 @@ def base_request(method, params=None, endpoint=_default_endpoint, timeout=_defau if params is None: params = [] elif not isinstance(params, list): - raise TypeError(f'invalid type {params.__class__}') + raise TypeError(f"invalid type {params.__class__}") try: - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": method, - "params": params - } - headers = { - 'Content-Type': 'application/json' - } - - resp = requests.request('POST', endpoint, headers=headers, data=json.dumps(payload), - timeout=timeout, allow_redirects=True) + payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} + headers = {"Content-Type": "application/json"} + + resp = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) return resp.content except requests.exceptions.Timeout as err: raise RequestsTimeoutError(endpoint) from err @@ -67,7 +64,9 @@ def base_request(method, params=None, endpoint=_default_endpoint, timeout=_defau raise RequestsError(endpoint) from err -def rpc_request(method, params=None, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def rpc_request( + method, params=None, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ RPC request @@ -106,8 +105,8 @@ def rpc_request(method, params=None, endpoint=_default_endpoint, timeout=_defaul try: resp = json.loads(raw_resp) - if 'error' in resp: - raise RPCError(method, endpoint, str(resp['error'])) + if "error" in resp: + raise RPCError(method, endpoint, str(resp["error"])) return resp except json.decoder.JSONDecodeError as err: raise RPCError(method, endpoint, raw_resp) from err diff --git a/pyhmy/signing.py b/pyhmy/signing.py index 9482ecf..4fddde8 100644 --- a/pyhmy/signing.py +++ b/pyhmy/signing.py @@ -1,33 +1,16 @@ import rlp -from eth_utils.curried import ( - keccak, - to_int, - hexstr_if_str, - apply_formatters_to_dict -) +from eth_utils.curried import keccak, to_int, hexstr_if_str, apply_formatters_to_dict -from rlp.sedes import ( - big_endian_int, - Binary, - binary -) +from rlp.sedes import big_endian_int, Binary, binary -from eth_account import ( - Account -) +from eth_account import Account -from eth_rlp import ( - HashableRLP -) +from eth_rlp import HashableRLP -from hexbytes import ( - HexBytes -) +from hexbytes import HexBytes -from eth_account._utils.signing import ( - sign_transaction_hash -) +from eth_account._utils.signing import sign_transaction_hash from eth_account._utils.legacy_transactions import ( Transaction as SignedEthereumTxData, @@ -35,96 +18,101 @@ from eth_account._utils.legacy_transactions import ( LEGACY_TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS, TRANSACTION_DEFAULTS, chain_id_to_v, - UNSIGNED_TRANSACTION_FIELDS + UNSIGNED_TRANSACTION_FIELDS, ) -from cytoolz import ( - dissoc, - pipe, - merge, - partial -) +from cytoolz import dissoc, pipe, merge, partial -from eth_account.datastructures import ( - SignedTransaction -) +from eth_account.datastructures import SignedTransaction -from .util import ( - chain_id_to_int, - convert_one_to_hex -) +from .util import chain_id_to_int, convert_one_to_hex HARMONY_FORMATTERS = dict( ETHEREUM_FORMATTERS, - shardID=hexstr_if_str(to_int), # additional fields for Harmony transaction - toShardID=hexstr_if_str(to_int), # which may be cross shard - ) + shardID=hexstr_if_str(to_int), # additional fields for Harmony transaction + toShardID=hexstr_if_str(to_int), # which may be cross shard +) + class UnsignedHarmonyTxData(HashableRLP): fields = ( - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gas', big_endian_int), - ('shardID', big_endian_int), - ('toShardID', big_endian_int), - ('to', Binary.fixed_length(20, allow_empty=True)), - ('value', big_endian_int), - ('data', binary), + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gas", big_endian_int), + ("shardID", big_endian_int), + ("toShardID", big_endian_int), + ("to", Binary.fixed_length(20, allow_empty=True)), + ("value", big_endian_int), + ("data", binary), ) + class SignedHarmonyTxData(HashableRLP): fields = UnsignedHarmonyTxData._meta.fields + ( - ("v", big_endian_int), # Recovery value + 27 - ("r", big_endian_int), # First 32 bytes - ("s", big_endian_int), # Next 32 bytes + ("v", big_endian_int), # Recovery value + 27 + ("r", big_endian_int), # First 32 bytes + ("s", big_endian_int), # Next 32 bytes ) -def encode_transaction(unsigned_transaction, vrs): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 - '''serialize and encode an unsigned transaction with v,r,s''' + +def encode_transaction( + unsigned_transaction, vrs +): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 + """serialize and encode an unsigned transaction with v,r,s""" (v, r, s) = vrs - chain_naive_transaction = dissoc( - unsigned_transaction.as_dict(), 'v', 'r', 's') - if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, - SignedHarmonyTxData)): + chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), "v", "r", "s") + if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, SignedHarmonyTxData)): serializer = SignedHarmonyTxData else: serializer = SignedEthereumTxData signed_transaction = serializer(v=v, r=r, s=s, **chain_naive_transaction) return rlp.encode(signed_transaction) + def serialize_transaction(filled_transaction): - '''serialize a signed/unsigned transaction''' - if 'v' in filled_transaction: - if 'shardID' in filled_transaction: + """serialize a signed/unsigned transaction""" + if "v" in filled_transaction: + if "shardID" in filled_transaction: serializer = SignedHarmonyTxData else: serializer = SignedEthereumTxData else: - if 'shardID' in filled_transaction: + if "shardID" in filled_transaction: serializer = UnsignedHarmonyTxData else: serializer = UnsignedEthereumTxData for f, _ in serializer._meta.fields: - assert f in filled_transaction, f'Could not find {f} in transaction' - return serializer.from_dict({f: filled_transaction[f] for f, _ in serializer._meta.fields}) + assert f in filled_transaction, f"Could not find {f} in transaction" + return serializer.from_dict( + {f: filled_transaction[f] for f, _ in serializer._meta.fields} + ) + def sanitize_transaction(transaction_dict, private_key): - '''remove the originating address from the dict and convert chainId to int''' - account = Account.from_key(private_key) # get account, from which you can derive public + private key - transaction_dict = transaction_dict.copy() # do not alter the original dictionary - if 'from' in transaction_dict: - transaction_dict[ 'from' ] = convert_one_to_hex( transaction_dict[ 'from' ] ) - if transaction_dict[ 'from' ] == account.address: # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 - sanitized_transaction = dissoc(transaction_dict, 'from') + """remove the originating address from the dict and convert chainId to int""" + account = Account.from_key( + private_key + ) # get account, from which you can derive public + private key + transaction_dict = transaction_dict.copy() # do not alter the original dictionary + if "from" in transaction_dict: + transaction_dict["from"] = convert_one_to_hex(transaction_dict["from"]) + if ( + transaction_dict["from"] == account.address + ): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 + sanitized_transaction = dissoc(transaction_dict, "from") else: - raise TypeError("from field must match key's %s, but it was %s" % ( + raise TypeError( + "from field must match key's %s, but it was %s" + % ( account.address, - transaction_dict['from'], - )) - if 'chainId' in transaction_dict: - transaction_dict[ 'chainId' ] = chain_id_to_int( transaction_dict[ 'chainId' ] ) + transaction_dict["from"], + ) + ) + if "chainId" in transaction_dict: + transaction_dict["chainId"] = chain_id_to_int(transaction_dict["chainId"]) return account, transaction_dict + def sign_transaction(transaction_dict, private_key) -> SignedTransaction: """ Sign a (non-staking) transaction dictionary with the specified private key @@ -171,24 +159,25 @@ def sign_transaction(transaction_dict, private_key) -> SignedTransaction: https://readthedocs.org/projects/eth-account/downloads/pdf/stable/ """ account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) - if 'to' in sanitized_transaction and sanitized_transaction[ 'to' ] is not None: - sanitized_transaction[ 'to' ] = convert_one_to_hex( sanitized_transaction[ 'to' ] ) - filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + if "to" in sanitized_transaction and sanitized_transaction["to"] is not None: + sanitized_transaction["to"] = convert_one_to_hex(sanitized_transaction["to"]) + filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 sanitized_transaction, dict, partial(merge, TRANSACTION_DEFAULTS), chain_id_to_v, - apply_formatters_to_dict(HARMONY_FORMATTERS) + apply_formatters_to_dict(HARMONY_FORMATTERS), ) unsigned_transaction = serialize_transaction(filled_transaction) transaction_hash = unsigned_transaction.hash() - if isinstance(unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData)): - chain_id = None # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 + if isinstance( + unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData) + ): + chain_id = None # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 else: chain_id = unsigned_transaction.v - (v, r, s) = sign_transaction_hash( - account._key_obj, transaction_hash, chain_id) + (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s)) signed_transaction_hash = keccak(encoded_transaction) return SignedTransaction( diff --git a/pyhmy/staking.py b/pyhmy/staking.py index 312f9d5..a70174a 100644 --- a/pyhmy/staking.py +++ b/pyhmy/staking.py @@ -1,18 +1,16 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ################## # Validator RPCs # ################## -def get_all_validator_addresses(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_all_validator_addresses( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of all created validator addresses on chain @@ -37,13 +35,16 @@ def get_all_validator_addresses(endpoint=_default_endpoint, timeout=_default_tim ------------- https://api.hmny.io/#69b93657-8d3c-4d20-9c9f-e51f08c9b3f5 """ - method = 'hmyv2_getAllValidatorAddresses' + method = "hmyv2_getAllValidatorAddresses" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_information(validator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_validator_information( + validator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get validator information for validator address @@ -112,16 +113,19 @@ def get_validator_information(validator_addr, endpoint=_default_endpoint, timeou ------------- https://api.hmny.io/#659ad999-14ca-4498-8f74-08ed347cab49 """ - method = 'hmyv2_getValidatorInformation' - params = [ - validator_addr - ] + method = "hmyv2_getValidatorInformation" + params = [validator_addr] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_elected_validator_addresses(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_elected_validator_addresses( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of elected validator addresses @@ -147,12 +151,13 @@ def get_elected_validator_addresses(endpoint=_default_endpoint, timeout=_default ------------- https://api.hmny.io/#e90a6131-d67c-4110-96ef-b283d452632d """ - method = 'hmyv2_getElectedValidatorAddresses' + method = "hmyv2_getElectedValidatorAddresses" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get validators list for a particular epoch @@ -183,16 +188,19 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152 """ - method = 'hmyv2_getValidators' - params = [ - epoch - ] + method = "hmyv2_getValidators" + params = [epoch] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_validator_keys( + epoch, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get validator BLS keys in the committee for a particular epoch @@ -218,16 +226,19 @@ def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeo ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152 """ - method = 'hmyv2_getValidatorKeys' - params = [ - epoch - ] + method = "hmyv2_getValidatorKeys" + params = [epoch] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_information_by_block_number(validator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout): + +def get_validator_information_by_block_number( + validator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout +): """ Get validator information for validator address at a block @@ -255,17 +266,19 @@ def get_validator_information_by_block_number(validator_addr, block_num, endpoin ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L319 """ - method = 'hmyv2_getValidatorInformationByBlockNumber' - params = [ - validator_addr, - block_num - ] + method = "hmyv2_getValidatorInformationByBlockNumber" + params = [validator_addr, block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_all_validator_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_all_validator_information( + page=0, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get validator information for all validators on chain @@ -291,16 +304,19 @@ def get_all_validator_information(page=0, endpoint=_default_endpoint, timeout=_d ------------- https://api.hmny.io/#df5f1631-7397-48e8-87b4-8dd873235b9c """ - method = 'hmyv2_getAllValidatorInformation' - params = [ - page - ] + method = "hmyv2_getAllValidatorInformation" + params = [page] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_self_delegation(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_validator_self_delegation( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the amount self delegated by validator @@ -326,16 +342,21 @@ def get_validator_self_delegation(address, endpoint=_default_endpoint, timeout=_ ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L352 """ - method = 'hmyv2_getValidatorSelfDelegation' - params = [ - address - ] + method = "hmyv2_getValidatorSelfDelegation" + params = [address] try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_total_delegation(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_validator_total_delegation( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the total amount delegated t ovalidator (including self delegated) @@ -361,16 +382,21 @@ def get_validator_total_delegation(address, endpoint=_default_endpoint, timeout= ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L379 """ - method = 'hmyv2_getValidatorTotalDelegation' - params = [ - address - ] + method = "hmyv2_getValidatorTotalDelegation" + params = [address] try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_all_validator_information_by_block_number(block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_all_validator_information_by_block_number( + block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get validator information at block number for all validators on chain @@ -399,20 +425,22 @@ def get_all_validator_information_by_block_number(block_num, page=0, endpoint=_d ------------- https://api.hmny.io/#a229253f-ca76-4b9d-88f5-9fd96e40d583 """ - method = 'hmyv2_getAllValidatorInformationByBlockNumber' - params = [ - page, - block_num - ] + method = "hmyv2_getAllValidatorInformationByBlockNumber" + params = [page, block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ################### # Delegation RPCs # ################### -def get_all_delegation_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_all_delegation_information( + page=0, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get delegation information for all delegators on chain @@ -440,16 +468,21 @@ def get_all_delegation_information(page=0, endpoint=_default_endpoint, timeout=_ ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L413 """ - method = 'hmyv2_getAllDelegationInformation' + method = "hmyv2_getAllDelegationInformation" params = [ page, ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegations_by_delegator(delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_delegations_by_delegator( + delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of delegations by a delegator @@ -482,16 +515,19 @@ def get_delegations_by_delegator(delegator_addr, endpoint=_default_endpoint, tim ------------- https://api.hmny.io/#454b032c-6072-4ecb-bf24-38b3d6d2af69 """ - method = 'hmyv2_getDelegationsByDelegator' - params = [ - delegator_addr - ] + method = "hmyv2_getDelegationsByDelegator" + params = [delegator_addr] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegations_by_delegator_by_block_number(delegator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_delegations_by_delegator_by_block_number( + delegator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of delegations by a delegator at a specific block @@ -519,18 +555,22 @@ def get_delegations_by_delegator_by_block_number(delegator_addr, block_num, endp ------------- https://api.hmny.io/#8ce13bda-e768-47b9-9dbe-193aba410b0a """ - method = 'hmyv2_getDelegationsByDelegatorByBlockNumber' - params = [ - delegator_addr, - block_num - ] + method = "hmyv2_getDelegationsByDelegatorByBlockNumber" + params = [delegator_addr, block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegation_by_delegator_and_validator(delegator_addr, validator_address, - endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_delegation_by_delegator_and_validator( + delegator_addr, + validator_address, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> dict: """ Get list of delegations by a delegator at a specific block @@ -558,17 +598,19 @@ def get_delegation_by_delegator_and_validator(delegator_addr, validator_address, ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L605 """ - method = 'hmyv2_getDelegationByDelegatorAndValidator' - params = [ - delegator_addr, - validator_address - ] + method = "hmyv2_getDelegationByDelegatorAndValidator" + params = [delegator_addr, validator_address] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_available_redelegation_balance(delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_available_redelegation_balance( + delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get amount of locked undelegated tokens @@ -594,16 +636,21 @@ def get_available_redelegation_balance(delegator_addr, endpoint=_default_endpoin ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L653 """ - method = 'hmyv2_getAvailableRedelegationBalance' - params = [ - delegator_addr - ] + method = "hmyv2_getAvailableRedelegationBalance" + params = [delegator_addr] try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegations_by_validator(validator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_delegations_by_validator( + validator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of delegations to a validator @@ -629,19 +676,22 @@ def get_delegations_by_validator(validator_addr, endpoint=_default_endpoint, tim ------------- https://api.hmny.io/#2e02d8db-8fec-41d9-a672-2c9862f63f39 """ - method = 'hmyv2_getDelegationsByValidator' - params = [ - validator_addr - ] + method = "hmyv2_getDelegationsByValidator" + params = [validator_addr] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ######################## # Staking Network RPCs # ######################## -def get_current_utility_metrics(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def get_current_utility_metrics( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get current utility metrics of network @@ -669,13 +719,16 @@ def get_current_utility_metrics(endpoint=_default_endpoint, timeout=_default_tim ------------- https://api.hmny.io/#78dd2d94-9ff1-4e0c-bbac-b4eec1cdf10b """ - method = 'hmyv2_getCurrentUtilityMetrics' + method = "hmyv2_getCurrentUtilityMetrics" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_network_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_staking_network_info( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking network information @@ -704,12 +757,13 @@ def get_staking_network_info(endpoint=_default_endpoint, timeout=_default_timeou ------------- https://api.hmny.io/#4a10fce0-2aa4-4583-bdcb-81ee0800993b """ - method = 'hmyv2_getStakingNetworkInfo' + method = "hmyv2_getStakingNetworkInfo" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get voting committees for current & previous epoch @@ -757,12 +811,13 @@ def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) - ------------- https://api.hmny.io/#8eef2fc4-92db-4610-a9cd-f7b75cfbd080 """ - method = 'hmyv2_getSuperCommittees' + method = "hmyv2_getSuperCommittees" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get total staking by validators, only meant to be called on beaconchain @@ -787,13 +842,16 @@ def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> i ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L102 """ - method = 'hmyv2_getTotalStaking' + method = "hmyv2_getTotalStaking" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_raw_median_stake_snapshot(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_raw_median_stake_snapshot( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get median stake & additional committee data of the current epoch @@ -830,8 +888,8 @@ def get_raw_median_stake_snapshot(endpoint=_default_endpoint, timeout=_default_t ------------- https://api.hmny.io/#bef93b3f-6763-4121-9c17-f0b0d9e5cc40 """ - method = 'hmyv2_getMedianRawStakeSnapshot' + method = "hmyv2_getMedianRawStakeSnapshot" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index afa5b52..03c96c6 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -6,29 +6,19 @@ from cytoolz import ( identity, ) -from hexbytes import ( - HexBytes -) +from hexbytes import HexBytes import rlp import math -from decimal import ( - Decimal -) +from decimal import Decimal -from eth_account.datastructures import ( - SignedTransaction -) +from eth_account.datastructures import SignedTransaction -from eth_account._utils.signing import ( - sign_transaction_hash -) +from eth_account._utils.signing import sign_transaction_hash -from eth_account._utils.legacy_transactions import ( - chain_id_to_v -) +from eth_account._utils.legacy_transactions import chain_id_to_v from eth_utils.curried import ( hexstr_if_str, @@ -37,12 +27,10 @@ from eth_utils.curried import ( apply_formatters_to_dict, to_int, apply_formatters_to_sequence, - apply_formatter_to_array + apply_formatter_to_array, ) -from .signing import ( - sanitize_transaction -) +from .signing import sanitize_transaction from .staking_structures import ( FORMATTERS, @@ -51,14 +39,15 @@ from .staking_structures import ( CreateValidator, EditValidator, DelegateOrUndelegate, - CollectRewards + CollectRewards, ) -from .util import ( - convert_one_to_hex -) +from .util import convert_one_to_hex + -def _convert_staking_percentage_to_number(value): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 +def _convert_staking_percentage_to_number( + value, +): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 """ Convert from staking percentage to integer For example, 0.1 becomes 1000000000000000000 @@ -77,38 +66,43 @@ def _convert_staking_percentage_to_number(value): # https://github.com/ha AssertionError, if data types are not as expected ValueError, if the input type is not supported """ - assert isinstance(value, (str, Decimal)), 'Only strings or decimals are supported' + assert isinstance(value, (str, Decimal)), "Only strings or decimals are supported" if isinstance(value, Decimal): value = str(value) - value1 = value; - if value[0] == '-': - raise ValueError('Negative numbers are not accepted') - if value[0] == '+': + value1 = value + if value[0] == "-": + raise ValueError("Negative numbers are not accepted") + if value[0] == "+": value1 = value[1:] if len(value1) == 0: - raise ValueError('StakingDecimal string is empty') - spaced = value1.split(' ') + raise ValueError("StakingDecimal string is empty") + spaced = value1.split(" ") if len(spaced) > 1: - raise ValueError('Bad decimal string') - splitted = value1.split('.') + raise ValueError("Bad decimal string") + splitted = value1.split(".") combined_str = splitted[0] if len(splitted) == 2: length = len(splitted[1]) if length == 0 or len(combined_str) == 0: - raise ValueError('Bad StakingDecimal length') - if splitted[1][0] == '-': - raise ValueError('Bad StakingDecimal string') + raise ValueError("Bad StakingDecimal length") + if splitted[1][0] == "-": + raise ValueError("Bad StakingDecimal string") combined_str += splitted[1] elif len(splitted) > 2: - raise ValueError('Too many periods to be a StakingDecimal string') + raise ValueError("Too many periods to be a StakingDecimal string") if length > StakingSettings.PRECISION: - raise ValueError('Too much precision, must be less than {StakingSettings.PRECISION}') + raise ValueError( + "Too much precision, must be less than {StakingSettings.PRECISION}" + ) zeroes_to_add = StakingSettings.PRECISION - length - combined_str += '0' * zeroes_to_add # This will not have any periods, so it is effectively a large integer + combined_str += ( + "0" * zeroes_to_add + ) # This will not have any periods, so it is effectively a large integer val = int(combined_str) - assert val <= StakingSettings.MAX_DECIMAL, 'Staking percentage is too large' + assert val <= StakingSettings.MAX_DECIMAL, "Staking percentage is too large" return val + def _get_account_and_transaction(transaction_dict, private_key): """ Create account from private key and sanitize the transaction @@ -132,10 +126,15 @@ def _get_account_and_transaction(transaction_dict, private_key): AssertionError, if chainId is not present in util.chain_id_to_int TypeError, if the value of 'from' key is not the same as account address """ - account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) # remove from, convert chain id (if present) to integer - sanitized_transaction['directive'] = sanitized_transaction['directive'].value # convert to value, like in TypeScript + account, sanitized_transaction = sanitize_transaction( + transaction_dict, private_key + ) # remove from, convert chain id (if present) to integer + sanitized_transaction["directive"] = sanitized_transaction[ + "directive" + ].value # convert to value, like in TypeScript return account, sanitized_transaction + def _sign_transaction_generic(account, sanitized_transaction, parent_serializer): """ Sign a generic staking transaction, given the serializer base class and account @@ -157,39 +156,54 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) rlp.exceptions.ObjectSerializationError, if data types are not as expected """ # obtain the serializers - if sanitized_transaction.get('chainId', 0) == 0: - unsigned_serializer, signed_serializer = parent_serializer.Unsigned(), parent_serializer.Signed() # unsigned, signed + if sanitized_transaction.get("chainId", 0) == 0: + unsigned_serializer, signed_serializer = ( + parent_serializer.Unsigned(), + parent_serializer.Signed(), + ) # unsigned, signed else: - unsigned_serializer, signed_serializer = parent_serializer.SignedChainId(), parent_serializer.SignedChainId() # since chain_id_to_v adds v/r/s, unsigned is not used here + unsigned_serializer, signed_serializer = ( + parent_serializer.SignedChainId(), + parent_serializer.SignedChainId(), + ) # since chain_id_to_v adds v/r/s, unsigned is not used here # fill the transaction - filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 sanitized_transaction, dict, - partial(merge, {'chainId': None}), - chain_id_to_v, # will move chain id to v and add v/r/s - apply_formatters_to_dict(FORMATTERS) + partial(merge, {"chainId": None}), + chain_id_to_v, # will move chain id to v and add v/r/s + apply_formatters_to_dict(FORMATTERS), ) # get the unsigned transaction for f, _ in unsigned_serializer._meta.fields: - assert f in filled_transaction, f'Could not find {f} in transaction' - unsigned_transaction = unsigned_serializer.from_dict(\ - {f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields}) # drop extras silently + assert f in filled_transaction, f"Could not find {f} in transaction" + unsigned_transaction = unsigned_serializer.from_dict( + {f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields} + ) # drop extras silently # sign the unsigned transaction - if 'v' in unsigned_transaction.as_dict(): + if "v" in unsigned_transaction.as_dict(): chain_id = unsigned_transaction.v else: chain_id = None transaction_hash = unsigned_transaction.hash() - (v, r, s) = sign_transaction_hash( - account._key_obj, transaction_hash, chain_id) + (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) chain_naive_transaction = dissoc( - unsigned_transaction.as_dict(), 'v', 'r', 's') # remove extra v/r/s added by chain_id_to_v + unsigned_transaction.as_dict(), "v", "r", "s" + ) # remove extra v/r/s added by chain_id_to_v # serialize it signed_transaction = signed_serializer( - v=v + (8 if chain_id is None else 0), # copied from https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 + v=v + + ( + 8 if chain_id is None else 0 + ), # copied from https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 r=r, - s=s, # in the below statement, remove everything not expected by signed_serializer - **{f: chain_naive_transaction[f] for f, _ in signed_serializer._meta.fields if f not in 'vrs'}) + s=s, # in the below statement, remove everything not expected by signed_serializer + **{ + f: chain_naive_transaction[f] + for f, _ in signed_serializer._meta.fields + if f not in "vrs" + }, + ) # encode it encoded_transaction = rlp.encode(signed_transaction) # hash it @@ -203,29 +217,34 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) v=v, ) + def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate): """ Sign a delegate or undelegate transaction See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] not in [ Directive.Delegate, Directive.Undelegate ]: - raise TypeError('Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate') + if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]: + raise TypeError( + "Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate" + ) # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg - sanitized_transaction['stakeMsg'] = \ - apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), - hexstr_if_str(to_bytes), - hexstr_if_str(to_int) - ], [ - convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')), - convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), - sanitized_transaction.pop('amount'), - ] - ) - return _sign_transaction_generic(account, sanitized_transaction, DelegateOrUndelegate) + sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + [hexstr_if_str(to_bytes), hexstr_if_str(to_bytes), hexstr_if_str(to_int)], + [ + convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")), + convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), + sanitized_transaction.pop("amount"), + ], + ) + return _sign_transaction_generic( + account, sanitized_transaction, DelegateOrUndelegate + ) + def _sign_collect_rewards(transaction_dict, private_key): """ @@ -233,109 +252,147 @@ def _sign_collect_rewards(transaction_dict, private_key): See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] != Directive.CollectRewards: - raise TypeError('Only CollectRewards is supported by _sign_collect_rewards') + if transaction_dict["directive"] != Directive.CollectRewards: + raise TypeError("Only CollectRewards is supported by _sign_collect_rewards") # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg - sanitized_transaction['stakeMsg'] = \ - [hexstr_if_str(to_bytes)(convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')))] + sanitized_transaction["stakeMsg"] = [ + hexstr_if_str(to_bytes)( + convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")) + ) + ] return _sign_transaction_generic(account, sanitized_transaction, CollectRewards) + def _sign_create_validator(transaction_dict, private_key): """ Sign a create validator transaction See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] != Directive.CreateValidator: - raise TypeError('Only CreateValidator is supported by _sign_create_or_edit_validator') + if transaction_dict["directive"] != Directive.CreateValidator: + raise TypeError( + "Only CreateValidator is supported by _sign_create_or_edit_validator" + ) # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg description = [ - sanitized_transaction.pop('name'), - sanitized_transaction.pop('identity'), - sanitized_transaction.pop('website'), - sanitized_transaction.pop('security-contact'), - sanitized_transaction.pop('details'), - ] - commission = apply_formatter_to_array( hexstr_if_str(to_int), # formatter + sanitized_transaction.pop("name"), + sanitized_transaction.pop("identity"), + sanitized_transaction.pop("website"), + sanitized_transaction.pop("security-contact"), + sanitized_transaction.pop("details"), + ] + commission = apply_formatter_to_array( + hexstr_if_str(to_int), # formatter [ - _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')), - _convert_staking_percentage_to_number(sanitized_transaction.pop('max-rate')), - _convert_staking_percentage_to_number(sanitized_transaction.pop('max-change-rate')), - ] + _convert_staking_percentage_to_number(sanitized_transaction.pop("rate")), + _convert_staking_percentage_to_number( + sanitized_transaction.pop("max-rate") + ), + _convert_staking_percentage_to_number( + sanitized_transaction.pop("max-change-rate") + ), + ], ) - commission = [ [element] for element in commission ] - bls_keys = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter - sanitized_transaction.pop('bls-public-keys') + commission = [[element] for element in commission] + bls_keys = apply_formatter_to_array( + hexstr_if_str(to_bytes), # formatter + sanitized_transaction.pop("bls-public-keys"), ) - bls_key_sigs = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter - sanitized_transaction.pop('bls-key-sigs') + bls_key_sigs = apply_formatter_to_array( + hexstr_if_str(to_bytes), sanitized_transaction.pop("bls-key-sigs") # formatter ) - sanitized_transaction['stakeMsg'] = \ - apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), # address - identity, # description - identity, # commission rates - hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped - hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped - identity, # bls public keys - identity, # bls key sigs - hexstr_if_str(to_int), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) - ], [ - convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), + sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + [ + hexstr_if_str(to_bytes), # address + identity, # description + identity, # commission rates + hexstr_if_str( + to_int + ), # min self delegation (in ONE), decimals are silently dropped + hexstr_if_str( + to_int + ), # max total delegation (in ONE), decimals are silently dropped + identity, # bls public keys + identity, # bls key sigs + hexstr_if_str( + to_int + ), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) + ], + [ + convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), description, commission, - math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly - math.floor(sanitized_transaction.pop('max-total-delegation')), + math.floor( + sanitized_transaction.pop("min-self-delegation") + ), # Decimal floors it correctly + math.floor(sanitized_transaction.pop("max-total-delegation")), bls_keys, bls_key_sigs, - math.floor(sanitized_transaction.pop('amount')), - ] - ) + math.floor(sanitized_transaction.pop("amount")), + ], + ) return _sign_transaction_generic(account, sanitized_transaction, CreateValidator) + def _sign_edit_validator(transaction_dict, private_key): """ Sign an edit validator transaction See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] != Directive.EditValidator: - raise TypeError('Only EditValidator is supported by _sign_create_or_edit_validator') + if transaction_dict["directive"] != Directive.EditValidator: + raise TypeError( + "Only EditValidator is supported by _sign_create_or_edit_validator" + ) # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg description = [ - sanitized_transaction.pop('name'), - sanitized_transaction.pop('identity'), - sanitized_transaction.pop('website'), - sanitized_transaction.pop('security-contact'), - sanitized_transaction.pop('details'), - ] - sanitized_transaction['stakeMsg'] = \ - apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), # address - identity, # description - identity, # new rate (it's in a list so can't do hexstr_if_str) - hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped - hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped - hexstr_if_str(to_bytes), # key to remove - hexstr_if_str(to_bytes), # key to add - ], [ - convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), + sanitized_transaction.pop("name"), + sanitized_transaction.pop("identity"), + sanitized_transaction.pop("website"), + sanitized_transaction.pop("security-contact"), + sanitized_transaction.pop("details"), + ] + sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + [ + hexstr_if_str(to_bytes), # address + identity, # description + identity, # new rate (it's in a list so can't do hexstr_if_str) + hexstr_if_str( + to_int + ), # min self delegation (in ONE), decimals are silently dropped + hexstr_if_str( + to_int + ), # max total delegation (in ONE), decimals are silently dropped + hexstr_if_str(to_bytes), # key to remove + hexstr_if_str(to_bytes), # key to add + ], + [ + convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), description, - [ _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')) ], - math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly - math.floor(sanitized_transaction.pop('max-total-delegation')), - sanitized_transaction.pop('bls-key-to-remove'), - sanitized_transaction.pop('bls-key-to-add') - ] - ) + [_convert_staking_percentage_to_number(sanitized_transaction.pop("rate"))], + math.floor( + sanitized_transaction.pop("min-self-delegation") + ), # Decimal floors it correctly + math.floor(sanitized_transaction.pop("max-total-delegation")), + sanitized_transaction.pop("bls-key-to-remove"), + sanitized_transaction.pop("bls-key-to-add"), + ], + ) return _sign_transaction_generic(account, sanitized_transaction, EditValidator) + def sign_staking_transaction(transaction_dict, private_key): """ Sign a supplied transaction_dict with the private_key @@ -404,18 +461,22 @@ def sign_staking_transaction(transaction_dict, private_key): ------------- https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts """ - assert isinstance(transaction_dict, dict), 'Only dictionaries are supported' # OrderedDict is a subclass + assert isinstance( + transaction_dict, dict + ), "Only dictionaries are supported" # OrderedDict is a subclass # chain_id missing => 'rlp: input string too long for uint64, decoding into (types.StakingTransaction)(types.txdata).GasLimit' - assert 'chainId' in transaction_dict, 'chainId missing' - assert 'directive' in transaction_dict, 'Staking transaction type not specified' - assert isinstance(transaction_dict['directive'], Directive), 'Unknown staking transaction type' - if transaction_dict['directive'] == Directive.CollectRewards: + assert "chainId" in transaction_dict, "chainId missing" + assert "directive" in transaction_dict, "Staking transaction type not specified" + assert isinstance( + transaction_dict["directive"], Directive + ), "Unknown staking transaction type" + if transaction_dict["directive"] == Directive.CollectRewards: return _sign_collect_rewards(transaction_dict, private_key) - elif transaction_dict['directive'] == Directive.Delegate: + elif transaction_dict["directive"] == Directive.Delegate: return _sign_delegate_or_undelegate(transaction_dict, private_key, True) - elif transaction_dict['directive'] == Directive.Undelegate: + elif transaction_dict["directive"] == Directive.Undelegate: return _sign_delegate_or_undelegate(transaction_dict, private_key, False) - elif transaction_dict['directive'] == Directive.CreateValidator: + elif transaction_dict["directive"] == Directive.CreateValidator: return _sign_create_validator(transaction_dict, private_key) - elif transaction_dict['directive'] == Directive.EditValidator: + elif transaction_dict["directive"] == Directive.EditValidator: return _sign_edit_validator(transaction_dict, private_key) diff --git a/pyhmy/staking_structures.py b/pyhmy/staking_structures.py index d8907f3..44f7102 100644 --- a/pyhmy/staking_structures.py +++ b/pyhmy/staking_structures.py @@ -1,219 +1,294 @@ -from enum import ( - Enum, - auto -) +from enum import Enum, auto -from rlp.sedes import ( - big_endian_int, - Binary, - CountableList, - List, - Text -) +from rlp.sedes import big_endian_int, Binary, CountableList, List, Text -from eth_rlp import ( - HashableRLP -) +from eth_rlp import HashableRLP from eth_utils.curried import ( to_int, hexstr_if_str, ) + class StakingSettings: PRECISION = 18 MAX_DECIMAL = 1000000000000000000 -class Directive(Enum): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 + +class Directive( + Enum +): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 def _generate_next_value_(name, start, count, last_values): return count + CreateValidator = auto() EditValidator = auto() Delegate = auto() Undelegate = auto() CollectRewards = auto() + FORMATTERS = { - 'directive': hexstr_if_str(to_int), # delegatorAddress is already formatted before the call - 'nonce': hexstr_if_str(to_int), - 'gasPrice': hexstr_if_str(to_int), - 'gasLimit': hexstr_if_str(to_int), - 'chainId': hexstr_if_str(to_int), + "directive": hexstr_if_str( + to_int + ), # delegatorAddress is already formatted before the call + "nonce": hexstr_if_str(to_int), + "gasPrice": hexstr_if_str(to_int), + "gasLimit": hexstr_if_str(to_int), + "chainId": hexstr_if_str(to_int), } + class CollectRewards: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', CountableList(Binary.fixed_length(20, allow_empty=True))), - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ("stakeMsg", CountableList(Binary.fixed_length(20, allow_empty=True))), + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = CollectRewards.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = CollectRewards.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = CollectRewards.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed + class DelegateOrUndelegate: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', List([Binary.fixed_length(20, allow_empty=True),Binary.fixed_length(20, allow_empty=True),big_endian_int],True)), - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ( + "stakeMsg", + List( + [ + Binary.fixed_length(20, allow_empty=True), + Binary.fixed_length(20, allow_empty=True), + big_endian_int, + ], + True, + ), + ), + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): - fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] # drop chainId + fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ + :-1 + ] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = DelegateOrUndelegate.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = DelegateOrUndelegate.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed + class CreateValidator: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', List([ # list with the following members - Binary.fixed_length(20, allow_empty=True), # validatorAddress - List([Text()]*5,True), # description is Text of 5 elements - List([List([big_endian_int],True)]*3,True), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] - big_endian_int, # min self delegation - big_endian_int, # max total delegation - CountableList(Binary.fixed_length(48, allow_empty=True)), # bls-public-keys array of unspecified length, each key of 48 - CountableList(Binary.fixed_length(96, allow_empty=True)), # bls-key-sigs array of unspecified length, each sig of 96 - big_endian_int, # amount - ], True)), # strictly these number of elements - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ( + "stakeMsg", + List( + [ # list with the following members + Binary.fixed_length( + 20, allow_empty=True + ), # validatorAddress + List( + [Text()] * 5, True + ), # description is Text of 5 elements + List( + [List([big_endian_int], True)] * 3, True + ), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] + big_endian_int, # min self delegation + big_endian_int, # max total delegation + CountableList( + Binary.fixed_length(48, allow_empty=True) + ), # bls-public-keys array of unspecified length, each key of 48 + CountableList( + Binary.fixed_length(96, allow_empty=True) + ), # bls-key-sigs array of unspecified length, each sig of 96 + big_endian_int, # amount + ], + True, + ), + ), # strictly these number of elements + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = CreateValidator.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = CreateValidator.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = CreateValidator.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed + class EditValidator: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', List([ # list with the following members - Binary.fixed_length(20, allow_empty=True), # validatorAddress - List([Text()]*5,True), # description is Text of 5 elements - List([big_endian_int],True), # new rate is in a list - big_endian_int, # min self delegation - big_endian_int, # max total delegation - Binary.fixed_length(48, allow_empty=True), # slot key to remove - Binary.fixed_length(48, allow_empty=True), # slot key to add - ], True)), # strictly these number of elements - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ( + "stakeMsg", + List( + [ # list with the following members + Binary.fixed_length( + 20, allow_empty=True + ), # validatorAddress + List( + [Text()] * 5, True + ), # description is Text of 5 elements + List([big_endian_int], True), # new rate is in a list + big_endian_int, # min self delegation + big_endian_int, # max total delegation + Binary.fixed_length( + 48, allow_empty=True + ), # slot key to remove + Binary.fixed_length( + 48, allow_empty=True + ), # slot key to add + ], + True, + ), + ), # strictly these number of elements + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = EditValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = EditValidator.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = EditValidator.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = EditValidator.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index 2e34914..b1c890c 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -1,21 +1,18 @@ -from .rpc.request import ( - rpc_request -) -from .exceptions import ( - TxConfirmationTimedoutError, - InvalidRPCReplyError -) +from .rpc.request import rpc_request +from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError import time import random -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ######################### # Transaction Pool RPCs # ######################### -def get_pending_transactions(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_pending_transactions( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of pending transactions @@ -39,13 +36,16 @@ def get_pending_transactions(endpoint=_default_endpoint, timeout=_default_timeou ------------- https://api.hmny.io/#de6c4a12-fa42-44e8-972f-801bfde1dd18 """ - method = 'hmyv2_pendingTransactions' + method = "hmyv2_pendingTransactions" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_transaction_error_sink( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get current transactions error sink @@ -72,13 +72,16 @@ def get_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_time ------------- https://api.hmny.io/#9aedbc22-6262-44b1-8276-cd8ae19fa600 """ - method = 'hmyv2_getCurrentTransactionErrorSink' + method = "hmyv2_getCurrentTransactionErrorSink" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_pending_staking_transactions(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_pending_staking_transactions( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of pending staking transactions @@ -102,14 +105,16 @@ def get_pending_staking_transactions(endpoint=_default_endpoint, timeout=_defaul ------------- https://api.hmny.io/#de0235e4-f4c9-4a69-b6d2-b77dc1ba7b12 """ - method = 'hmyv2_pendingStakingTransactions' + method = "hmyv2_pendingStakingTransactions" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_staking_transaction_error_sink( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get current staking transactions error sink @@ -137,12 +142,13 @@ def get_staking_transaction_error_sink(endpoint=_default_endpoint, timeout=_defa ------------- https://api.hmny.io/#bdd00e0f-2ba0-480e-b996-2ef13f10d75a """ - method = 'hmyv2_getCurrentStakingErrorSink' + method = "hmyv2_getCurrentStakingErrorSink" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get stats of the pool, that is, number of pending and queued (non-executable) transactions @@ -169,16 +175,19 @@ def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict ------------- https://api.hmny.io/#7c2b9395-8f5e-4eb5-a687-2f1be683d83e """ - method = 'hmyv2_getPoolStats' + method = "hmyv2_getPoolStats" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + #################### # Transaction RPCs # #################### -def get_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def get_transaction_by_hash( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction by hash @@ -224,18 +233,19 @@ def get_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_defaul ------------- https://api.hmny.io/#117e84f6-a0ec-444e-abe0-455701310389 """ - method = 'hmyv2_getTransactionByHash' - params = [ - tx_hash - ] + method = "hmyv2_getTransactionByHash" + params = [tx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_by_block_hash_and_index(block_hash, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_transaction_by_block_hash_and_index( + block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction based on index in list of transactions in a block by block hash @@ -263,19 +273,19 @@ def get_transaction_by_block_hash_and_index(block_hash, tx_index, ------------- https://api.hmny.io/#7c7e8d90-4984-4ebe-bb7e-d7adec167503 """ - method = 'hmyv2_getTransactionByBlockHashAndIndex' - params = [ - block_hash, - tx_index - ] + method = "hmyv2_getTransactionByBlockHashAndIndex" + params = [block_hash, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_by_block_number_and_index(block_num, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_transaction_by_block_number_and_index( + block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction based on index in list of transactions in a block by block number @@ -303,17 +313,19 @@ def get_transaction_by_block_number_and_index(block_num, tx_index, ------------- https://api.hmny.io/#bcde8b1c-6ab9-4950-9835-3c7564e49c3e """ - method = 'hmyv2_getTransactionByBlockNumberAndIndex' - params = [ - block_num, - tx_index - ] + method = "hmyv2_getTransactionByBlockNumberAndIndex" + params = [block_num, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_receipt(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_transaction_receipt( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction receipt corresponding to tx_hash @@ -353,16 +365,19 @@ def get_transaction_receipt(tx_hash, endpoint=_default_endpoint, timeout=_defaul ------------- https://api.hmny.io/#0c2799f8-bcdc-41a4-b362-c3a6a763bb5e """ - method = 'hmyv2_getTransactionReceipt' - params = [ - tx_hash - ] + method = "hmyv2_getTransactionReceipt" + params = [tx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def send_raw_transaction( + signed_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Send signed transaction @@ -391,16 +406,19 @@ def send_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default ------------- https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0 """ - params = [ - signed_tx - ] - method = 'hmyv2_sendRawTransaction' + params = [signed_tx] + method = "hmyv2_sendRawTransaction" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def send_and_confirm_raw_transaction( + signed_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Send signed transaction and wait for it to be confirmed @@ -433,20 +451,23 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time """ tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint) start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint) if tx_response is not None: - block_hash = tx_response.get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_response.get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": return tx_response time.sleep(random.uniform(0.2, 0.5)) raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") + ############################### # CrossShard Transaction RPCs # ############################### -def get_pending_cx_receipts(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_pending_cx_receipts( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of pending cross shard transactions @@ -493,13 +514,16 @@ def get_pending_cx_receipts(endpoint=_default_endpoint, timeout=_default_timeout ------------- https://api.hmny.io/#fe60070d-97b4-458d-9365-490b44c18851 """ - method = 'hmyv2_getPendingCXReceipts' + method = "hmyv2_getPendingCXReceipts" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_cx_receipt_by_hash(cx_hash, endpoint = _default_endpoint, timeout = _default_timeout) -> dict: + +def get_cx_receipt_by_hash( + cx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get cross shard receipt by hash on the receiving shard end point @@ -534,16 +558,19 @@ def get_cx_receipt_by_hash(cx_hash, endpoint = _default_endpoint, timeout = _def ------------- https://api.hmny.io/#3d6ad045-800d-4021-aeb5-30a0fbf724fe """ - params = [ - cx_hash - ] - method = 'hmyv2_getCXReceiptByHash' + params = [cx_hash] + method = "hmyv2_getCXReceiptByHash" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def resend_cx_receipt(cx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def resend_cx_receipt( + cx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ Resend the cross shard receipt to the receiving shard to re-process if the transaction did not pay out @@ -570,19 +597,22 @@ def resend_cx_receipt(cx_hash, endpoint=_default_endpoint, timeout=_default_time ------------- https://api.hmny.io/#c658b56b-d20b-480d-b71a-b0bc505d2164 """ - method = 'hmyv2_resendCx' - params = [ - cx_hash - ] + method = "hmyv2_resendCx" + params = [cx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ############################ # Staking Transaction RPCs # ############################ -def get_staking_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def get_staking_transaction_by_hash( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking transaction by hash @@ -622,18 +652,19 @@ def get_staking_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout ------------- https://api.hmny.io/#296cb4d0-bce2-48e3-bab9-64c3734edd27 """ - method = 'hmyv2_getStakingTransactionByHash' - params = [ - tx_hash - ] + method = "hmyv2_getStakingTransactionByHash" + params = [tx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_by_block_hash_and_index(block_hash, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_staking_transaction_by_block_hash_and_index( + block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking transaction by block hash and transaction index @@ -661,19 +692,19 @@ def get_staking_transaction_by_block_hash_and_index(block_hash, tx_index, ------------- https://api.hmny.io/#ba96cf61-61fe-464a-aa06-2803bb4b358f """ - method = 'hmyv2_getStakingTransactionByBlockHashAndIndex' - params = [ - block_hash, - tx_index - ] + method = "hmyv2_getStakingTransactionByBlockHashAndIndex" + params = [block_hash, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_by_block_number_and_index(block_num, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_staking_transaction_by_block_number_and_index( + block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking transaction by block number and transaction index @@ -701,17 +732,19 @@ def get_staking_transaction_by_block_number_and_index(block_num, tx_index, ------------- https://api.hmny.io/#fb41d717-1645-4d3e-8071-6ce8e1b65dd3 """ - method = 'hmyv2_getStakingTransactionByBlockNumberAndIndex' - params = [ - block_num, - tx_index - ] + method = "hmyv2_getStakingTransactionByBlockNumberAndIndex" + params = [block_num, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_raw_staking_transaction(raw_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def send_raw_staking_transaction( + raw_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Send signed staking transaction @@ -740,16 +773,19 @@ def send_raw_staking_transaction(raw_tx, endpoint=_default_endpoint, timeout=_de ------------- https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401 """ - method = 'hmyv2_sendRawStakingTransaction' - params = [ - raw_tx - ] + method = "hmyv2_sendRawStakingTransaction" + params = [raw_tx] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def send_and_confirm_raw_staking_transaction( + signed_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Send signed staking transaction and wait for it to be confirmed @@ -782,12 +818,12 @@ def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoi """ tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint) start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint) if tx_response is not None: - block_hash = tx_response.get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_response.get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": return tx_response time.sleep(random.uniform(0.2, 0.5)) - raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") \ No newline at end of file + raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") diff --git a/pyhmy/util.py b/pyhmy/util.py index 36c350f..d287821 100644 --- a/pyhmy/util.py +++ b/pyhmy/util.py @@ -6,9 +6,7 @@ import datetime import requests -from .blockchain import ( - get_latest_header -) +from .blockchain import get_latest_header from .rpc.exceptions import ( RPCError, @@ -16,63 +14,63 @@ from .rpc.exceptions import ( RequestsTimeoutError, ) -from .account import ( - is_valid_address -) +from .account import is_valid_address -from .bech32.bech32 import ( - bech32_decode, - bech32_encode, - convertbits -) +from .bech32.bech32 import bech32_decode, bech32_encode, convertbits from eth_utils import to_checksum_address datetime_format = "%Y-%m-%d %H:%M:%S.%f" + class Typgpy(str): """ Typography constants for pretty printing. Note that an ENDC is needed to mark the end of a 'highlighted' text segment. """ - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' + + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" + def chain_id_to_int(chainId): chainIds = dict( - Default = 0, - EthMainnet = 1, - Morden = 2, - Ropsten = 3, - Rinkeby = 4, - RootstockMainnet = 30, - RootstockTestnet = 31, - Kovan = 42, - EtcMainnet = 61, - EtcTestnet = 62, - Geth = 1337, - Ganache = 0, - HmyMainnet = 1, - HmyTestnet = 2, - HmyLocal = 2, - HmyPangaea = 3, + Default=0, + EthMainnet=1, + Morden=2, + Ropsten=3, + Rinkeby=4, + RootstockMainnet=30, + RootstockTestnet=31, + Kovan=42, + EtcMainnet=61, + EtcTestnet=62, + Geth=1337, + Ganache=0, + HmyMainnet=1, + HmyTestnet=2, + HmyLocal=2, + HmyPangaea=3, ) # do not validate integer chainids, only known strings if isinstance(chainId, str): - assert chainId in chainIds, f'Chain {chainId} unknown, specify an integer chainId' + assert ( + chainId in chainIds + ), f"Chain {chainId} unknown, specify an integer chainId" return chainIds.get(chainId) elif isinstance(chainId, int): return chainId else: - raise TypeError( 'chainId must be str or int' ) + raise TypeError("chainId must be str or int") + def get_gopath(): """ @@ -87,6 +85,7 @@ def get_goversion(): """ return subprocess.check_output(["go", "version"]).decode().strip() + def convert_one_to_hex(addr): """ Given a one address, convert it to hex checksum address @@ -95,9 +94,10 @@ def convert_one_to_hex(addr): return to_checksum_address(addr) hrp, data = bech32_decode(addr) buf = convertbits(data, 5, 8, False) - address = '0x' + ''.join('{:02x}'.format(x) for x in buf) + address = "0x" + "".join("{:02x}".format(x) for x in buf) return to_checksum_address(address) + def convert_hex_to_one(addr): """ Given a hex address, convert it to a one address @@ -105,10 +105,13 @@ def convert_hex_to_one(addr): if is_valid_address(addr): return addr checksum_addr = to_checksum_address(addr) - data = bytearray.fromhex(checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr) + data = bytearray.fromhex( + checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr + ) buf = convertbits(data, 8, 5) return bech32_encode("one", buf) + def is_active_shard(endpoint, delay_tolerance=60): """ :param endpoint: The endpoint of the SHARD to check @@ -118,8 +121,10 @@ def is_active_shard(endpoint, delay_tolerance=60): try: curr_time = datetime.datetime.utcnow() latest_header = get_latest_header(endpoint=endpoint) - time_str = latest_header["timestamp"][:19] + '.0' # Fit time format - timestamp = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S.%f").replace(tzinfo=None) + time_str = latest_header["timestamp"][:19] + ".0" # Fit time format + timestamp = datetime.datetime.strptime( + time_str, "%Y-%m-%d %H:%M:%S.%f" + ).replace(tzinfo=None) time_delta = curr_time - timestamp return abs(time_delta.seconds) < delay_tolerance except (RPCError, RequestsError, RequestsTimeoutError): @@ -137,7 +142,12 @@ def get_bls_build_variables(): """ variables = {} try: - openssl_dir = subprocess.check_output(["which", "openssl"]).decode().strip().split("\n")[0] + openssl_dir = ( + subprocess.check_output(["which", "openssl"]) + .decode() + .strip() + .split("\n")[0] + ) except (IndexError, subprocess.CalledProcessError) as e: raise RuntimeError("`openssl` not found") from e hmy_path = f"{get_gopath()}/src/github.com/harmony-one" @@ -146,7 +156,9 @@ def get_bls_build_variables(): assert os.path.exists(bls_dir), f"Harmony BLS repo not found at {bls_dir}" assert os.path.exists(mcl_dir), f"Harmony MCL repo not found at {mcl_dir}" if sys.platform.startswith("darwin"): - variables["CGO_CFLAGS"] = f"-I{bls_dir}/include -I{mcl_dir}/include -I{openssl_dir}/include" + variables[ + "CGO_CFLAGS" + ] = f"-I{bls_dir}/include -I{mcl_dir}/include -I{openssl_dir}/include" variables["CGO_LDFLAGS"] = f"-L{bls_dir}/lib -L{openssl_dir}/lib" variables["LD_LIBRARY_PATH"] = f"{bls_dir}/lib:{mcl_dir}/lib:{openssl_dir}/lib" variables["DYLD_FALLBACK_LIBRARY_PATH"] = variables["LD_LIBRARY_PATH"] diff --git a/pyhmy/validator.py b/pyhmy/validator.py index ccc2b14..c2178fc 100644 --- a/pyhmy/validator.py +++ b/pyhmy/validator.py @@ -1,44 +1,27 @@ import json -from eth_account.datastructures import ( - SignedTransaction -) +from eth_account.datastructures import SignedTransaction -from decimal import ( - Decimal, - InvalidOperation -) +from decimal import Decimal, InvalidOperation -from .account import ( - get_balance, - is_valid_address -) +from .account import get_balance, is_valid_address -from .numbers import ( - convert_one_to_atto -) +from .numbers import convert_one_to_atto from .exceptions import ( InvalidValidatorError, RPCError, RequestsError, - RequestsTimeoutError + RequestsTimeoutError, ) -from .staking import ( - get_all_validator_addresses, - get_validator_information -) +from .staking import get_all_validator_addresses, get_validator_information -from .staking_structures import ( - Directive -) +from .staking_structures import Directive -from .staking_signing import ( - sign_staking_transaction -) +from .staking_signing import sign_staking_transaction -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 # TODO: Add unit testing @@ -49,13 +32,13 @@ class Validator: website_char_limit = 140 security_contact_char_limit = 140 details_char_limit = 280 - min_required_delegation = convert_one_to_atto(10000) # in ATTO + min_required_delegation = convert_one_to_atto(10000) # in ATTO def __init__(self, address): if not isinstance(address, str): - raise InvalidValidatorError(1, 'given ONE address was not a string') + raise InvalidValidatorError(1, "given ONE address was not a string") if not is_valid_address(address): - raise InvalidValidatorError(1, f'{address} is not valid ONE address') + raise InvalidValidatorError(1, f"{address} is not valid ONE address") self._address = address self._bls_keys = [] self._bls_key_sigs = [] @@ -84,8 +67,11 @@ class Validator: """ if check_str: if not isinstance(data, str): - raise InvalidValidatorError(3, f'Expected data to be string to avoid floating point precision issues but got {data}') - return '' if not data else str(data) + raise InvalidValidatorError( + 3, + f"Expected data to be string to avoid floating point precision issues but got {data}", + ) + return "" if not data else str(data) def __str__(self) -> str: """ @@ -98,7 +84,7 @@ class Validator: return json.dumps(info) def __repr__(self) -> str: - return f'' + return f"" def get_address(self) -> str: """ @@ -209,7 +195,9 @@ class Validator: """ name = self._sanitize_input(name) if len(name) > self.name_char_limit: - raise InvalidValidatorError(3, f'Name must be less than {self.name_char_limit} characters') + raise InvalidValidatorError( + 3, f"Name must be less than {self.name_char_limit} characters" + ) self._name = name def get_name(self) -> str: @@ -239,7 +227,9 @@ class Validator: """ identity = self._sanitize_input(identity) if len(identity) > self.identity_char_limit: - raise InvalidValidatorError(3, f'Identity must be less than {self.identity_char_limit} characters') + raise InvalidValidatorError( + 3, f"Identity must be less than {self.identity_char_limit} characters" + ) self._identity = identity def get_identity(self) -> str: @@ -269,7 +259,9 @@ class Validator: """ website = self._sanitize_input(website) if len(website) > self.website_char_limit: - raise InvalidValidatorError(3, f'Website must be less than {self.website_char_limit} characters') + raise InvalidValidatorError( + 3, f"Website must be less than {self.website_char_limit} characters" + ) self._website = website def get_website(self) -> str: @@ -299,7 +291,10 @@ class Validator: """ contact = self._sanitize_input(contact) if len(contact) > self.security_contact_char_limit: - raise InvalidValidatorError(3, f'Security contact must be less than {self.security_contact_char_limit} characters') + raise InvalidValidatorError( + 3, + f"Security contact must be less than {self.security_contact_char_limit} characters", + ) self._security_contact = contact def get_security_contact(self) -> str: @@ -329,7 +324,9 @@ class Validator: """ details = self._sanitize_input(details) if len(details) > self.details_char_limit: - raise InvalidValidatorError(3, f'Details must be less than {self.details_char_limit} characters') + raise InvalidValidatorError( + 3, f"Details must be less than {self.details_char_limit} characters" + ) self._details = details def get_details(self) -> str: @@ -361,9 +358,14 @@ class Validator: try: delegation = Decimal(delegation) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Min self delegation must be a number') from e + raise InvalidValidatorError( + 3, "Min self delegation must be a number" + ) from e if delegation < self.min_required_delegation: - raise InvalidValidatorError(3, f'Min self delegation must be greater than {self.min_required_delegation} ATTO') + raise InvalidValidatorError( + 3, + f"Min self delegation must be greater than {self.min_required_delegation} ATTO", + ) self._min_self_delegation = delegation def get_min_self_delegation(self) -> Decimal: @@ -395,13 +397,20 @@ class Validator: try: max_delegation = Decimal(max_delegation) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Max total delegation must be a number') from e + raise InvalidValidatorError( + 3, "Max total delegation must be a number" + ) from e if self._min_self_delegation: if max_delegation < self._min_self_delegation: - raise InvalidValidatorError(3, f'Max total delegation must be greater than min self delegation: ' - '{self._min_self_delegation}') + raise InvalidValidatorError( + 3, + f"Max total delegation must be greater than min self delegation: " + "{self._min_self_delegation}", + ) else: - raise InvalidValidatorError(4, 'Min self delegation must be set before max total delegation') + raise InvalidValidatorError( + 4, "Min self delegation must be set before max total delegation" + ) self._max_total_delegation = max_delegation def get_max_total_delegation(self) -> Decimal: @@ -433,19 +442,29 @@ class Validator: try: amount = Decimal(amount) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Amount must be a number') from e + raise InvalidValidatorError(3, "Amount must be a number") from e if self._min_self_delegation: if amount < self._min_self_delegation: - raise InvalidValidatorError(3, 'Amount must be greater than min self delegation: ' - f'{self._min_self_delegation}') + raise InvalidValidatorError( + 3, + "Amount must be greater than min self delegation: " + f"{self._min_self_delegation}", + ) else: - raise InvalidValidatorError(4, 'Min self delegation must be set before amount') + raise InvalidValidatorError( + 4, "Min self delegation must be set before amount" + ) if self._max_total_delegation: if amount > self._max_total_delegation: - raise InvalidValidatorError(3, 'Amount must be less than max total delegation: ' - f'{self._max_total_delegation}') + raise InvalidValidatorError( + 3, + "Amount must be less than max total delegation: " + f"{self._max_total_delegation}", + ) else: - raise InvalidValidatorError(4, 'Max total delegation must be set before amount') + raise InvalidValidatorError( + 4, "Max total delegation must be set before amount" + ) self._inital_delegation = amount def get_amount(self) -> Decimal: @@ -477,9 +496,9 @@ class Validator: try: rate = Decimal(rate) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Max rate must be a number') from e + raise InvalidValidatorError(3, "Max rate must be a number") from e if rate < 0 or rate > 1: - raise InvalidValidatorError(3, 'Max rate must be between 0 and 1') + raise InvalidValidatorError(3, "Max rate must be between 0 and 1") self._max_rate = rate def get_max_rate(self) -> Decimal: @@ -511,14 +530,21 @@ class Validator: try: rate = Decimal(rate) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Max change rate must be a number') from e + raise InvalidValidatorError(3, "Max change rate must be a number") from e if rate < 0: - raise InvalidValidatorError(3, 'Max change rate must be greater than or equal to 0') + raise InvalidValidatorError( + 3, "Max change rate must be greater than or equal to 0" + ) if self._max_rate: if rate > self._max_rate: - raise InvalidValidatorError(3, f'Max change rate must be less than or equal to max rate: {self._max_rate}') + raise InvalidValidatorError( + 3, + f"Max change rate must be less than or equal to max rate: {self._max_rate}", + ) else: - raise InvalidValidatorError(4, 'Max rate must be set before max change rate') + raise InvalidValidatorError( + 4, "Max rate must be set before max change rate" + ) self._max_change_rate = rate def get_max_change_rate(self) -> Decimal: @@ -550,14 +576,16 @@ class Validator: try: rate = Decimal(rate) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Rate must be a number') from e + raise InvalidValidatorError(3, "Rate must be a number") from e if rate < 0: - raise InvalidValidatorError(3, 'Rate must be greater than or equal to 0') + raise InvalidValidatorError(3, "Rate must be greater than or equal to 0") if self._max_rate: if rate > self._max_rate: - raise InvalidValidatorError(3, f'Rate must be less than or equal to max rate: {self._max_rate}') + raise InvalidValidatorError( + 3, f"Rate must be less than or equal to max rate: {self._max_rate}" + ) else: - raise InvalidValidatorError(4, 'Max rate must be set before rate') + raise InvalidValidatorError(4, "Max rate must be set before rate") self._rate = rate def get_rate(self) -> Decimal: @@ -571,7 +599,9 @@ class Validator: """ return self._rate - def does_validator_exist(self, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + def does_validator_exist( + self, endpoint=_default_endpoint, timeout=_default_timeout + ) -> bool: """ Check if validator exists on blockchain @@ -629,31 +659,33 @@ class Validator: If input value is invalid """ try: - self.set_name(info['name']) - self.set_identity(info['identity']) - self.set_website(info['website']) - self.set_details(info['details']) - self.set_security_contact(info['security-contact']) + self.set_name(info["name"]) + self.set_identity(info["identity"]) + self.set_website(info["website"]) + self.set_details(info["details"]) + self.set_security_contact(info["security-contact"]) - self.set_min_self_delegation(info['min-self-delegation']) - self.set_max_total_delegation(info['max-total-delegation']) - self.set_amount(info['amount']) + self.set_min_self_delegation(info["min-self-delegation"]) + self.set_max_total_delegation(info["max-total-delegation"]) + self.set_amount(info["amount"]) - self.set_max_rate(info['max-rate']) - self.set_max_change_rate(info['max-change-rate']) - self.set_rate(info['rate']) + self.set_max_rate(info["max-rate"]) + self.set_max_change_rate(info["max-change-rate"]) + self.set_rate(info["rate"]) self._bls_keys = [] - for key in info['bls-public-keys']: + for key in info["bls-public-keys"]: self.add_bls_key(key) self._bls_key_sigs = [] - for key in info['bls-key-sigs']: + for key in info["bls-key-sigs"]: self.add_bls_key_sig(key) except KeyError as e: - raise InvalidValidatorError(3, 'Info has missing key') from e + raise InvalidValidatorError(3, "Info has missing key") from e - def load_from_blockchain(self, endpoint=_default_endpoint, timeout=_default_timeout): + def load_from_blockchain( + self, endpoint=_default_endpoint, timeout=_default_timeout + ): """ Import validator information from blockchain with given address At the moment, this is unable to fetch the BLS Signature, which is not implemented @@ -673,33 +705,43 @@ class Validator: """ try: if not self.does_validator_exist(endpoint, timeout): - raise InvalidValidatorError(5, f'Validator does not exist on chain according to {endpoint}') + raise InvalidValidatorError( + 5, f"Validator does not exist on chain according to {endpoint}" + ) except (RPCError, RequestsError, RequestsTimeoutError) as e: - raise InvalidValidatorError(5, 'Error requesting validator information') from e + raise InvalidValidatorError( + 5, "Error requesting validator information" + ) from e try: validator_info = get_validator_information(self._address, endpoint, timeout) except (RPCError, RequestsError, RequestsTimeoutError) as e: - raise InvalidValidatorError(5, 'Error requesting validator information') from e + raise InvalidValidatorError( + 5, "Error requesting validator information" + ) from e # Skip additional sanity checks when importing from chain try: - info = validator_info['validator'] - self._name = info['name'] - self._identity = info['identity'] - self._website = info['website'] - self._details = info['details'] - self._security_contact = info['security-contact'] - - self._min_self_delegation = info['min-self-delegation'] - self._max_total_delegation = info['max-total-delegation'] - self._inital_delegation = self._min_self_delegation # Since validator exists, set initial delegation to 0 - - self._max_rate = Decimal(info['max-rate']) - self._max_change_rate = Decimal(info['max-change-rate']) - self._rate = Decimal(info['rate']) - self._bls_keys = info[ 'bls-public-keys' ] + info = validator_info["validator"] + self._name = info["name"] + self._identity = info["identity"] + self._website = info["website"] + self._details = info["details"] + self._security_contact = info["security-contact"] + + self._min_self_delegation = info["min-self-delegation"] + self._max_total_delegation = info["max-total-delegation"] + self._inital_delegation = ( + self._min_self_delegation + ) # Since validator exists, set initial delegation to 0 + + self._max_rate = Decimal(info["max-rate"]) + self._max_change_rate = Decimal(info["max-change-rate"]) + self._rate = Decimal(info["rate"]) + self._bls_keys = info["bls-public-keys"] except KeyError as e: - raise InvalidValidatorError(5, 'Error importing validator information from RPC result') from e + raise InvalidValidatorError( + 5, "Error importing validator information from RPC result" + ) from e def export(self) -> dict: """ @@ -724,11 +766,13 @@ class Validator: "max-rate": self._max_rate, "max-change-rate": self._max_change_rate, "bls-public-keys": self._bls_keys, - "bls-key-sigs": self._bls_key_sigs + "bls-key-sigs": self._bls_key_sigs, } return info - def sign_create_validator_transaction(self, nonce, gas_price, gas_limit, private_key, chain_id=None) -> SignedTransaction: + def sign_create_validator_transaction( + self, nonce, gas_price, gas_limit, private_key, chain_id=None + ) -> SignedTransaction: """ Create but not post a transaction to Create the Validator using private_key @@ -746,16 +790,27 @@ class Validator: https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L413 """ info = self.export().copy() - info['directive'] = Directive.CreateValidator - info['validatorAddress'] = info.pop('validator-addr') # change the key - info['nonce'] = nonce - info['gasPrice'] = gas_price - info['gasLimit'] = gas_limit + info["directive"] = Directive.CreateValidator + info["validatorAddress"] = info.pop("validator-addr") # change the key + info["nonce"] = nonce + info["gasPrice"] = gas_price + info["gasLimit"] = gas_limit if chain_id: - info['chainId'] = chain_id + info["chainId"] = chain_id return sign_staking_transaction(info, private_key) - def sign_edit_validator_transaction(self, nonce, gas_price, gas_limit, rate, bls_key_to_remove, bls_key_to_add, bls_key_to_add_sig, private_key, chain_id=None) -> SignedTransaction: + def sign_edit_validator_transaction( + self, + nonce, + gas_price, + gas_limit, + rate, + bls_key_to_remove, + bls_key_to_add, + bls_key_to_add_sig, + private_key, + chain_id=None, + ) -> SignedTransaction: """ Create but not post a transaction to Edit the Validator using private_key @@ -776,18 +831,18 @@ class Validator: self.add_bls_key(bls_key_to_add) self.remove_bls_key(bls_key_to_remove) info = self.export().copy() - info['directive'] = Directive.EditValidator - info['validatorAddress'] = info.pop('validator-addr') # change the key - info['nonce'] = nonce - info['gasPrice'] = gas_price - info['gasLimit'] = gas_limit - _ = info.pop('max-rate') # not needed - _ = info.pop('max-change-rate') # not needed - _ = info.pop('bls-public-keys') # remove this list - _ = info.pop('amount') # also unused - info['bls-key-to-remove'] = bls_key_to_remove - info['bls-key-to-add'] = bls_key_to_add - info['bls-key-to-add-sig'] = bls_key_to_add_sig + info["directive"] = Directive.EditValidator + info["validatorAddress"] = info.pop("validator-addr") # change the key + info["nonce"] = nonce + info["gasPrice"] = gas_price + info["gasLimit"] = gas_limit + _ = info.pop("max-rate") # not needed + _ = info.pop("max-change-rate") # not needed + _ = info.pop("bls-public-keys") # remove this list + _ = info.pop("amount") # also unused + info["bls-key-to-remove"] = bls_key_to_remove + info["bls-key-to-add"] = bls_key_to_add + info["bls-key-to-add-sig"] = bls_key_to_add_sig if chain_id: - info['chainId'] = chain_id + info["chainId"] = chain_id return sign_staking_transaction(info, private_key) diff --git a/tests/bech32-pyhmy/test_bech32.py b/tests/bech32-pyhmy/test_bech32.py index 2d58938..e92202f 100644 --- a/tests/bech32-pyhmy/test_bech32.py +++ b/tests/bech32-pyhmy/test_bech32.py @@ -1,10 +1,9 @@ -from pyhmy.bech32 import ( - bech32 -) +from pyhmy.bech32 import bech32 + def test_encode(): - bech32.encode('one', 5, [121, 161]) + bech32.encode("one", 5, [121, 161]) -def test_decode(): - bech32.decode('one', 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') +def test_decode(): + bech32.decode("one", "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") diff --git a/tests/logging-pyhmy/test_logging.py b/tests/logging-pyhmy/test_logging.py index 018a66e..f15796d 100644 --- a/tests/logging-pyhmy/test_logging.py +++ b/tests/logging-pyhmy/test_logging.py @@ -14,11 +14,11 @@ def test_basic_logger(): logger.debug("test debug") logger.error("test error") logger.warning("test warning") - with open(f"{os.getcwd()}/logs/pytest.log", 'r') as f: + with open(f"{os.getcwd()}/logs/pytest.log", "r") as f: log_file_contents = f.readlines() assert not log_file_contents logger.write() - with open(f"{os.getcwd()}/logs/pytest.log", 'r') as f: + with open(f"{os.getcwd()}/logs/pytest.log", "r") as f: log_file_contents = f.readlines() for line in log_file_contents: if "INFO" in line: diff --git a/tests/numbers-pyhmy/test_numbers.py b/tests/numbers-pyhmy/test_numbers.py index 02fad81..b8d277a 100644 --- a/tests/numbers-pyhmy/test_numbers.py +++ b/tests/numbers-pyhmy/test_numbers.py @@ -1,8 +1,6 @@ from decimal import Decimal -from pyhmy import ( - numbers -) +from pyhmy import numbers def test_convert_atto_to_one(): @@ -12,12 +10,13 @@ def test_convert_atto_to_one(): b = numbers.convert_atto_to_one(1e18 + 0.6) assert Decimal(1) == b - c = numbers.convert_atto_to_one('1' + ('0' * 18)) + c = numbers.convert_atto_to_one("1" + ("0" * 18)) assert Decimal(1) == c d = numbers.convert_atto_to_one(Decimal(1e18)) assert Decimal(1) == d + def test_convert_one_to_atto(): a = numbers.convert_one_to_atto(1e-18) assert Decimal(1) == a @@ -25,7 +24,7 @@ def test_convert_one_to_atto(): b = numbers.convert_one_to_atto(1.5) assert Decimal(1.5e18) == b - c = numbers.convert_one_to_atto('1') + c = numbers.convert_one_to_atto("1") assert Decimal(1e18) == c d = numbers.convert_one_to_atto(Decimal(1)) diff --git a/tests/request-pyhmy/test_request.py b/tests/request-pyhmy/test_request.py index 19acdeb..4505069 100644 --- a/tests/request-pyhmy/test_request.py +++ b/tests/request-pyhmy/test_request.py @@ -4,31 +4,27 @@ import socket import pytest import requests -from pyhmy.rpc import ( - exceptions, - request -) +from pyhmy.rpc import exceptions, request @pytest.fixture(scope="session", autouse=True) def setup(): - endpoint = 'http://localhost:9500' + endpoint = "http://localhost:9500" timeout = 30 - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" params = [] - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": method, - "params": params - } - headers = { - 'Content-Type': 'application/json' - } + payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} + headers = {"Content-Type": "application/json"} try: - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) except Exception as e: pytest.skip("can not connect to local blockchain", allow_module_level=True) @@ -36,16 +32,18 @@ def setup(): def test_request_connection_error(): # Find available port s = socket.socket() - s.bind(('localhost', 0)) + s.bind(("localhost", 0)) port = s.getsockname()[1] s.close() if port == 0: pytest.skip("could not find available port") - bad_endpoint = f'http://localhost:{port}' + bad_endpoint = f"http://localhost:{port}" bad_request = None try: - bad_request = request.rpc_request('hmyv2_getNodeMetadata', endpoint=bad_endpoint) + bad_request = request.rpc_request( + "hmyv2_getNodeMetadata", endpoint=bad_endpoint + ) except Exception as e: assert isinstance(e, exceptions.RequestsError) assert bad_request is None @@ -54,7 +52,7 @@ def test_request_connection_error(): def test_request_rpc_error(): error_request = None try: - error_request = request.rpc_request('hmyv2_getBalance') + error_request = request.rpc_request("hmyv2_getBalance") except (exceptions.RequestsTimeoutError, exceptions.RequestsError) as err: pytest.skip("can not connect to local blockchain", allow_module_level=True) except Exception as e: @@ -63,24 +61,23 @@ def test_request_rpc_error(): def test_rpc_request(): - endpoint = 'http://localhost:9500' + endpoint = "http://localhost:9500" timeout = 30 - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" params = [] - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": method, - "params": params - } - headers = { - 'Content-Type': 'application/json' - } + payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} + headers = {"Content-Type": "application/json"} response = None try: - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) except: pytest.skip("can not connect to local blockchain") assert response is not None @@ -89,14 +86,14 @@ def test_rpc_request(): try: resp = json.loads(response.content) except json.decoder.JSONDecodeError as err: - pytest.skip('unable to decode response') + pytest.skip("unable to decode response") assert resp is not None rpc_response = None try: rpc_response = request.rpc_request(method, params, endpoint, timeout) except exceptions.RPCError as e: - assert 'error' in resp + assert "error" in resp if rpc_response is not None: assert rpc_response == resp diff --git a/tests/sdk-pyhmy/conftest.py b/tests/sdk-pyhmy/conftest.py index 5767b6c..d9f3af3 100644 --- a/tests/sdk-pyhmy/conftest.py +++ b/tests/sdk-pyhmy/conftest.py @@ -9,11 +9,9 @@ import requests # 1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc / one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 (genesis) # 3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65 / one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (transferred 503) -endpoint = 'http://localhost:9500' +endpoint = "http://localhost:9500" timeout = 30 -headers = { - 'Content-Type': 'application/json' -} +headers = {"Content-Type": "application/json"} txs = [ # same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce) "0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3", @@ -37,8 +35,9 @@ stx_hashes = [ "0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968", "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f", ] -assert len( txs ) == len( tx_hashes ), "Mismatch in tx and tx_hash count" -assert len( stxs ) == len( stx_hashes ), "Mismatch in stx and stx_hash count" +assert len(txs) == len(tx_hashes), "Mismatch in tx and tx_hash count" +assert len(stxs) == len(stx_hashes), "Mismatch in stx and stx_hash count" + @pytest.fixture(scope="session", autouse=True) def setup_blockchain(): @@ -47,38 +46,61 @@ def setup_blockchain(): metadata = _check_connection() _check_staking_epoch(metadata) - for i in range( len( txs ) ): - tx = txs[ i ] - tx_hash = tx_hashes[ i ] - _send_transaction( tx, endpoint ) - if not _wait_for_transaction_confirmed( tx_hash, endpoint ): - pytest.skip("Could not confirm initial transaction #{} on chain".format( i ), allow_module_level = True) - - for i in range( len( stxs ) ): - stx = stxs[ i ] - stx_hash = stx_hashes[ i ] - _send_staking_transaction( stx, endpoint ) - if not _wait_for_staking_transaction_confirmed( stx_hash, endpoint ): - pytest.skip("Could not confirm initial staking transaction #{} on chain".format( i ), allow_module_level = True) + for i in range(len(txs)): + tx = txs[i] + tx_hash = tx_hashes[i] + _send_transaction(tx, endpoint) + if not _wait_for_transaction_confirmed(tx_hash, endpoint): + pytest.skip( + "Could not confirm initial transaction #{} on chain".format(i), + allow_module_level=True, + ) + + for i in range(len(stxs)): + stx = stxs[i] + stx_hash = stx_hashes[i] + _send_staking_transaction(stx, endpoint) + if not _wait_for_staking_transaction_confirmed(stx_hash, endpoint): + pytest.skip( + "Could not confirm initial staking transaction #{} on chain".format(i), + allow_module_level=True, + ) + def _check_connection(): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_getNodeMetadata', - "params": [] + "method": "hmyv2_getNodeMetadata", + "params": [], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) metadata = json.loads(response.content) - if 'error' in metadata: - pytest.skip(f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}", allow_module_level=True) - if 'chain-config' not in metadata['result']: - pytest.skip("Chain config not found in hmyv2_getNodeMetadata reply", allow_module_level=True) + if "error" in metadata: + pytest.skip( + f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}", + allow_module_level=True, + ) + if "chain-config" not in metadata["result"]: + pytest.skip( + "Chain config not found in hmyv2_getNodeMetadata reply", + allow_module_level=True, + ) return metadata except Exception as e: - pytest.skip('Can not connect to local blockchain or bad hmyv2_getNodeMetadata reply', allow_module_level=True) + pytest.skip( + "Can not connect to local blockchain or bad hmyv2_getNodeMetadata reply", + allow_module_level=True, + ) + def _check_staking_epoch(metadata): latest_header = None @@ -86,105 +108,163 @@ def _check_staking_epoch(metadata): payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_latestHeader', - "params": [] + "method": "hmyv2_latestHeader", + "params": [], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) latest_header = json.loads(response.content) - if 'error' in latest_header: - pytest.skip(f"Error in hmyv2_latestHeader reply: {latest_header['error']}", allow_module_level=True) + if "error" in latest_header: + pytest.skip( + f"Error in hmyv2_latestHeader reply: {latest_header['error']}", + allow_module_level=True, + ) except Exception as e: - pytest.skip('Failed to get hmyv2_latestHeader reply', allow_module_level=True) + pytest.skip("Failed to get hmyv2_latestHeader reply", allow_module_level=True) if metadata and latest_header: - staking_epoch = metadata['result']['chain-config']['staking-epoch'] - current_epoch = latest_header['result']['epoch'] + staking_epoch = metadata["result"]["chain-config"]["staking-epoch"] + current_epoch = latest_header["result"]["epoch"] if staking_epoch > current_epoch: - pytest.skip(f'Not staking epoch: current {current_epoch}, staking {staking_epoch}', allow_module_level=True) + pytest.skip( + f"Not staking epoch: current {current_epoch}, staking {staking_epoch}", + allow_module_level=True, + ) + def _send_transaction(raw_tx, endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_sendRawTransaction', - "params": [raw_tx] + "method": "hmyv2_sendRawTransaction", + "params": [raw_tx], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) tx = json.loads(response.content) - if 'error' in tx: - pytest.skip(f"Error in hmyv2_sendRawTransaction reply: {tx['error']}", allow_module_level=True) + if "error" in tx: + pytest.skip( + f"Error in hmyv2_sendRawTransaction reply: {tx['error']}", + allow_module_level=True, + ) except Exception as e: - pytest.skip('Failed to get hmyv2_sendRawTransaction reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_sendRawTransaction reply", allow_module_level=True + ) + def _check_transaction(tx_hash, endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_getTransactionByHash', - "params": [tx_hash] + "method": "hmyv2_getTransactionByHash", + "params": [tx_hash], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) tx_data = json.loads(response.content) return tx_data except Exception as e: - pytest.skip('Failed to get hmyv2_getTransactionByHash reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_getTransactionByHash reply", allow_module_level=True + ) + -def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout = 30): +def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout=30): start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_data = _check_transaction(tx_hash, endpoint) if tx_data is not None: - block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_data["result"].get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": return True time.sleep(random.uniform(0.2, 0.5)) return False -def _send_staking_transaction(raw_tx, endpoint = endpoint): + +def _send_staking_transaction(raw_tx, endpoint=endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_sendRawStakingTransaction', - "params": [raw_tx] + "method": "hmyv2_sendRawStakingTransaction", + "params": [raw_tx], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) staking_tx = json.loads(response.content) - if 'error' in staking_tx: - pytest.skip(f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}", allow_module_level=True) + if "error" in staking_tx: + pytest.skip( + f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}", + allow_module_level=True, + ) except Exception as e: - pytest.skip('Failed to get hmyv2_sendRawStakingTransaction reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_sendRawStakingTransaction reply", + allow_module_level=True, + ) -def _check_staking_transaction(stx_hash, endpoint = endpoint): + +def _check_staking_transaction(stx_hash, endpoint=endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_getStakingTransactionByHash', - "params": [stx_hash] + "method": "hmyv2_getStakingTransactionByHash", + "params": [stx_hash], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) stx_data = json.loads(response.content) return stx_data except Exception as e: - pytest.skip('Failed to get hmyv2_getStakingTransactionByHash reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_getStakingTransactionByHash reply", + allow_module_level=True, + ) + -def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout = 30): +def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout=30): answer = False start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_data = _check_staking_transaction(tx_hash, endpoint) if tx_data is not None: - block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_data["result"].get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": answer = True time.sleep(random.uniform(0.2, 0.5)) diff --git a/tests/sdk-pyhmy/test_account.py b/tests/sdk-pyhmy/test_account.py index 1c1aa4a..b6f0ed5 100644 --- a/tests/sdk-pyhmy/test_account.py +++ b/tests/sdk-pyhmy/test_account.py @@ -1,33 +1,32 @@ import pytest import requests -from pyhmy import ( - account -) +from pyhmy import account -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -explorer_endpoint = 'http://localhost:9700' -endpoint_shard_one = 'http://localhost:9502' -local_test_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' +explorer_endpoint = "http://localhost:9700" +endpoint_shard_one = "http://localhost:9502" +local_test_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" test_validator_address = local_test_address genesis_block_number = 0 test_block_number = 1 -fake_shard = 'http://example.com' +fake_shard = "http://example.com" + def _test_account_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response @@ -36,69 +35,102 @@ def test_get_balance(setup_blockchain): assert isinstance(balance, int) assert balance > 0 + def test_get_balance_by_block(setup_blockchain): - balance = _test_account_rpc(account.get_balance_by_block, local_test_address, genesis_block_number) + balance = _test_account_rpc( + account.get_balance_by_block, local_test_address, genesis_block_number + ) assert isinstance(balance, int) assert balance > 0 + def test_get_account_nonce(setup_blockchain): - true_nonce = _test_account_rpc(account.get_account_nonce, local_test_address, test_block_number, endpoint=endpoint_shard_one) + true_nonce = _test_account_rpc( + account.get_account_nonce, + local_test_address, + test_block_number, + endpoint=endpoint_shard_one, + ) assert isinstance(true_nonce, int) + def test_get_transaction_history(setup_blockchain): - tx_history = _test_account_rpc(account.get_transaction_history, local_test_address, endpoint=explorer_endpoint) + tx_history = _test_account_rpc( + account.get_transaction_history, local_test_address, endpoint=explorer_endpoint + ) assert isinstance(tx_history, list) assert len(tx_history) >= 0 + def test_get_staking_transaction_history(setup_blockchain): - staking_tx_history = _test_account_rpc(account.get_staking_transaction_history, test_validator_address, endpoint=explorer_endpoint) + staking_tx_history = _test_account_rpc( + account.get_staking_transaction_history, + test_validator_address, + endpoint=explorer_endpoint, + ) assert isinstance(staking_tx_history, list) assert len(staking_tx_history) > 0 + def test_get_balance_on_all_shards(setup_blockchain): balances = _test_account_rpc(account.get_balance_on_all_shards, local_test_address) assert isinstance(balances, list) assert len(balances) == 2 + def test_get_total_balance(setup_blockchain): total_balance = _test_account_rpc(account.get_total_balance, local_test_address) assert isinstance(total_balance, int) assert total_balance > 0 + def test_is_valid_address(): - assert account.is_valid_address('one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur') - assert not account.is_valid_address('one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0') + assert account.is_valid_address("one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur") + assert not account.is_valid_address("one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0") + def test_get_transaction_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_transaction_count, local_test_address, 'latest', explorer_endpoint) + tx_count = _test_account_rpc( + account.get_transaction_count, local_test_address, "latest", explorer_endpoint + ) assert isinstance(tx_count, int) assert tx_count > 0 + def test_get_transactions_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_transactions_count, local_test_address, 'ALL', explorer_endpoint) + tx_count = _test_account_rpc( + account.get_transactions_count, local_test_address, "ALL", explorer_endpoint + ) + def test_get_staking_transactions_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_staking_transactions_count, local_test_address, 'ALL', explorer_endpoint) + tx_count = _test_account_rpc( + account.get_staking_transactions_count, + local_test_address, + "ALL", + explorer_endpoint, + ) assert isinstance(tx_count, int) + def test_errors(): with pytest.raises(exceptions.RPCError): - account.get_balance('', fake_shard) + account.get_balance("", fake_shard) with pytest.raises(exceptions.RPCError): - account.get_balance_by_block('', 1, fake_shard) + account.get_balance_by_block("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_account_nonce('', 1, fake_shard) + account.get_account_nonce("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transaction_count('', 1, fake_shard) + account.get_transaction_count("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transactions_count('', 1, fake_shard) + account.get_transactions_count("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transactions_count('', 'ALL', fake_shard) + account.get_transactions_count("", "ALL", fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transaction_history('', endpoint=fake_shard) + account.get_transaction_history("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - account.get_staking_transaction_history('', endpoint=fake_shard) + account.get_staking_transaction_history("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - account.get_balance_on_all_shards('', endpoint=fake_shard) + account.get_balance_on_all_shards("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - account.get_total_balance('', endpoint=fake_shard) + account.get_total_balance("", endpoint=fake_shard) diff --git a/tests/sdk-pyhmy/test_blockchain.py b/tests/sdk-pyhmy/test_blockchain.py index 4d5efc8..327a97e 100644 --- a/tests/sdk-pyhmy/test_blockchain.py +++ b/tests/sdk-pyhmy/test_blockchain.py @@ -1,211 +1,264 @@ import pytest import requests -from pyhmy import ( - blockchain -) +from pyhmy import blockchain -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions test_epoch_number = 0 genesis_block_number = 0 test_block_number = 1 test_block_hash = None -fake_shard = 'http://example.com' -address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' +fake_shard = "http://example.com" +address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" + def _test_blockchain_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_node_metadata(setup_blockchain): metadata = _test_blockchain_rpc(blockchain.get_node_metadata) assert isinstance(metadata, dict) + def test_get_sharding_structure(setup_blockchain): sharding_structure = _test_blockchain_rpc(blockchain.get_sharding_structure) assert isinstance(sharding_structure, list) assert len(sharding_structure) > 0 + def test_get_leader_address(setup_blockchain): leader = _test_blockchain_rpc(blockchain.get_leader_address) assert isinstance(leader, str) - assert 'one1' in leader + assert "one1" in leader + def test_get_block_number(setup_blockchain): current_block_number = _test_blockchain_rpc(blockchain.get_block_number) assert isinstance(current_block_number, int) + def test_get_current_epoch(setup_blockchain): current_epoch = _test_blockchain_rpc(blockchain.get_current_epoch) assert isinstance(current_epoch, int) + def tset_get_gas_price(setup_blockchain): gas = _test_blockchain_rpc(blockchain.get_gas_price) assert isinstance(gas, int) + def test_get_num_peers(setup_blockchain): peers = _test_blockchain_rpc(blockchain.get_num_peers) assert isinstance(peers, int) + def test_get_latest_header(setup_blockchain): header = _test_blockchain_rpc(blockchain.get_latest_header) assert isinstance(header, dict) + def test_get_latest_chain_headers(setup_blockchain): header_pair = _test_blockchain_rpc(blockchain.get_latest_chain_headers) assert isinstance(header_pair, dict) + def test_get_block_by_number(setup_blockchain): global test_block_hash block = _test_blockchain_rpc(blockchain.get_block_by_number, test_block_number) assert isinstance(block, dict) - assert 'hash' in block.keys() - test_block_hash = block['hash'] + assert "hash" in block.keys() + test_block_hash = block["hash"] + def test_get_block_by_hash(setup_blockchain): if not test_block_hash: - pytest.skip('Failed to get reference block hash') + pytest.skip("Failed to get reference block hash") block = _test_blockchain_rpc(blockchain.get_block_by_hash, test_block_hash) assert isinstance(block, dict) + def test_get_block_transaction_count_by_number(setup_blockchain): - tx_count = _test_blockchain_rpc(blockchain.get_block_transaction_count_by_number, test_block_number) + tx_count = _test_blockchain_rpc( + blockchain.get_block_transaction_count_by_number, test_block_number + ) assert isinstance(tx_count, int) + def test_get_block_transaction_count_by_hash(setup_blockchain): if not test_block_hash: - pytest.skip('Failed to get reference block hash') - tx_count = _test_blockchain_rpc(blockchain.get_block_transaction_count_by_hash, test_block_hash) + pytest.skip("Failed to get reference block hash") + tx_count = _test_blockchain_rpc( + blockchain.get_block_transaction_count_by_hash, test_block_hash + ) assert isinstance(tx_count, int) + def test_get_blocks(setup_blockchain): - blocks = _test_blockchain_rpc(blockchain.get_blocks, genesis_block_number, test_block_number) + blocks = _test_blockchain_rpc( + blockchain.get_blocks, genesis_block_number, test_block_number + ) assert isinstance(blocks, list) assert len(blocks) == (test_block_number - genesis_block_number + 1) + def test_get_block_signers(setup_blockchain): - block_signers = _test_blockchain_rpc(blockchain.get_block_signers, test_block_number) + block_signers = _test_blockchain_rpc( + blockchain.get_block_signers, test_block_number + ) assert isinstance(block_signers, list) assert len(block_signers) > 0 + def test_get_validators(setup_blockchain): validators = _test_blockchain_rpc(blockchain.get_validators, test_epoch_number) assert isinstance(validators, dict) - assert 'validators' in validators.keys() - assert len(validators['validators']) > 0 + assert "validators" in validators.keys() + assert len(validators["validators"]) > 0 + def test_get_shard(setup_blockchain): shard = _test_blockchain_rpc(blockchain.get_shard) assert isinstance(shard, int) assert shard == 0 + def test_get_staking_epoch(setup_blockchain): staking_epoch = _test_blockchain_rpc(blockchain.get_staking_epoch) assert isinstance(staking_epoch, int) + def test_get_prestaking_epoch(setup_blockchain): prestaking_epoch = _test_blockchain_rpc(blockchain.get_prestaking_epoch) assert isinstance(prestaking_epoch, int) + def test_get_bad_blocks(setup_blockchain): # TODO: Remove skip when RPC is fixed pytest.skip("Known error with hmyv2_getCurrentBadBlocks") bad_blocks = _test_blockchain_rpc(blockchain.get_bad_blocks) assert isinstance(bad_blocks, list) + def test_get_validator_keys(setup_blockchain): keys = _test_blockchain_rpc(blockchain.get_validator_keys, test_epoch_number) assert isinstance(keys, list) assert len(keys) > 0 + def test_get_block_signers_keys(setup_blockchain): keys = _test_blockchain_rpc(blockchain.get_block_signers_keys, test_block_number) assert isinstance(keys, list) assert len(keys) > 0 + def test_chain_id(setup_blockchain): chain_id = _test_blockchain_rpc(blockchain.chain_id) assert isinstance(chain_id, int) + def test_get_peer_info(setup_blockchain): peer_info = _test_blockchain_rpc(blockchain.get_peer_info) assert isinstance(peer_info, dict) + def test_protocol_version(setup_blockchain): protocol_version = _test_blockchain_rpc(blockchain.protocol_version) assert isinstance(protocol_version, int) + def test_is_last_block(setup_blockchain): is_last_block = _test_blockchain_rpc(blockchain.is_last_block, 0) assert isinstance(is_last_block, bool) assert not is_last_block + def test_epoch_last_block(setup_blockchain): epoch_last_block = _test_blockchain_rpc(blockchain.epoch_last_block, 0) assert isinstance(epoch_last_block, int) + def test_get_circulating_supply(setup_blockchain): circulating_supply = _test_blockchain_rpc(blockchain.get_circulating_supply) assert isinstance(circulating_supply, str) + def test_get_total_supply(setup_blockchain): total_supply = _test_blockchain_rpc(blockchain.get_total_supply) assert isinstance(total_supply, str) or total_supply == None + def test_get_last_cross_links(setup_blockchain): last_cross_links = _test_blockchain_rpc(blockchain.get_last_cross_links) assert isinstance(last_cross_links, list) + def test_get_gas_price(setup_blockchain): gas_price = _test_blockchain_rpc(blockchain.get_gas_price) assert isinstance(gas_price, int) + def test_get_version(setup_blockchain): version = _test_blockchain_rpc(blockchain.get_version) assert isinstance(version, int) + def test_get_header_by_number(setup_blockchain): header_pair = _test_blockchain_rpc(blockchain.get_header_by_number, 0) assert isinstance(header_pair, dict) + def test_get_block_staking_transaction_count_by_number(setup_blockchain): - tx_count = _test_blockchain_rpc(blockchain.get_block_staking_transaction_count_by_number, test_block_number) + tx_count = _test_blockchain_rpc( + blockchain.get_block_staking_transaction_count_by_number, test_block_number + ) assert isinstance(tx_count, int) + def test_get_block_staking_transaction_count_by_hash(setup_blockchain): if not test_block_hash: - pytest.skip('Failed to get reference block hash') - tx_count = _test_blockchain_rpc(blockchain.get_block_staking_transaction_count_by_hash, test_block_hash) + pytest.skip("Failed to get reference block hash") + tx_count = _test_blockchain_rpc( + blockchain.get_block_staking_transaction_count_by_hash, test_block_hash + ) assert isinstance(tx_count, int) + def test_is_block_signer(setup_blockchain): - is_signer = _test_blockchain_rpc(blockchain.is_block_signer, test_block_number, address) + is_signer = _test_blockchain_rpc( + blockchain.is_block_signer, test_block_number, address + ) assert isinstance(is_signer, bool) + def test_get_signed_blocks(setup_blockchain): signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address) assert isinstance(signed_blocks, int) + def test_in_sync(setup_blockchain): in_sync = _test_blockchain_rpc(blockchain.in_sync) assert isinstance(in_sync, bool) + def test_beacon_in_sync(setup_blockchain): beacon_in_sync = _test_blockchain_rpc(blockchain.beacon_in_sync) assert isinstance(beacon_in_sync, bool) + def test_errors(): with pytest.raises(exceptions.RPCError): blockchain.chain_id(fake_shard) @@ -254,15 +307,15 @@ def test_errors(): with pytest.raises(exceptions.RPCError): blockchain.get_block_by_number(0, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_block_by_hash('', endpoint=fake_shard) + blockchain.get_block_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_block_transaction_count_by_number(0, fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_block_transaction_count_by_hash('', fake_shard) + blockchain.get_block_transaction_count_by_hash("", fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_block_staking_transaction_count_by_number(0, fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_block_staking_transaction_count_by_hash('', fake_shard) + blockchain.get_block_staking_transaction_count_by_hash("", fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_blocks(0, 1, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): @@ -270,9 +323,9 @@ def test_errors(): with pytest.raises(exceptions.RPCError): blockchain.get_block_signers_keys(0, fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.is_block_signer(0, '', fake_shard) + blockchain.is_block_signer(0, "", fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_signed_blocks('', fake_shard) + blockchain.get_signed_blocks("", fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_validators(1, fake_shard) with pytest.raises(exceptions.RPCError): diff --git a/tests/sdk-pyhmy/test_contract.py b/tests/sdk-pyhmy/test_contract.py index 59dacc4..3739c5f 100644 --- a/tests/sdk-pyhmy/test_contract.py +++ b/tests/sdk-pyhmy/test_contract.py @@ -1,71 +1,80 @@ import pytest -from pyhmy import ( - contract -) +from pyhmy import contract -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -explorer_endpoint = 'http://localhost:9599' -contract_tx_hash = '0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f' +explorer_endpoint = "http://localhost:9599" +contract_tx_hash = "0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f" # deployedBytecode from json file -contract_code = '0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033' +contract_code = "0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033" contract_address = None -fake_shard = 'http://example.com' +fake_shard = "http://example.com" + def _test_contract_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - elif isinstance(e, exceptions.RPCError) and 'estimateGas returned' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + elif isinstance(e, exceptions.RPCError) and "estimateGas returned" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_contract_address_from_hash(setup_blockchain): global contract_address - contract_address = _test_contract_rpc(contract.get_contract_address_from_hash, contract_tx_hash) + contract_address = _test_contract_rpc( + contract.get_contract_address_from_hash, contract_tx_hash + ) assert isinstance(contract_address, str) + def test_call(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') - called = _test_contract_rpc(contract.call, contract_address, 'latest') - assert isinstance(called, str) and called.startswith('0x') + pytest.skip("Contract address not loaded yet") + called = _test_contract_rpc(contract.call, contract_address, "latest") + assert isinstance(called, str) and called.startswith("0x") + def test_estimate_gas(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') + pytest.skip("Contract address not loaded yet") gas = _test_contract_rpc(contract.estimate_gas, contract_address) assert isinstance(gas, int) + def test_get_code(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') - code = _test_contract_rpc(contract.get_code, contract_address, 'latest') + pytest.skip("Contract address not loaded yet") + code = _test_contract_rpc(contract.get_code, contract_address, "latest") assert code == contract_code + def test_get_storage_at(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') - storage = _test_contract_rpc(contract.get_storage_at, contract_address, '0x0', 'latest') - assert isinstance(storage, str) and storage.startswith('0x') + pytest.skip("Contract address not loaded yet") + storage = _test_contract_rpc( + contract.get_storage_at, contract_address, "0x0", "latest" + ) + assert isinstance(storage, str) and storage.startswith("0x") + def test_errors(): with pytest.raises(exceptions.RPCError): - contract.get_contract_address_from_hash('', fake_shard) + contract.get_contract_address_from_hash("", fake_shard) with pytest.raises(exceptions.RPCError): - contract.call('', '', endpoint=fake_shard) + contract.call("", "", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - contract.estimate_gas('', endpoint=fake_shard) + contract.estimate_gas("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - contract.get_code('', 'latest', endpoint=fake_shard) + contract.get_code("", "latest", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - contract.get_storage_at('', 1, 'latest', endpoint=fake_shard) + contract.get_storage_at("", 1, "latest", endpoint=fake_shard) diff --git a/tests/sdk-pyhmy/test_signing.py b/tests/sdk-pyhmy/test_signing.py index 34ac601..7709e20 100644 --- a/tests/sdk-pyhmy/test_signing.py +++ b/tests/sdk-pyhmy/test_signing.py @@ -1,6 +1,4 @@ -from pyhmy import ( - signing -) +from pyhmy import signing """ Test signature source (node.js) @@ -30,16 +28,25 @@ let signed = RLPSign(transaction, privateKey); console.log( 'Signed transaction' ) console.log(signed) """ + + def test_eth_transaction(): transaction_dict = { - 'nonce': 2, - 'gasPrice': 1, - 'gas': 100, # signing.py uses Ether, which by default calls it gas - 'to': '0x14791697260e4c9a71f18484c9f997b308e59325', - 'value': 5, + "nonce": 2, + "gasPrice": 1, + "gas": 100, # signing.py uses Ether, which by default calls it gas + "to": "0x14791697260e4c9a71f18484c9f997b308e59325", + "value": 5, } - signed_tx = signing.sign_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1' + signed_tx = signing.sign_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1" + ) + """ Test signature source (node.js) @@ -71,16 +78,24 @@ let signed = RLPSign(transaction, privateKey); console.log( 'Signed transaction' ) console.log(signed) """ + + def test_hmy_transaction(): transaction_dict = { - 'nonce': 2, - 'gasPrice': 1, - 'gas': 100, # signing.py uses Ether, which by default calls it gas - 'to': '0x14791697260e4c9a71f18484c9f997b308e59325', - 'value': 5, - 'shardID': 0, - 'toShardID': 1, - 'chainId': 'HmyMainnet' + "nonce": 2, + "gasPrice": 1, + "gas": 100, # signing.py uses Ether, which by default calls it gas + "to": "0x14791697260e4c9a71f18484c9f997b308e59325", + "value": 5, + "shardID": 0, + "toShardID": 1, + "chainId": "HmyMainnet", } - signed_tx = signing.sign_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d' + signed_tx = signing.sign_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d" + ) diff --git a/tests/sdk-pyhmy/test_staking.py b/tests/sdk-pyhmy/test_staking.py index f84375a..7fc0ed0 100644 --- a/tests/sdk-pyhmy/test_staking.py +++ b/tests/sdk-pyhmy/test_staking.py @@ -1,135 +1,191 @@ import pytest import requests -from pyhmy import ( - staking -) +from pyhmy import staking -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -explorer_endpoint = 'http://localhost:9700' -test_validator_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' -fake_shard = 'http://example.com' +explorer_endpoint = "http://localhost:9700" +test_validator_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" +fake_shard = "http://example.com" + def _test_staking_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_all_validator_addresses(setup_blockchain): validator_addresses = _test_staking_rpc(staking.get_all_validator_addresses) assert isinstance(validator_addresses, list) assert len(validator_addresses) > 0 assert test_validator_address in validator_addresses + def test_get_validator_information(setup_blockchain): info = _test_staking_rpc(staking.get_validator_information, test_validator_address) assert isinstance(info, dict) + def test_get_all_validator_information(setup_blockchain): all_validator_information = _test_staking_rpc(staking.get_all_validator_information) assert isinstance(all_validator_information, list) assert len(all_validator_information) > 0 + def test_get_delegations_by_delegator(setup_blockchain): - delegations = _test_staking_rpc(staking.get_delegations_by_delegator, test_validator_address) + delegations = _test_staking_rpc( + staking.get_delegations_by_delegator, test_validator_address + ) assert isinstance(delegations, list) assert len(delegations) > 0 + def test_get_delegations_by_validator(setup_blockchain): - delegations = _test_staking_rpc(staking.get_delegations_by_validator, test_validator_address) + delegations = _test_staking_rpc( + staking.get_delegations_by_validator, test_validator_address + ) assert isinstance(delegations, list) assert len(delegations) > 0 + def test_get_current_utility_metrics(setup_blockchain): metrics = _test_staking_rpc(staking.get_current_utility_metrics) assert isinstance(metrics, dict) + def test_get_staking_network_info(setup_blockchain): info = _test_staking_rpc(staking.get_staking_network_info) assert isinstance(info, dict) + def test_get_super_committees(setup_blockchain): committee = _test_staking_rpc(staking.get_super_committees) assert isinstance(committee, dict) + def test_get_raw_median_stake_snapshot(setup_blockchain): median_stake = _test_staking_rpc(staking.get_raw_median_stake_snapshot) assert isinstance(median_stake, dict) + def test_get_validator_information_by_block(setup_blockchain): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block - info = _test_staking_rpc(staking.get_validator_information_by_block_number, test_validator_address, 'latest', endpoint=explorer_endpoint) + info = _test_staking_rpc( + staking.get_validator_information_by_block_number, + test_validator_address, + "latest", + endpoint=explorer_endpoint, + ) assert isinstance(info, dict) + def test_get_validator_information_by_block(setup_blockchain): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block - info = _test_staking_rpc(staking.get_all_validator_information_by_block_number, 'latest', endpoint=explorer_endpoint) + info = _test_staking_rpc( + staking.get_all_validator_information_by_block_number, + "latest", + endpoint=explorer_endpoint, + ) assert isinstance(info, list) + def test_get_delegations_by_delegator_by_block(setup_blockchain): - delegations = _test_staking_rpc(staking.get_delegations_by_delegator_by_block_number, test_validator_address, 'latest', endpoint=explorer_endpoint) + delegations = _test_staking_rpc( + staking.get_delegations_by_delegator_by_block_number, + test_validator_address, + "latest", + endpoint=explorer_endpoint, + ) assert isinstance(delegations, list) + def test_get_elected_validator_addresses(setup_blockchain): validator_addresses = _test_staking_rpc(staking.get_elected_validator_addresses) assert isinstance(validator_addresses, list) assert len(validator_addresses) > 0 + def test_get_validators(setup_blockchain): validators = _test_staking_rpc(staking.get_validators, 2) assert isinstance(validators, dict) - assert len(validators['validators']) > 0 + assert len(validators["validators"]) > 0 + def test_get_validator_keys(setup_blockchain): validators = _test_staking_rpc(staking.get_validator_keys, 2) assert isinstance(validators, list) + def test_get_validator_self_delegation(setup_blockchain): - self_delegation = _test_staking_rpc(staking.get_validator_self_delegation, test_validator_address) + self_delegation = _test_staking_rpc( + staking.get_validator_self_delegation, test_validator_address + ) assert isinstance(self_delegation, int) assert self_delegation > 0 + def test_get_validator_total_delegation(setup_blockchain): - total_delegation = _test_staking_rpc(staking.get_validator_total_delegation, test_validator_address) + total_delegation = _test_staking_rpc( + staking.get_validator_total_delegation, test_validator_address + ) assert isinstance(total_delegation, int) assert total_delegation > 0 + def test_get_all_delegation_information(setup_blockchain): - delegation_information = _test_staking_rpc(staking.get_all_delegation_information, 0) + delegation_information = _test_staking_rpc( + staking.get_all_delegation_information, 0 + ) assert isinstance(delegation_information, list) assert len(delegation_information) > 0 + def test_get_delegation_by_delegator_and_validator(setup_blockchain): - delegation_information = _test_staking_rpc(staking.get_delegation_by_delegator_and_validator, test_validator_address, test_validator_address) + delegation_information = _test_staking_rpc( + staking.get_delegation_by_delegator_and_validator, + test_validator_address, + test_validator_address, + ) assert isinstance(delegation_information, dict) + def test_get_available_redelegation_balance(setup_blockchain): - redelgation_balance = _test_staking_rpc(staking.get_available_redelegation_balance, test_validator_address) + redelgation_balance = _test_staking_rpc( + staking.get_available_redelegation_balance, test_validator_address + ) assert isinstance(redelgation_balance, int) assert redelgation_balance == 0 + def test_get_total_staking(setup_blockchain): total_staking = _test_staking_rpc(staking.get_total_staking) assert isinstance(total_staking, int) - if staking.get_validator_information(test_validator_address, explorer_endpoint)[ 'active-status' ] == 'active': + if ( + staking.get_validator_information(test_validator_address, explorer_endpoint)[ + "active-status" + ] + == "active" + ): assert total_staking > 0 + def test_errors(): with pytest.raises(exceptions.RPCError): staking.get_all_validator_addresses(fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_information('', fake_shard) + staking.get_validator_information("", fake_shard) with pytest.raises(exceptions.RPCError): staking.get_elected_validator_addresses(fake_shard) with pytest.raises(exceptions.RPCError): @@ -137,27 +193,27 @@ def test_errors(): with pytest.raises(exceptions.RPCError): staking.get_validator_keys(1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_information_by_block_number('', 1, fake_shard) + staking.get_validator_information_by_block_number("", 1, fake_shard) with pytest.raises(exceptions.RPCError): staking.get_all_validator_information(-1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_self_delegation('', fake_shard) + staking.get_validator_self_delegation("", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_total_delegation('', fake_shard) + staking.get_validator_total_delegation("", fake_shard) with pytest.raises(exceptions.RPCError): staking.get_all_validator_information_by_block_number(1, 1, fake_shard) with pytest.raises(exceptions.RPCError): staking.get_all_delegation_information(1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_delegator('', fake_shard) + staking.get_delegations_by_delegator("", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_delegator_by_block_number('', 1, fake_shard) + staking.get_delegations_by_delegator_by_block_number("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegation_by_delegator_and_validator('', '', fake_shard) + staking.get_delegation_by_delegator_and_validator("", "", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_available_redelegation_balance('', fake_shard) + staking.get_available_redelegation_balance("", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_validator('', fake_shard) + staking.get_delegations_by_validator("", fake_shard) with pytest.raises(exceptions.RPCError): staking.get_current_utility_metrics(fake_shard) with pytest.raises(exceptions.RPCError): diff --git a/tests/sdk-pyhmy/test_staking_signing.py b/tests/sdk-pyhmy/test_staking_signing.py index 9e7aefe..b947cbb 100644 --- a/tests/sdk-pyhmy/test_staking_signing.py +++ b/tests/sdk-pyhmy/test_staking_signing.py @@ -1,11 +1,6 @@ -from pyhmy import ( - staking_signing, - staking_structures -) +from pyhmy import staking_signing, staking_structures -from pyhmy.numbers import ( - convert_one_to_atto -) +from pyhmy.numbers import convert_one_to_atto # other transactions (create/edit validator) are in test_validator.py # test_delegate is the same as test_undelegate (except the directive) so it has been omitted @@ -56,17 +51,26 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_collect_rewards_chain_id(): transaction_dict = { - 'directive': staking_structures.Directive.CollectRewards, - 'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', - 'nonce': 2, - 'gasPrice': int(convert_one_to_atto(1)), - 'gasLimit': 100, - 'chainId': 1, # with chainId for coverage + "directive": staking_structures.Directive.CollectRewards, + "delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9", + "nonce": 2, + "gasPrice": int(convert_one_to_atto(1)), + "gasLimit": 100, + "chainId": 1, # with chainId for coverage } - signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099' + signed_tx = staking_signing.sign_staking_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099" + ) + """ let stakingTx @@ -87,16 +91,24 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_delegate(): transaction_dict = { - 'directive': staking_structures.Directive.Delegate, - 'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', - 'validatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', - 'amount': 5, - 'nonce': 2, - 'gasPrice': int(convert_one_to_atto(1)), - 'gasLimit': 100, - 'chainId': 2, + "directive": staking_structures.Directive.Delegate, + "delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9", + "validatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9", + "amount": 5, + "nonce": 2, + "gasPrice": int(convert_one_to_atto(1)), + "gasLimit": 100, + "chainId": 2, } - signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6' + signed_tx = staking_signing.sign_staking_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6" + ) diff --git a/tests/sdk-pyhmy/test_transaction.py b/tests/sdk-pyhmy/test_transaction.py index 2bbcd61..3194e5d 100644 --- a/tests/sdk-pyhmy/test_transaction.py +++ b/tests/sdk-pyhmy/test_transaction.py @@ -1,157 +1,205 @@ import pytest -from pyhmy import ( - transaction -) +from pyhmy import transaction -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -endpoint = 'http://localhost:9500' -endpoint_shard_one = 'http://localhost:9502' -fake_shard = 'http://example.com' +endpoint = "http://localhost:9500" +endpoint_shard_one = "http://localhost:9502" +fake_shard = "http://example.com" # previously sent txs to get and check -tx_hash = '0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43' +tx_hash = "0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43" tx_block_num = None tx_block_hash = None tx_index = None -cx_hash = '0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf' +cx_hash = "0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf" -stx_hash = '0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f' +stx_hash = "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f" stx_block_num = None stx_block_hash = None stx_index = None # new txs to send and check -raw_tx = '0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0' -raw_tx_hash = '0x7ccd80f8513f76ec58b357c7a82a12a95e025d88f1444e953f90e3d86e222571' +raw_tx = "0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0" +raw_tx_hash = "0x7ccd80f8513f76ec58b357c7a82a12a95e025d88f1444e953f90e3d86e222571" + +raw_stx = "0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000008085174876e80082c35027a0808ea7d27adf3b1f561e8da4676814084bb75ac541b616bece87c6446e6cc54ea02f19f0b14240354bd42ad60b0c7189873c0be87044e13072b0981a837ca76f64" +raw_stx_hash = "0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629" -raw_stx = '0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000008085174876e80082c35027a0808ea7d27adf3b1f561e8da4676814084bb75ac541b616bece87c6446e6cc54ea02f19f0b14240354bd42ad60b0c7189873c0be87044e13072b0981a837ca76f64' -raw_stx_hash = '0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629' def _test_transaction_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_pending_transactions(setup_blockchain): pool = _test_transaction_rpc(transaction.get_pending_transactions) assert isinstance(pool, list) + def test_get_transaction_by_hash(setup_blockchain): - tx = _test_transaction_rpc(transaction.get_transaction_by_hash, tx_hash, endpoint=endpoint) + tx = _test_transaction_rpc( + transaction.get_transaction_by_hash, tx_hash, endpoint=endpoint + ) assert tx assert isinstance(tx, dict) - assert 'blockNumber' in tx.keys() - assert 'blockHash' in tx.keys() + assert "blockNumber" in tx.keys() + assert "blockHash" in tx.keys() global tx_block_num - tx_block_num = int(tx['blockNumber']) + tx_block_num = int(tx["blockNumber"]) global tx_block_hash - tx_block_hash = tx['blockHash'] + tx_block_hash = tx["blockHash"] global tx_index - tx_index = int(tx[ 'transactionIndex' ]) + tx_index = int(tx["transactionIndex"]) + def test_get_transaction_by_block_hash_and_index(setup_blockchain): if not tx_block_hash: - pytest.skip('Failed to get reference block hash') - tx = _test_transaction_rpc(transaction.get_transaction_by_block_hash_and_index, - tx_block_hash, tx_index, endpoint=endpoint) + pytest.skip("Failed to get reference block hash") + tx = _test_transaction_rpc( + transaction.get_transaction_by_block_hash_and_index, + tx_block_hash, + tx_index, + endpoint=endpoint, + ) assert tx assert isinstance(tx, dict) + def test_get_transaction_by_block_number_and_index(setup_blockchain): if not tx_block_num: - pytest.skip('Failed to get reference block num') - tx = _test_transaction_rpc(transaction.get_transaction_by_block_number_and_index, tx_block_num, tx_index, - endpoint=endpoint) + pytest.skip("Failed to get reference block num") + tx = _test_transaction_rpc( + transaction.get_transaction_by_block_number_and_index, + tx_block_num, + tx_index, + endpoint=endpoint, + ) assert tx assert isinstance(tx, dict) + def test_get_transaction_receipt(setup_blockchain): - tx_receipt = _test_transaction_rpc(transaction.get_transaction_receipt, tx_hash, endpoint=endpoint) + tx_receipt = _test_transaction_rpc( + transaction.get_transaction_receipt, tx_hash, endpoint=endpoint + ) assert tx_receipt assert isinstance(tx_receipt, dict) + def test_get_transaction_error_sink(setup_blockchain): errors = _test_transaction_rpc(transaction.get_transaction_error_sink) assert isinstance(errors, list) + def test_send_and_confirm_raw_transaction(setup_blockchain): # Note: this test is not yet idempotent since the localnet will reject transactions which were previously finalized. - test_tx = _test_transaction_rpc(transaction.send_and_confirm_raw_transaction, - raw_tx) + test_tx = _test_transaction_rpc( + transaction.send_and_confirm_raw_transaction, raw_tx + ) assert isinstance(test_tx, dict) - assert test_tx[ 'hash' ] == raw_tx_hash + assert test_tx["hash"] == raw_tx_hash + def test_get_pending_cx_receipts(setup_blockchain): pending = _test_transaction_rpc(transaction.get_pending_cx_receipts) assert isinstance(pending, list) + def test_get_cx_receipt_by_hash(setup_blockchain): - cx = _test_transaction_rpc(transaction.get_cx_receipt_by_hash, cx_hash, endpoint_shard_one) + cx = _test_transaction_rpc( + transaction.get_cx_receipt_by_hash, cx_hash, endpoint_shard_one + ) assert cx assert isinstance(cx, dict) + def test_resend_cx_receipt(setup_blockchain): sent = _test_transaction_rpc(transaction.resend_cx_receipt, cx_hash) assert isinstance(sent, bool) assert sent + def test_get_staking_transaction_by_hash(setup_blockchain): - staking_tx = _test_transaction_rpc(transaction.get_staking_transaction_by_hash, stx_hash) + staking_tx = _test_transaction_rpc( + transaction.get_staking_transaction_by_hash, stx_hash + ) assert staking_tx assert isinstance(staking_tx, dict) - assert 'blockNumber' in staking_tx.keys() - assert 'blockHash' in staking_tx.keys() + assert "blockNumber" in staking_tx.keys() + assert "blockHash" in staking_tx.keys() global stx_block_num - stx_block_num = int(staking_tx['blockNumber']) + stx_block_num = int(staking_tx["blockNumber"]) global stx_block_hash - stx_block_hash = staking_tx['blockHash'] + stx_block_hash = staking_tx["blockHash"] global stx_index - stx_index = int(staking_tx['transactionIndex']) + stx_index = int(staking_tx["transactionIndex"]) + def test_get_transaction_by_block_hash_and_index(setup_blockchain): if not stx_block_hash: - pytest.skip('Failed to get reference block hash') - stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_hash_and_index, stx_block_hash, stx_index) + pytest.skip("Failed to get reference block hash") + stx = _test_transaction_rpc( + transaction.get_staking_transaction_by_block_hash_and_index, + stx_block_hash, + stx_index, + ) assert stx assert isinstance(stx, dict) + def test_get_transaction_by_block_number_and_index(setup_blockchain): if not stx_block_num: - pytest.skip('Failed to get reference block num') - stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_number_and_index, stx_block_num, stx_index) + pytest.skip("Failed to get reference block num") + stx = _test_transaction_rpc( + transaction.get_staking_transaction_by_block_number_and_index, + stx_block_num, + stx_index, + ) assert stx assert isinstance(stx, dict) + def test_get_staking_transaction_error_sink(setup_blockchain): errors = _test_transaction_rpc(transaction.get_staking_transaction_error_sink) assert isinstance(errors, list) + def test_send_raw_staking_transaction(setup_blockchain): - test_stx = _test_transaction_rpc(transaction.send_and_confirm_raw_staking_transaction, raw_stx, endpoint=endpoint) + test_stx = _test_transaction_rpc( + transaction.send_and_confirm_raw_staking_transaction, raw_stx, endpoint=endpoint + ) assert isinstance(test_stx, dict) - assert test_stx[ 'hash' ] == raw_stx_hash + assert test_stx["hash"] == raw_stx_hash + def test_get_pool_stats(setup_blockchain): - test_pool_stats = _test_transaction_rpc(transaction.get_pool_stats, endpoint=endpoint) + test_pool_stats = _test_transaction_rpc( + transaction.get_pool_stats, endpoint=endpoint + ) assert isinstance(test_pool_stats, dict) + def test_get_pending_staking_transactions(setup_blockchain): - pending_staking_transactions = _test_transaction_rpc(transaction.get_pending_staking_transactions, endpoint=endpoint) + pending_staking_transactions = _test_transaction_rpc( + transaction.get_pending_staking_transactions, endpoint=endpoint + ) assert isinstance(pending_staking_transactions, list) + def test_errors(): with pytest.raises(exceptions.RPCError): transaction.get_pending_transactions(fake_shard) @@ -160,30 +208,34 @@ def test_errors(): with pytest.raises(exceptions.RPCError): transaction.get_pool_stats(fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_transaction_by_hash('', endpoint=fake_shard) + transaction.get_transaction_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_transaction_by_block_hash_and_index('', 1, endpoint=fake_shard) + transaction.get_transaction_by_block_hash_and_index("", 1, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): transaction.get_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_transaction_receipt('', endpoint=fake_shard) + transaction.get_transaction_receipt("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.send_raw_transaction('', endpoint=fake_shard) + transaction.send_raw_transaction("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): transaction.get_pending_cx_receipts(fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_cx_receipt_by_hash('', endpoint=fake_shard) + transaction.get_cx_receipt_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.resend_cx_receipt('', endpoint=fake_shard) + transaction.resend_cx_receipt("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_by_hash('', endpoint=fake_shard) + transaction.get_staking_transaction_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_by_block_hash_and_index('', 1, endpoint=fake_shard) + transaction.get_staking_transaction_by_block_hash_and_index( + "", 1, endpoint=fake_shard + ) with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard) + transaction.get_staking_transaction_by_block_number_and_index( + 1, 1, endpoint=fake_shard + ) with pytest.raises(exceptions.RPCError): transaction.get_staking_transaction_error_sink(endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.send_raw_staking_transaction('', endpoint=fake_shard) + transaction.send_raw_staking_transaction("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): transaction.get_pending_staking_transactions(endpoint=fake_shard) diff --git a/tests/sdk-pyhmy/test_validator.py b/tests/sdk-pyhmy/test_validator.py index f6269e8..21f33b7 100644 --- a/tests/sdk-pyhmy/test_validator.py +++ b/tests/sdk-pyhmy/test_validator.py @@ -1,19 +1,11 @@ import pytest -from decimal import ( - Decimal -) +from decimal import Decimal -from pyhmy import ( - validator -) +from pyhmy import validator -from pyhmy.numbers import ( - convert_one_to_atto -) +from pyhmy.numbers import convert_one_to_atto -from pyhmy.exceptions import ( - InvalidValidatorError -) +from pyhmy.exceptions import InvalidValidatorError test_epoch_number = 0 genesis_block_number = 0 @@ -21,33 +13,42 @@ test_block_number = 1 test_validator_object = None test_validator_loaded = False + def test_instantiate_validator(setup_blockchain): global test_validator_object - test_validator_object = validator.Validator('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') + test_validator_object = validator.Validator( + "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) assert isinstance(test_validator_object, validator.Validator) + def test_load_validator(setup_blockchain): if not test_validator_object: - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") info = { - 'name': 'Alice', - 'identity': 'alice', - 'website': 'alice.harmony.one', - 'details': "Don't mess with me!!!", - 'security-contact': 'Bob', - 'min-self-delegation': convert_one_to_atto(10000), - 'amount': convert_one_to_atto(10001), - 'max-rate': '0.9', - 'max-change-rate': '0.05', - 'rate': '0.01', - 'bls-public-keys': ['0x30b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202'], - 'bls-key-sigs': ['0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414'], - 'max-total-delegation': convert_one_to_atto(40000) + "name": "Alice", + "identity": "alice", + "website": "alice.harmony.one", + "details": "Don't mess with me!!!", + "security-contact": "Bob", + "min-self-delegation": convert_one_to_atto(10000), + "amount": convert_one_to_atto(10001), + "max-rate": "0.9", + "max-change-rate": "0.05", + "rate": "0.01", + "bls-public-keys": [ + "0x30b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202" + ], + "bls-key-sigs": [ + "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414" + ], + "max-total-delegation": convert_one_to_atto(40000), } test_validator_object.load(info) global test_validator_loaded test_validator_loaded = True + """ TypeScript signature source (is outdated because the JS SDK has not been updated for SlotKeySigs) For now I have checked that the below transaction to localnet works @@ -75,16 +76,23 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_create_validator_sign(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") signed_hash = test_validator_object.sign_create_validator_transaction( - 2, - int(convert_one_to_atto(1)), - 100, - '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', - 2).rawTransaction.hex() - assert signed_hash == '0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd' + 2, + int(convert_one_to_atto(1)), + 100, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + 2, + ).rawTransaction.hex() + assert ( + signed_hash + == "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd" + ) + """ Signature matched from TypeScript (is outdated because the JS SDK has not been updated for SlotKeyToAddSig) @@ -127,39 +135,46 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_edit_validator_sign(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") signed_hash = test_validator_object.sign_edit_validator_transaction( - 2, - int(convert_one_to_atto(1)), - 100, - '0.06', - '0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612', # remove key - "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # add key - "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414", # add key sig - '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', - 2).rawTransaction.hex() - assert signed_hash == '0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995' + 2, + int(convert_one_to_atto(1)), + 100, + "0.06", + "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612", # remove key + "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # add key + "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414", # add key sig + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + 2, + ).rawTransaction.hex() + assert ( + signed_hash + == "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995" + ) + def test_invalid_validator(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") with pytest.raises(InvalidValidatorError): info = { - 'name': 'Alice', + "name": "Alice", } test_validator_object.load(info) with pytest.raises(InvalidValidatorError): - test_validator_object.set_name('a'*141) + test_validator_object.set_name("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_identity('a'*141) + test_validator_object.set_identity("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_website('a'*141) + test_validator_object.set_website("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_security_contact('a'*141) + test_validator_object.set_security_contact("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_details('a'*281) + test_validator_object.set_details("a" * 281) with pytest.raises(InvalidValidatorError): test_validator_object.set_min_self_delegation(1) with pytest.raises(InvalidValidatorError): @@ -167,22 +182,26 @@ def test_invalid_validator(setup_blockchain): with pytest.raises(InvalidValidatorError): test_validator_object.set_amount(1) with pytest.raises(InvalidValidatorError): - test_validator_object.set_max_rate('2.0') + test_validator_object.set_max_rate("2.0") with pytest.raises(InvalidValidatorError): - test_validator_object.set_max_change_rate('-2.0') + test_validator_object.set_max_change_rate("-2.0") with pytest.raises(InvalidValidatorError): - test_validator_object.set_rate('-2.0') + test_validator_object.set_rate("-2.0") + def test_validator_getters(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') - assert test_validator_object.get_address() == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' - assert test_validator_object.add_bls_key('5') - assert test_validator_object.remove_bls_key('5') - assert test_validator_object.get_name() == 'Alice' - assert test_validator_object.get_identity() == 'alice' - assert test_validator_object.get_website() == 'alice.harmony.one' - assert test_validator_object.get_security_contact() == 'Bob' + pytest.skip("Validator not instantiated yet") + assert ( + test_validator_object.get_address() + == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) + assert test_validator_object.add_bls_key("5") + assert test_validator_object.remove_bls_key("5") + assert test_validator_object.get_name() == "Alice" + assert test_validator_object.get_identity() == "alice" + assert test_validator_object.get_website() == "alice.harmony.one" + assert test_validator_object.get_security_contact() == "Bob" assert test_validator_object.get_details() == "Don't mess with me!!!" assert isinstance(test_validator_object.get_min_self_delegation(), Decimal) assert isinstance(test_validator_object.get_max_total_delegation(), Decimal) @@ -192,6 +211,9 @@ def test_validator_getters(setup_blockchain): assert isinstance(test_validator_object.get_rate(), Decimal) assert len(test_validator_object.get_bls_keys()) > 0 + def test_validator_load_from_blockchain(setup_blockchain): - test_validator_object2 = validator.Validator('one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3') + test_validator_object2 = validator.Validator( + "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" + ) test_validator_object2.load_from_blockchain() diff --git a/tests/util-pyhmy/test_util.py b/tests/util-pyhmy/test_util.py index e278ef2..2988c47 100644 --- a/tests/util-pyhmy/test_util.py +++ b/tests/util-pyhmy/test_util.py @@ -19,41 +19,52 @@ def setup(): def test_json_load(): - dec = util.json_load('1.1', parse_float=decimal.Decimal) + dec = util.json_load("1.1", parse_float=decimal.Decimal) assert isinstance(dec, decimal.Decimal) assert float(dec) == 1.1 - ref_dict = { - 'test': 'val', - 'arr': [ - 1, - 2, - 3 - ] - } + ref_dict = {"test": "val", "arr": [1, 2, 3]} loaded_dict = util.json_load(json.dumps(ref_dict)) assert str(ref_dict) == str(loaded_dict) + def test_chain_id_to_int(): assert util.chain_id_to_int(2) == 2 - assert util.chain_id_to_int('HmyMainnet') == 1 + assert util.chain_id_to_int("HmyMainnet") == 1 + def test_get_gopath(): assert isinstance(util.get_gopath(), str) + def test_get_goversion(): assert isinstance(util.get_goversion(), str) + def test_convert_one_to_hex(): - assert util.convert_one_to_hex('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' - assert util.convert_one_to_hex('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' + assert ( + util.convert_one_to_hex("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58") + == "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58" + ) + assert ( + util.convert_one_to_hex("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") + == "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58" + ) + def test_convert_hex_to_one(): - assert util.convert_hex_to_one('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' - assert util.convert_hex_to_one('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' - + assert ( + util.convert_hex_to_one("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58") + == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) + assert ( + util.convert_hex_to_one("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") + == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) + def test_get_bls_build_variables(): assert isinstance(util.get_bls_build_variables(), dict) + def test_is_active_shard(): - assert isinstance(util.is_active_shard(''), bool) + assert isinstance(util.is_active_shard(""), bool)