chore: run `black` on all files

pull/35/head
MaxMustermann2 2 years ago
parent 2135d1050c
commit a3295b9650
No known key found for this signature in database
GPG Key ID: 4F4AB9DB6FF24C94
  1. 20
      pyhmy/__init__.py
  2. 2
      pyhmy/_version.py
  3. 227
      pyhmy/account.py
  4. 17
      pyhmy/bech32/bech32.py
  5. 417
      pyhmy/blockchain.py
  6. 113
      pyhmy/cli.py
  7. 125
      pyhmy/contract.py
  8. 25
      pyhmy/exceptions.py
  9. 41
      pyhmy/logging.py
  10. 8
      pyhmy/rpc/exceptions.py
  11. 45
      pyhmy/rpc/request.py
  12. 155
      pyhmy/signing.py
  13. 280
      pyhmy/staking.py
  14. 363
      pyhmy/staking_signing.py
  15. 215
      pyhmy/staking_structures.py
  16. 258
      pyhmy/transaction.py
  17. 98
      pyhmy/util.py
  18. 289
      pyhmy/validator.py
  19. 11
      tests/bech32-pyhmy/test_bech32.py
  20. 4
      tests/logging-pyhmy/test_logging.py
  21. 9
      tests/numbers-pyhmy/test_numbers.py
  22. 69
      tests/request-pyhmy/test_request.py
  23. 222
      tests/sdk-pyhmy/conftest.py
  24. 98
      tests/sdk-pyhmy/test_account.py
  25. 117
      tests/sdk-pyhmy/test_blockchain.py
  26. 71
      tests/sdk-pyhmy/test_contract.py
  27. 55
      tests/sdk-pyhmy/test_signing.py
  28. 124
      tests/sdk-pyhmy/test_staking.py
  29. 62
      tests/sdk-pyhmy/test_staking_signing.py
  30. 174
      tests/sdk-pyhmy/test_transaction.py
  31. 150
      tests/sdk-pyhmy/test_validator.py
  32. 41
      tests/util-pyhmy/test_util.py

@ -3,28 +3,16 @@ import warnings
from ._version import __version__ from ._version import __version__
from .util import ( from .util import Typgpy, get_gopath, get_goversion, get_bls_build_variables, json_load
Typgpy,
get_gopath,
get_goversion,
get_bls_build_variables,
json_load
)
if sys.version_info.major < 3: if sys.version_info.major < 3:
warnings.simplefilter("always", DeprecationWarning) warnings.simplefilter("always", DeprecationWarning)
warnings.warn( warnings.warn(
DeprecationWarning( DeprecationWarning("`pyhmy` does not support Python 2. Please use Python 3.")
"`pyhmy` does not support Python 2. Please use Python 3."
)
) )
warnings.resetwarnings() 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.simplefilter("always", ImportWarning)
warnings.warn( warnings.warn(ImportWarning("`pyhmy` does not work on Windows or Cygwin."))
ImportWarning(
"`pyhmy` does not work on Windows or Cygwin."
)
)
warnings.resetwarnings() warnings.resetwarnings()

@ -7,5 +7,5 @@ Provides pyhmy version information.
from incremental import Version from incremental import Version
__version__ = Version('pyhmy', 20, 5, 20) __version__ = Version("pyhmy", 20, 5, 20)
__all__ = ["__version__"] __all__ = ["__version__"]

@ -1,26 +1,14 @@
from .rpc.request import ( from .rpc.request import rpc_request
rpc_request
)
from .rpc.exceptions import ( from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError
RPCError,
RequestsError,
RequestsTimeoutError
)
from .exceptions import ( from .exceptions import InvalidRPCReplyError
InvalidRPCReplyError
)
from .blockchain import ( from .blockchain import get_sharding_structure
get_sharding_structure
)
from .bech32.bech32 import ( from .bech32.bech32 import bech32_decode
bech32_decode
)
_default_endpoint = 'http://localhost:9500' _default_endpoint = "http://localhost:9500"
_default_timeout = 30 _default_timeout = 30
_address_length = 42 _address_length = 42
@ -40,13 +28,14 @@ def is_valid_address(address) -> bool:
bool bool
Is valid address Is valid address
""" """
if not address.startswith('one1'): if not address.startswith("one1"):
return False return False
hrp, _ = bech32_decode(address) hrp, _ = bech32_decode(address)
if not hrp: if not hrp:
return False return False
return True return True
def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get current account balance 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 https://api.hmny.io/#da8901d2-d237-4c3b-9d7d-10af9def05c4
""" """
method = 'hmyv2_getBalance' method = "hmyv2_getBalance"
params = [ params = [address]
address
]
try: try:
balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] balance = rpc_request(
return int(balance) # v2 returns the result as it is method, params=params, endpoint=endpoint, timeout=timeout
except TypeError as e: # check will work if rpc returns None )["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 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 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://api.hmny.io/#9aeae4b8-1a09-4ed2-956b-d7c96266dd33
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92
""" """
method = 'hmyv2_getBalanceByBlockNumber' method = "hmyv2_getBalanceByBlockNumber"
params = [ params = [address, block_num]
address,
block_num
]
try: 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) return int(balance)
except TypeError as e: except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L51
""" """
method = 'hmyv2_getAccountNonce' method = "hmyv2_getAccountNonce"
params = [ params = [address, block_num]
address,
block_num
]
try: 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) return int(nonce)
except TypeError as e: except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 See get_account_nonce
""" """
return get_account_nonce(address, block_num, endpoint, timeout) 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 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 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 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L69
""" """
method = 'hmyv2_getTransactionCount' method = "hmyv2_getTransactionCount"
params = [ params = [address, block_num]
address,
block_num
]
try: 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) return int(nonce)
except TypeError as e: except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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://api.hmny.io/#fc97aed2-e65e-4cf4-bc01-8dadb76732c0
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114
""" """
method = 'hmyv2_getTransactionsCount' method = "hmyv2_getTransactionsCount"
params = [ params = [address, tx_type]
address,
tx_type
]
try: 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) return int(tx_count)
except TypeError as e: except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from 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") 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://api.hmny.io/#ddc1b029-f341-4c4d-ba19-74b528d6e5e5
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134
""" """
method = 'hmyv2_getStakingTransactionsCount' method = "hmyv2_getStakingTransactionsCount"
params = [ params = [address, tx_type]
address,
tx_type
]
try: 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) return int(tx_count)
except (KeyError, TypeError) as e: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 def get_transaction_history(
) -> list: 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 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 = [ params = [
{ {
'address': address, "address": address,
'pageIndex': page, "pageIndex": page,
'pageSize': page_size, "pageSize": page_size,
'fullTx': include_full_tx, "fullTx": include_full_tx,
'txType': tx_type, "txType": tx_type,
'order': order "order": order,
} }
] ]
method = 'hmyv2_getTransactionsHistory' method = "hmyv2_getTransactionsHistory"
try: try:
tx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout) tx_history = rpc_request(
return tx_history['result']['transactions'] method, params=params, endpoint=endpoint, timeout=timeout
)
return tx_history["result"]["transactions"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 def get_staking_transaction_history(
) -> list: 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 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 = [ params = [
{ {
'address': address, "address": address,
'pageIndex': page, "pageIndex": page,
'pageSize': page_size, "pageSize": page_size,
'fullTx': include_full_tx, "fullTx": include_full_tx,
'txType': tx_type, "txType": tx_type,
'order': order "order": order,
} }
] ]
# Using v2 API, because getStakingTransactionHistory not implemented in v1 # Using v2 API, because getStakingTransactionHistory not implemented in v1
method = 'hmyv2_getStakingTransactionsHistory' method = "hmyv2_getStakingTransactionsHistory"
try: try:
stx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] stx_history = rpc_request(
return stx_history['staking_transactions'] method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return stx_history["staking_transactions"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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) sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout)
for shard in sharding_structure: for shard in sharding_structure:
try: try:
balances.append({ balances.append(
'shard': shard['shardID'], {
'balance': get_balance(address, endpoint=shard['http'], timeout=timeout) "shard": shard["shardID"],
}) "balance": get_balance(
address, endpoint=shard["http"], timeout=timeout
),
}
)
except (KeyError, RPCError, RequestsError, RequestsTimeoutError): except (KeyError, RPCError, RequestsError, RequestsTimeoutError):
if not skip_error: if not skip_error:
balances.append({ balances.append({"shard": shard["shardID"], "balance": None})
'shard': shard['shardID'],
'balance': None
})
return balances 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 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 get_balance_on_all_shards
""" """
try: try:
balances = get_balance_on_all_shards(address, skip_error=False, endpoint=endpoint, timeout=timeout) balances = get_balance_on_all_shards(
return sum(b['balance'] for b in balances) address, skip_error=False, endpoint=endpoint, timeout=timeout
)
return sum(b["balance"] for b in balances)
except TypeError as e: except TypeError as e:
raise RuntimeError from e raise RuntimeError from e

@ -26,11 +26,11 @@ CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
def bech32_polymod(values): def bech32_polymod(values):
"""Internal function that computes the Bech32 checksum.""" """Internal function that computes the Bech32 checksum."""
generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3]
chk = 1 chk = 1
for value in values: for value in values:
top = chk >> 25 top = chk >> 25
chk = (chk & 0x1ffffff) << 5 ^ value chk = (chk & 0x1FFFFFF) << 5 ^ value
for i in range(5): for i in range(5):
chk ^= generator[i] if ((top >> i) & 1) else 0 chk ^= generator[i] if ((top >> i) & 1) else 0
return chk return chk
@ -56,22 +56,23 @@ def bech32_create_checksum(hrp, data):
def bech32_encode(hrp, data): def bech32_encode(hrp, data):
"""Compute a Bech32 string given HRP and data values.""" """Compute a Bech32 string given HRP and data values."""
combined = data + bech32_create_checksum(hrp, data) 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): def bech32_decode(bech):
"""Validate a Bech32 string, and determine HRP and data.""" """Validate a Bech32 string, and determine HRP and data."""
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or (
(bech.lower() != bech and bech.upper() != bech)): bech.lower() != bech and bech.upper() != bech
):
return (None, None) return (None, None)
bech = bech.lower() bech = bech.lower()
pos = bech.rfind('1') pos = bech.rfind("1")
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
return (None, None) 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) return (None, None)
hrp = bech[:pos] 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): if not bech32_verify_checksum(hrp, data):
return (None, None) return (None, None)
return (hrp, data[:-6]) return (hrp, data[:-6])

@ -1,12 +1,8 @@
from .rpc.request import ( from .rpc.request import rpc_request
rpc_request
)
from .exceptions import ( from .exceptions import InvalidRPCReplyError
InvalidRPCReplyError
)
_default_endpoint = 'http://localhost:9500' _default_endpoint = "http://localhost:9500"
_default_timeout = 30 _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 https://api.hmny.io/#0ba3c7b6-6aa9-46b8-9c84-f8782e935951
""" """
method = 'hmyv2_getCurrentBadBlocks' method = "hmyv2_getCurrentBadBlocks"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
""" """
Chain id of the chain 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 https://github.com/harmony-one/harmony/blob/343dbe89b3c105f8104ab877769070ba6fdd0133/rpc/blockchain.go#L44
""" """
method = 'hmyv2_chainId' method = "hmyv2_chainId"
try: try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout) data = rpc_request(method, endpoint=endpoint, timeout=timeout)
return data['result'] return data["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
""" """
Get config for the node 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/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 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/node/api.go#L110 for consensus dict
""" """
method = 'hmyv2_getNodeMetadata' method = "hmyv2_getNodeMetadata"
try: try:
metadata = rpc_request(method, endpoint=endpoint, timeout=timeout) metadata = rpc_request(method, endpoint=endpoint, timeout=timeout)
return metadata['result'] return metadata["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
""" """
Get peer info for the node Get peer info for the node
@ -181,12 +180,13 @@ def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
-------- --------
get_node_metadata get_node_metadata
""" """
method = 'hmyv2_getPeerInfo' method = "hmyv2_getPeerInfo"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get the current Harmony protocol version this node supports 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 https://api.hmny.io/#cab9fcc2-e3cd-4bc9-b62a-13e4e046e2fd
""" """
method = 'hmyv2_protocolVersion' method = "hmyv2_protocolVersion"
try: try:
value = rpc_request(method, endpoint=endpoint, timeout=timeout) value = rpc_request(method, endpoint=endpoint, timeout=timeout)
return value['result'] return value["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get number of peers connected to the node 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 https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1
""" """
method = 'net_peerCount' method = "net_peerCount"
try: # Number of peers represented as a hex string try: # Number of peers represented as a hex string
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'], 16) return int(
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16
)
except (KeyError, TypeError) as e: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get version of the EVM network (https://chainid.network/) 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 https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1
""" """
method = 'net_version' method = "net_version"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
""" """
Whether the shard chain is in sync or syncing (not out of sync) 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L690
""" """
method = 'hmyv2_inSync' method = "hmyv2_inSync"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
""" """
Whether the beacon chain is in sync or syncing (not out of sync) 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L695
""" """
method = 'hmyv2_beaconInSync' method = "hmyv2_beaconInSync"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get epoch number when blockchain switches to EPoS election 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 get_node_metadata
""" """
method = 'hmyv2_getNodeMetadata' method = "hmyv2_getNodeMetadata"
try: try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
return int(data['chain-config']['staking-epoch']) return int(data["chain-config"]["staking-epoch"])
except (KeyError, TypeError) as e: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get epoch number when blockchain switches to allow staking features without election 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 get_node_metadata
""" """
method = 'hmyv2_getNodeMetadata' method = "hmyv2_getNodeMetadata"
try: try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
return int(data['chain-config']['prestaking-epoch']) return int(data["chain-config"]["prestaking-epoch"])
except (KeyError, TypeError) as e: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
######################## ########################
# Sharding information # # Sharding information #
######################## ########################
@ -441,13 +452,18 @@ def get_shard(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
-------- --------
get_node_metadata get_node_metadata
""" """
method = 'hmyv2_getNodeMetadata' method = "hmyv2_getNodeMetadata"
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#9669d49e-43c1-47d9-a3fd-e7786e5879df
""" """
method = 'hmyv2_getShardingStructure' method = "hmyv2_getShardingStructure"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
############################# #############################
# Current status of network # # 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 https://api.hmny.io/#8b08d18c-017b-4b44-a3c3-356f9c12dacd
""" """
method = 'hmyv2_getLeader' method = "hmyv2_getLeader"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 = [ params = [
block_num, block_num,
] ]
method = 'hmyv2_isLastBlock' method = "hmyv2_isLastBlock"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 = [ params = [
epoch, epoch,
] ]
method = 'hmyv2_epochLastBlock' method = "hmyv2_epochLastBlock"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get current circulation supply of tokens in ONE 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 https://api.hmny.io/#8398e818-ac2d-4ad8-a3b4-a00927395044
""" """
method = 'hmyv2_getCirculatingSupply' method = "hmyv2_getCirculatingSupply"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get total number of pre-mined tokens 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 https://api.hmny.io/#3dcea518-9e9a-4a20-84f4-c7a0817b2196
""" """
method = 'hmyv2_getTotalSupply' method = "hmyv2_getTotalSupply"
try: try:
rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get current block number 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 https://api.hmny.io/#2602b6c4-a579-4b7c-bce8-85331e0db1a7
""" """
method = 'hmyv2_blockNumber' method = "hmyv2_blockNumber"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get current epoch number 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 https://api.hmny.io/#9b8e98b0-46d1-4fa0-aaa6-317ff1ddba59
""" """
method = 'hmyv2_getEpoch' method = "hmyv2_getEpoch"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) -> list: def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
""" """
Get last cross shard links 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 https://api.hmny.io/#4994cdf9-38c4-4b1d-90a8-290ddaa3040e
""" """
method = 'hmyv2_getLastCrossLinks' method = "hmyv2_getLastCrossLinks"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get network gas price 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 https://api.hmny.io/#1d53fd59-a89f-436c-a171-aec9d9623f48
""" """
method = 'hmyv2_gasPrice' method = "hmyv2_gasPrice"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
############## ##############
# Block RPCs # # 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 https://api.hmny.io/#73fc9b97-b048-4b85-8a93-4d2bf1da54a6
""" """
method = 'hmyv2_latestHeader' method = "hmyv2_latestHeader"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#01148e4f-72bb-426d-a123-718a161eaec0
""" """
method = 'hmyv2_getHeaderByNumber' method = "hmyv2_getHeaderByNumber"
params = [ params = [block_num]
block_num
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#7625493d-16bf-4611-8009-9635d063b4c0
""" """
method = 'hmyv2_getLatestChainHeaders' method = "hmyv2_getLatestChainHeaders"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 Get block by number
@ -979,20 +1031,30 @@ def get_block_by_number(block_num, full_tx=False, include_tx=False, include_stak
params = [ params = [
block_num, block_num,
{ {
'inclTx': include_tx, "inclTx": include_tx,
'fullTx': full_tx, "fullTx": full_tx,
'inclStaking': include_staking_tx, "inclStaking": include_staking_tx,
'withSigners': include_signers, "withSigners": include_signers,
}, },
] ]
method = 'hmyv2_getBlockByNumber' method = "hmyv2_getBlockByNumber"
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 Get block by hash
@ -1027,19 +1089,24 @@ def get_block_by_hash(block_hash, full_tx=False, include_tx=False, include_staki
params = [ params = [
block_hash, block_hash,
{ {
'inclTx': include_tx, "inclTx": include_tx,
'fullTx': full_tx, "fullTx": full_tx,
'inclStaking': include_staking_tx, "inclStaking": include_staking_tx,
'withSigners': include_signers, "withSigners": include_signers,
}, },
] ]
method = 'hmyv2_getBlockByHash' method = "hmyv2_getBlockByHash"
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#26c5adfb-d757-4595-9eb7-c6efef63df32
""" """
params = [ params = [block_num]
block_num method = "hmyv2_getBlockTransactionCountByNumber"
]
method = 'hmyv2_getBlockTransactionCountByNumber'
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#66c68844-0208-49bb-a83b-08722bc113eb
""" """
params = [ params = [block_hash]
block_hash method = "hmyv2_getBlockTransactionCountByHash"
]
method = 'hmyv2_getBlockTransactionCountByHash'
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L494
""" """
params = [ params = [block_num]
block_num method = "hmyv2_getBlockStakingTransactionCountByNumber"
]
method = 'hmyv2_getBlockStakingTransactionCountByNumber'
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L523
""" """
params = [ params = [block_hash]
block_hash method = "hmyv2_getBlockStakingTransactionCountByHash"
]
method = 'hmyv2_getBlockStakingTransactionCountByHash'
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 def get_blocks(
) -> list: 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 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, start_block,
end_block, end_block,
{ {
'withSigners': include_signers, "withSigners": include_signers,
'fullTx': full_tx, "fullTx": full_tx,
'inclStaking': include_staking_tx, "inclStaking": include_staking_tx,
'inclTx': include_tx "inclTx": include_tx,
}, },
] ]
method = 'hmyv2_getBlocks' method = "hmyv2_getBlocks"
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#1e4b5f41-9db6-4dea-92fb-4408db78e622
""" """
params = [ params = [block_num]
block_num method = "hmyv2_getBlockSigners"
]
method = 'hmyv2_getBlockSigners'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#9f9c8298-1a4e-4901-beac-f34b59ed02f1
""" """
params = [ params = [block_num]
block_num method = "hmyv2_getBlockSignerKeys"
]
method = 'hmyv2_getBlockSignerKeys'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L368
""" """
params = [ params = [block_num, address]
block_num, method = "hmyv2_isBlockSigner"
address
]
method = 'hmyv2_isBlockSigner'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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) 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L406
""" """
params = [ params = [address]
address method = "hmyv2_getSignedBlocks"
]
method = 'hmyv2_getSignedBlocks'
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
""" """
Get list of validators for specific epoch number 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 https://api.hmny.io/#4dfe91ad-71fa-4c7d-83f3-d1c86a804da5
""" """
params = [ params = [epoch]
epoch method = "hmyv2_getValidators"
]
method = 'hmyv2_getValidators'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#1439b580-fa3c-4d44-a79d-303390997a8c
""" """
params = [ params = [epoch]
epoch method = "hmyv2_getValidatorKeys"
]
method = 'hmyv2_getValidatorKeys'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e

@ -54,9 +54,21 @@ import requests
from .util import get_bls_build_variables, get_gopath from .util import get_bls_build_variables, get_gopath
if sys.platform.startswith("linux"): 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: 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. _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. _account_keystore_path = "~/.hmy/account-keys" # Internal path to account keystore, will match the current binary.
_binary_path = "hmy" # Internal binary path. _binary_path = "hmy" # Internal binary path.
@ -149,14 +161,16 @@ def _make_call_command(command):
if isinstance(command, list): if isinstance(command, list):
command_toks = command command_toks = command
else: 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): for i, string in enumerate(all_strings):
command = command.replace(string, f"{_arg_prefix}_{i}") command = command.replace(string, f"{_arg_prefix}_{i}")
command_toks_prefix = [el for el in command.split(" ") if el] command_toks_prefix = [el for el in command.split(" ") if el]
command_toks = [] command_toks = []
for el in command_toks_prefix: for el in command_toks_prefix:
if el.startswith(f'"{_arg_prefix}_') and el.endswith(f'"'): 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]) command_toks.append(all_strings[index])
else: else:
command_toks.append(el) command_toks.append(el)
@ -183,8 +197,12 @@ def is_valid_binary(path):
path = os.path.realpath(path) path = os.path.realpath(path)
os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
try: try:
proc = subprocess.Popen([path, "version"], env=environment, proc = subprocess.Popen(
stdout=subprocess.PIPE, stderr=subprocess.PIPE) [path, "version"],
env=environment,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out, err = proc.communicate() out, err = proc.communicate()
if not err: if not err:
return False return False
@ -223,12 +241,18 @@ def get_version():
""" """
:return: The version string of the CLI binary. :return: The version string of the CLI binary.
""" """
proc = subprocess.Popen([_binary_path, "version"], env=environment, proc = subprocess.Popen(
stdout=subprocess.PIPE, stderr=subprocess.PIPE) [_binary_path, "version"],
env=environment,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out, err = proc.communicate() out, err = proc.communicate()
if not err: if not err:
raise RuntimeError(f"Could not get version.\n" raise RuntimeError(
f"\tGot exit code {proc.returncode}. Expected non-empty error message.") f"Could not get version.\n"
f"\tGot exit code {proc.returncode}. Expected non-empty error message."
)
return err.decode().strip() return err.decode().strip()
@ -280,8 +304,9 @@ def remove_account(name):
try: try:
shutil.rmtree(keystore_path) shutil.rmtree(keystore_path)
except (shutil.Error, FileNotFoundError) as err: except (shutil.Error, FileNotFoundError) as err:
raise RuntimeError(f"Failed to delete dir: {keystore_path}\n" raise RuntimeError(
f"\tException: {err}") from err f"Failed to delete dir: {keystore_path}\n" f"\tException: {err}"
) from err
_sync_accounts() _sync_accounts()
@ -304,11 +329,14 @@ def single_call(command, timeout=60, error_ok=False):
""" """
command_toks = [_binary_path] + _make_call_command(command) command_toks = [_binary_path] + _make_call_command(command)
try: 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: except subprocess.CalledProcessError as err:
if not error_ok: if not error_ok:
raise RuntimeError(f"Bad CLI args: `{command}`\n " raise RuntimeError(
f"\tException: {err}") from err f"Bad CLI args: `{command}`\n " f"\tException: {err}"
) from err
return err.output.decode() return err.output.decode()
@ -321,11 +349,14 @@ def expect_call(command, timeout=60):
""" """
command_toks = _make_call_command(command) command_toks = _make_call_command(command)
try: 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 proc.delaybeforesend = None
except pexpect.ExceptionPexpect as err: except pexpect.ExceptionPexpect as err:
raise RuntimeError(f"Bad CLI args: `{command}`\n " raise RuntimeError(
f"\tException: {err}") from err f"Bad CLI args: `{command}`\n " f"\tException: {err}"
) from err
return proc return proc
@ -341,28 +372,43 @@ def download(path="./bin/hmy", replace=True, verbose=True):
""" """
path = os.path.realpath(path) path = os.path.realpath(path)
parent_dir = Path(path).parent 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: if not os.path.exists(path) or replace:
old_cwd = os.getcwd() old_cwd = os.getcwd()
os.makedirs(parent_dir, exist_ok=True) os.makedirs(parent_dir, exist_ok=True)
os.chdir(parent_dir) os.chdir(parent_dir)
hmy_script_path = os.path.join(parent_dir, "hmy.sh") hmy_script_path = os.path.join(parent_dir, "hmy.sh")
with open(hmy_script_path, 'w') as f: with open(hmy_script_path, "w") as f:
f.write(requests.get("https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh") f.write(
.content.decode()) 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) os.chmod(hmy_script_path, os.stat(hmy_script_path).st_mode | stat.S_IEXEC)
same_name_file = False 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 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: if verbose:
subprocess.call([hmy_script_path, '-d']) subprocess.call([hmy_script_path, "-d"])
else: 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) os.rename(os.path.join(parent_dir, "hmy"), path)
if same_name_file: 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: if verbose:
print(f"Saved harmony binary to: `{path}`") print(f"Saved harmony binary to: `{path}`")
os.chdir(old_cwd) 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)) files_in_parent_dir = set(os.listdir(parent_dir))
if files_in_parent_dir.intersection(_libs) == _libs: if files_in_parent_dir.intersection(_libs) == _libs:
env["DYLD_FALLBACK_LIBRARY_PATH"] = parent_dir env["DYLD_FALLBACK_LIBRARY_PATH"] = parent_dir
elif os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/bls") \ elif os.path.exists(
and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"): 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()) env.update(get_bls_build_variables())
else: 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: 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 return env

@ -1,25 +1,28 @@
from .rpc.request import ( from .rpc.request import rpc_request
rpc_request
)
from .transaction import ( from .transaction import get_transaction_receipt
get_transaction_receipt
)
from .exceptions import ( from .exceptions import InvalidRPCReplyError
InvalidRPCReplyError
)
_default_endpoint = 'http://localhost:9500' _default_endpoint = "http://localhost:9500"
_default_timeout = 30 _default_timeout = 30
######################### #########################
# Smart contract RPCs # Smart contract RPCs
######################### #########################
def call(to, block_num, from_address=None, gas=None, gas_price=None, value=None, data=None, def call(
endpoint=_default_endpoint, timeout=_default_timeout) -> str: 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 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 = [ params = [
{ {
'to': to, "to": to,
'from': from_address, "from": from_address,
'gas': gas, "gas": gas,
'gasPrice': gas_price, "gasPrice": gas_price,
'value': value, "value": value,
'data': data "data": data,
}, },
block_num block_num,
] ]
method = 'hmyv2_call' method = "hmyv2_call"
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/?version=latest#b9bbfe71-8127-4dda-b26c-ff95c4c22abd
""" """
params = [ { params = [
'to': to, {
'from': from_address, "to": to,
'gas': gas, "from": from_address,
'gasPrice': gas_price, "gas": gas,
'value': value, "gasPrice": gas_price,
'data': data "value": value,
} ] "data": data,
method = 'hmyv2_estimateGas' }
]
method = "hmyv2_estimateGas"
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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://api.hmny.io/?version=latest#e13e9d78-9322-4dc8-8917-f2e721a8e556
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L59 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L59
""" """
params = [ params = [address, block_num]
address, method = "hmyv2_getCode"
block_num
]
method = 'hmyv2_getCode'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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://api.hmny.io/?version=latest#fa8ac8bd-952d-4149-968c-857ca76da43f
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L84 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L84
""" """
params = [ params = [address, key, block_num]
address, method = "hmyv2_getStorageAt"
key,
block_num
]
method = 'hmyv2_getStorageAt'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 Get address of the contract which was deployed in the transaction
represented by tx_hash represented by tx_hash

@ -1,8 +1,5 @@
from .rpc.exceptions import ( from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError
RPCError,
RequestsError,
RequestsTimeoutError
)
class InvalidRPCReplyError(RuntimeError): class InvalidRPCReplyError(RuntimeError):
""" """
@ -11,7 +8,8 @@ class InvalidRPCReplyError(RuntimeError):
""" """
def __init__(self, method, endpoint): 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): class InvalidValidatorError(ValueError):
""" """
@ -19,11 +17,11 @@ class InvalidValidatorError(ValueError):
""" """
errors = { errors = {
1: 'Invalid ONE address', 1: "Invalid ONE address",
2: 'Field not initialized', 2: "Field not initialized",
3: 'Invalid field input', 3: "Invalid field input",
4: 'Error checking blockchain', 4: "Error checking blockchain",
5: 'Unable to import validator information from blockchain' 5: "Unable to import validator information from blockchain",
} }
def __init__(self, err_code, msg): def __init__(self, err_code, msg):
@ -32,7 +30,8 @@ class InvalidValidatorError(ValueError):
super().__init__(msg) super().__init__(msg)
def __str__(self): 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): class TxConfirmationTimedoutError(AssertionError):
""" """
@ -41,4 +40,4 @@ class TxConfirmationTimedoutError(AssertionError):
""" """
def __init__(self, msg): def __init__(self, msg):
super().__init__(f'{msg}') super().__init__(f"{msg}")

@ -9,8 +9,8 @@ import logging.handlers
class _GZipRotator: class _GZipRotator:
def __call__(self, source, dest): def __call__(self, source, dest):
os.rename(source, dest) os.rename(source, dest)
f_in = open(dest, 'rb') f_in = open(dest, "rb")
f_out = gzip.open("%s.gz" % dest, 'wb') f_out = gzip.open("%s.gz" % dest, "wb")
f_out.writelines(f_in) f_out.writelines(f_in)
f_out.close() f_out.close()
f_in.close() f_in.close()
@ -27,13 +27,14 @@ class ControlledLogger:
:param logger_name: The name of the logger and logfile :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). :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 = log_dir[:-1]
log_dir = os.path.realpath(log_dir) log_dir = os.path.realpath(log_dir)
os.makedirs(log_dir, exist_ok=True) os.makedirs(log_dir, exist_ok=True)
handler = logging.handlers.TimedRotatingFileHandler(f"{log_dir}/{logger_name}.log", 'midnight', 1, handler = logging.handlers.TimedRotatingFileHandler(
backupCount=backup_count) f"{log_dir}/{logger_name}.log", "midnight", 1, backupCount=backup_count
handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) )
handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s"))
handler.rotator = _GZipRotator() handler.rotator = _GZipRotator()
self.filename = handler.baseFilename self.filename = handler.baseFilename
@ -63,8 +64,9 @@ class ControlledLogger:
:param msg: The info message to log :param msg: The info message to log
""" """
self._lock.acquire() self._lock.acquire()
self.info_buffer.append(f"[{threading.get_ident()}] " self.info_buffer.append(
f"{datetime.datetime.utcnow()} : {msg}") f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
)
self._lock.release() self._lock.release()
def debug(self, msg): def debug(self, msg):
@ -72,8 +74,9 @@ class ControlledLogger:
:param msg: The debug message to log :param msg: The debug message to log
""" """
self._lock.acquire() self._lock.acquire()
self.debug_buffer.append(f"[{threading.get_ident()}] " self.debug_buffer.append(
f"{datetime.datetime.utcnow()} : {msg}") f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
)
self._lock.release() self._lock.release()
def warning(self, msg): def warning(self, msg):
@ -81,8 +84,9 @@ class ControlledLogger:
:param msg: The warning message to log :param msg: The warning message to log
""" """
self._lock.acquire() self._lock.acquire()
self.warning_buffer.append(f"[{threading.get_ident()}] " self.warning_buffer.append(
f"{datetime.datetime.utcnow()} : {msg}") f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
)
self._lock.release() self._lock.release()
def error(self, msg): def error(self, msg):
@ -90,33 +94,34 @@ class ControlledLogger:
:param msg: The error message to log :param msg: The error message to log
""" """
self._lock.acquire() self._lock.acquire()
self.error_buffer.append(f"[{threading.get_ident()}] " self.error_buffer.append(
f"{datetime.datetime.utcnow()} : {msg}") f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
)
self._lock.release() self._lock.release()
def print_info(self): def print_info(self):
""" """
Prints the current info buffer but does not flush it to log file. 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): def print_debug(self):
""" """
Prints the current debug buffer but does not flush it to log file. 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): def print_warning(self):
""" """
Prints the current warning buffer but does not flush it to log file. 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): def print_error(self):
""" """
Prints the current error buffer but does not flush it to log file. 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): def write(self):
""" """

@ -8,7 +8,8 @@ class RPCError(RuntimeError):
def __init__(self, method, endpoint, error): def __init__(self, method, endpoint, error):
self.error = 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): class RequestsError(requests.exceptions.RequestException):
""" """
@ -16,7 +17,8 @@ class RequestsError(requests.exceptions.RequestException):
""" """
def __init__(self, endpoint): def __init__(self, endpoint):
super().__init__(f'Error connecting to {endpoint}') super().__init__(f"Error connecting to {endpoint}")
class RequestsTimeoutError(requests.exceptions.Timeout): class RequestsTimeoutError(requests.exceptions.Timeout):
""" """
@ -24,4 +26,4 @@ class RequestsTimeoutError(requests.exceptions.Timeout):
""" """
def __init__(self, endpoint): def __init__(self, endpoint):
super().__init__(f'Error connecting to {endpoint}') super().__init__(f"Error connecting to {endpoint}")

@ -2,18 +2,16 @@ import json
import requests import requests
from .exceptions import ( from .exceptions import RequestsError, RequestsTimeoutError, RPCError
RequestsError,
RequestsTimeoutError,
RPCError
)
_default_endpoint = 'http://localhost:9500' _default_endpoint = "http://localhost:9500"
_default_timeout = 30 _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 Basic RPC request
@ -45,21 +43,20 @@ def base_request(method, params=None, endpoint=_default_endpoint, timeout=_defau
if params is None: if params is None:
params = [] params = []
elif not isinstance(params, list): elif not isinstance(params, list):
raise TypeError(f'invalid type {params.__class__}') raise TypeError(f"invalid type {params.__class__}")
try: try:
payload = { payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params}
"id": "1", headers = {"Content-Type": "application/json"}
"jsonrpc": "2.0",
"method": method, resp = requests.request(
"params": params "POST",
} endpoint,
headers = { headers=headers,
'Content-Type': 'application/json' data=json.dumps(payload),
} timeout=timeout,
allow_redirects=True,
resp = requests.request('POST', endpoint, headers=headers, data=json.dumps(payload), )
timeout=timeout, allow_redirects=True)
return resp.content return resp.content
except requests.exceptions.Timeout as err: except requests.exceptions.Timeout as err:
raise RequestsTimeoutError(endpoint) from 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 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 RPC request
@ -106,8 +105,8 @@ def rpc_request(method, params=None, endpoint=_default_endpoint, timeout=_defaul
try: try:
resp = json.loads(raw_resp) resp = json.loads(raw_resp)
if 'error' in resp: if "error" in resp:
raise RPCError(method, endpoint, str(resp['error'])) raise RPCError(method, endpoint, str(resp["error"]))
return resp return resp
except json.decoder.JSONDecodeError as err: except json.decoder.JSONDecodeError as err:
raise RPCError(method, endpoint, raw_resp) from err raise RPCError(method, endpoint, raw_resp) from err

@ -1,33 +1,16 @@
import rlp import rlp
from eth_utils.curried import ( from eth_utils.curried import keccak, to_int, hexstr_if_str, apply_formatters_to_dict
keccak,
to_int,
hexstr_if_str,
apply_formatters_to_dict
)
from rlp.sedes import ( from rlp.sedes import big_endian_int, Binary, binary
big_endian_int,
Binary,
binary
)
from eth_account import ( from eth_account import Account
Account
)
from eth_rlp import ( from eth_rlp import HashableRLP
HashableRLP
)
from hexbytes import ( from hexbytes import HexBytes
HexBytes
)
from eth_account._utils.signing import ( from eth_account._utils.signing import sign_transaction_hash
sign_transaction_hash
)
from eth_account._utils.legacy_transactions import ( from eth_account._utils.legacy_transactions import (
Transaction as SignedEthereumTxData, Transaction as SignedEthereumTxData,
@ -35,96 +18,101 @@ from eth_account._utils.legacy_transactions import (
LEGACY_TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS, LEGACY_TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS,
TRANSACTION_DEFAULTS, TRANSACTION_DEFAULTS,
chain_id_to_v, chain_id_to_v,
UNSIGNED_TRANSACTION_FIELDS UNSIGNED_TRANSACTION_FIELDS,
) )
from cytoolz import ( from cytoolz import dissoc, pipe, merge, partial
dissoc,
pipe,
merge,
partial
)
from eth_account.datastructures import ( from eth_account.datastructures import SignedTransaction
SignedTransaction
)
from .util import ( from .util import chain_id_to_int, convert_one_to_hex
chain_id_to_int,
convert_one_to_hex
)
HARMONY_FORMATTERS = dict( HARMONY_FORMATTERS = dict(
ETHEREUM_FORMATTERS, ETHEREUM_FORMATTERS,
shardID=hexstr_if_str(to_int), # additional fields for Harmony transaction shardID=hexstr_if_str(to_int), # additional fields for Harmony transaction
toShardID=hexstr_if_str(to_int), # which may be cross shard toShardID=hexstr_if_str(to_int), # which may be cross shard
) )
class UnsignedHarmonyTxData(HashableRLP): class UnsignedHarmonyTxData(HashableRLP):
fields = ( fields = (
('nonce', big_endian_int), ("nonce", big_endian_int),
('gasPrice', big_endian_int), ("gasPrice", big_endian_int),
('gas', big_endian_int), ("gas", big_endian_int),
('shardID', big_endian_int), ("shardID", big_endian_int),
('toShardID', big_endian_int), ("toShardID", big_endian_int),
('to', Binary.fixed_length(20, allow_empty=True)), ("to", Binary.fixed_length(20, allow_empty=True)),
('value', big_endian_int), ("value", big_endian_int),
('data', binary), ("data", binary),
) )
class SignedHarmonyTxData(HashableRLP): class SignedHarmonyTxData(HashableRLP):
fields = UnsignedHarmonyTxData._meta.fields + ( fields = UnsignedHarmonyTxData._meta.fields + (
("v", big_endian_int), # Recovery value + 27 ("v", big_endian_int), # Recovery value + 27
("r", big_endian_int), # First 32 bytes ("r", big_endian_int), # First 32 bytes
("s", big_endian_int), # Next 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 (v, r, s) = vrs
chain_naive_transaction = dissoc( chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), "v", "r", "s")
unsigned_transaction.as_dict(), 'v', 'r', 's') if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, SignedHarmonyTxData)):
if isinstance(unsigned_transaction, (UnsignedHarmonyTxData,
SignedHarmonyTxData)):
serializer = SignedHarmonyTxData serializer = SignedHarmonyTxData
else: else:
serializer = SignedEthereumTxData serializer = SignedEthereumTxData
signed_transaction = serializer(v=v, r=r, s=s, **chain_naive_transaction) signed_transaction = serializer(v=v, r=r, s=s, **chain_naive_transaction)
return rlp.encode(signed_transaction) return rlp.encode(signed_transaction)
def serialize_transaction(filled_transaction): def serialize_transaction(filled_transaction):
'''serialize a signed/unsigned transaction''' """serialize a signed/unsigned transaction"""
if 'v' in filled_transaction: if "v" in filled_transaction:
if 'shardID' in filled_transaction: if "shardID" in filled_transaction:
serializer = SignedHarmonyTxData serializer = SignedHarmonyTxData
else: else:
serializer = SignedEthereumTxData serializer = SignedEthereumTxData
else: else:
if 'shardID' in filled_transaction: if "shardID" in filled_transaction:
serializer = UnsignedHarmonyTxData serializer = UnsignedHarmonyTxData
else: else:
serializer = UnsignedEthereumTxData serializer = UnsignedEthereumTxData
for f, _ in serializer._meta.fields: for f, _ in serializer._meta.fields:
assert f in filled_transaction, f'Could not find {f} in transaction' 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}) return serializer.from_dict(
{f: filled_transaction[f] for f, _ in serializer._meta.fields}
)
def sanitize_transaction(transaction_dict, private_key): def sanitize_transaction(transaction_dict, private_key):
'''remove the originating address from the dict and convert chainId to int''' """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 account = Account.from_key(
transaction_dict = transaction_dict.copy() # do not alter the original dictionary private_key
if 'from' in transaction_dict: ) # get account, from which you can derive public + private key
transaction_dict[ 'from' ] = convert_one_to_hex( transaction_dict[ 'from' ] ) transaction_dict = transaction_dict.copy() # do not alter the original dictionary
if transaction_dict[ 'from' ] == account.address: # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 if "from" in transaction_dict:
sanitized_transaction = dissoc(transaction_dict, 'from') 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: 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, account.address,
transaction_dict['from'], transaction_dict["from"],
)) )
if 'chainId' in transaction_dict: )
transaction_dict[ 'chainId' ] = chain_id_to_int( transaction_dict[ 'chainId' ] ) if "chainId" in transaction_dict:
transaction_dict["chainId"] = chain_id_to_int(transaction_dict["chainId"])
return account, transaction_dict return account, transaction_dict
def sign_transaction(transaction_dict, private_key) -> SignedTransaction: def sign_transaction(transaction_dict, private_key) -> SignedTransaction:
""" """
Sign a (non-staking) transaction dictionary with the specified private key 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/ https://readthedocs.org/projects/eth-account/downloads/pdf/stable/
""" """
account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key)
if 'to' in sanitized_transaction and sanitized_transaction[ 'to' ] is not None: if "to" in sanitized_transaction and sanitized_transaction["to"] is not None:
sanitized_transaction[ 'to' ] = convert_one_to_hex( sanitized_transaction[ 'to' ] ) 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 filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39
sanitized_transaction, sanitized_transaction,
dict, dict,
partial(merge, TRANSACTION_DEFAULTS), partial(merge, TRANSACTION_DEFAULTS),
chain_id_to_v, chain_id_to_v,
apply_formatters_to_dict(HARMONY_FORMATTERS) apply_formatters_to_dict(HARMONY_FORMATTERS),
) )
unsigned_transaction = serialize_transaction(filled_transaction) unsigned_transaction = serialize_transaction(filled_transaction)
transaction_hash = unsigned_transaction.hash() transaction_hash = unsigned_transaction.hash()
if isinstance(unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData)): if isinstance(
chain_id = None # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData)
):
chain_id = None # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26
else: else:
chain_id = unsigned_transaction.v chain_id = unsigned_transaction.v
(v, r, s) = sign_transaction_hash( (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id)
account._key_obj, transaction_hash, chain_id)
encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s)) encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s))
signed_transaction_hash = keccak(encoded_transaction) signed_transaction_hash = keccak(encoded_transaction)
return SignedTransaction( return SignedTransaction(

@ -1,18 +1,16 @@
from .rpc.request import ( from .rpc.request import rpc_request
rpc_request
)
from .exceptions import ( from .exceptions import InvalidRPCReplyError
InvalidRPCReplyError
)
_default_endpoint = 'http://localhost:9500' _default_endpoint = "http://localhost:9500"
_default_timeout = 30 _default_timeout = 30
################## ##################
# Validator RPCs # # 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 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 https://api.hmny.io/#69b93657-8d3c-4d20-9c9f-e51f08c9b3f5
""" """
method = 'hmyv2_getAllValidatorAddresses' method = "hmyv2_getAllValidatorAddresses"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#659ad999-14ca-4498-8f74-08ed347cab49
""" """
method = 'hmyv2_getValidatorInformation' method = "hmyv2_getValidatorInformation"
params = [ params = [validator_addr]
validator_addr
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#e90a6131-d67c-4110-96ef-b283d452632d
""" """
method = 'hmyv2_getElectedValidatorAddresses' method = "hmyv2_getElectedValidatorAddresses"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
""" """
Get validators list for a particular epoch 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152
""" """
method = 'hmyv2_getValidators' method = "hmyv2_getValidators"
params = [ params = [epoch]
epoch
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152
""" """
method = 'hmyv2_getValidatorKeys' method = "hmyv2_getValidatorKeys"
params = [ params = [epoch]
epoch
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L319
""" """
method = 'hmyv2_getValidatorInformationByBlockNumber' method = "hmyv2_getValidatorInformationByBlockNumber"
params = [ params = [validator_addr, block_num]
validator_addr,
block_num
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#df5f1631-7397-48e8-87b4-8dd873235b9c
""" """
method = 'hmyv2_getAllValidatorInformation' method = "hmyv2_getAllValidatorInformation"
params = [ params = [page]
page
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L352
""" """
method = 'hmyv2_getValidatorSelfDelegation' method = "hmyv2_getValidatorSelfDelegation"
params = [ params = [address]
address
]
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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) 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L379
""" """
method = 'hmyv2_getValidatorTotalDelegation' method = "hmyv2_getValidatorTotalDelegation"
params = [ params = [address]
address
]
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#a229253f-ca76-4b9d-88f5-9fd96e40d583
""" """
method = 'hmyv2_getAllValidatorInformationByBlockNumber' method = "hmyv2_getAllValidatorInformationByBlockNumber"
params = [ params = [page, block_num]
page,
block_num
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
################### ###################
# Delegation RPCs # # 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L413
""" """
method = 'hmyv2_getAllDelegationInformation' method = "hmyv2_getAllDelegationInformation"
params = [ params = [
page, page,
] ]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#454b032c-6072-4ecb-bf24-38b3d6d2af69
""" """
method = 'hmyv2_getDelegationsByDelegator' method = "hmyv2_getDelegationsByDelegator"
params = [ params = [delegator_addr]
delegator_addr
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#8ce13bda-e768-47b9-9dbe-193aba410b0a
""" """
method = 'hmyv2_getDelegationsByDelegatorByBlockNumber' method = "hmyv2_getDelegationsByDelegatorByBlockNumber"
params = [ params = [delegator_addr, block_num]
delegator_addr,
block_num
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L605
""" """
method = 'hmyv2_getDelegationByDelegatorAndValidator' method = "hmyv2_getDelegationByDelegatorAndValidator"
params = [ params = [delegator_addr, validator_address]
delegator_addr,
validator_address
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L653
""" """
method = 'hmyv2_getAvailableRedelegationBalance' method = "hmyv2_getAvailableRedelegationBalance"
params = [ params = [delegator_addr]
delegator_addr
]
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#2e02d8db-8fec-41d9-a672-2c9862f63f39
""" """
method = 'hmyv2_getDelegationsByValidator' method = "hmyv2_getDelegationsByValidator"
params = [ params = [validator_addr]
validator_addr
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
######################## ########################
# Staking Network RPCs # # 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 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 https://api.hmny.io/#78dd2d94-9ff1-4e0c-bbac-b4eec1cdf10b
""" """
method = 'hmyv2_getCurrentUtilityMetrics' method = "hmyv2_getCurrentUtilityMetrics"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#4a10fce0-2aa4-4583-bdcb-81ee0800993b
""" """
method = 'hmyv2_getStakingNetworkInfo' method = "hmyv2_getStakingNetworkInfo"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
""" """
Get voting committees for current & previous epoch 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 https://api.hmny.io/#8eef2fc4-92db-4610-a9cd-f7b75cfbd080
""" """
method = 'hmyv2_getSuperCommittees' method = "hmyv2_getSuperCommittees"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> int: def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
""" """
Get total staking by validators, only meant to be called on beaconchain 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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L102
""" """
method = 'hmyv2_getTotalStaking' method = "hmyv2_getTotalStaking"
try: 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: except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#bef93b3f-6763-4121-9c17-f0b0d9e5cc40
""" """
method = 'hmyv2_getMedianRawStakeSnapshot' method = "hmyv2_getMedianRawStakeSnapshot"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e

@ -6,29 +6,19 @@ from cytoolz import (
identity, identity,
) )
from hexbytes import ( from hexbytes import HexBytes
HexBytes
)
import rlp import rlp
import math import math
from decimal import ( from decimal import Decimal
Decimal
)
from eth_account.datastructures import ( from eth_account.datastructures import SignedTransaction
SignedTransaction
)
from eth_account._utils.signing import ( from eth_account._utils.signing import sign_transaction_hash
sign_transaction_hash
)
from eth_account._utils.legacy_transactions import ( from eth_account._utils.legacy_transactions import chain_id_to_v
chain_id_to_v
)
from eth_utils.curried import ( from eth_utils.curried import (
hexstr_if_str, hexstr_if_str,
@ -37,12 +27,10 @@ from eth_utils.curried import (
apply_formatters_to_dict, apply_formatters_to_dict,
to_int, to_int,
apply_formatters_to_sequence, apply_formatters_to_sequence,
apply_formatter_to_array apply_formatter_to_array,
) )
from .signing import ( from .signing import sanitize_transaction
sanitize_transaction
)
from .staking_structures import ( from .staking_structures import (
FORMATTERS, FORMATTERS,
@ -51,14 +39,15 @@ from .staking_structures import (
CreateValidator, CreateValidator,
EditValidator, EditValidator,
DelegateOrUndelegate, DelegateOrUndelegate,
CollectRewards CollectRewards,
) )
from .util import ( from .util import convert_one_to_hex
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 Convert from staking percentage to integer
For example, 0.1 becomes 1000000000000000000 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 AssertionError, if data types are not as expected
ValueError, if the input type is not supported 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): if isinstance(value, Decimal):
value = str(value) value = str(value)
value1 = value; value1 = value
if value[0] == '-': if value[0] == "-":
raise ValueError('Negative numbers are not accepted') raise ValueError("Negative numbers are not accepted")
if value[0] == '+': if value[0] == "+":
value1 = value[1:] value1 = value[1:]
if len(value1) == 0: if len(value1) == 0:
raise ValueError('StakingDecimal string is empty') raise ValueError("StakingDecimal string is empty")
spaced = value1.split(' ') spaced = value1.split(" ")
if len(spaced) > 1: if len(spaced) > 1:
raise ValueError('Bad decimal string') raise ValueError("Bad decimal string")
splitted = value1.split('.') splitted = value1.split(".")
combined_str = splitted[0] combined_str = splitted[0]
if len(splitted) == 2: if len(splitted) == 2:
length = len(splitted[1]) length = len(splitted[1])
if length == 0 or len(combined_str) == 0: if length == 0 or len(combined_str) == 0:
raise ValueError('Bad StakingDecimal length') raise ValueError("Bad StakingDecimal length")
if splitted[1][0] == '-': if splitted[1][0] == "-":
raise ValueError('Bad StakingDecimal string') raise ValueError("Bad StakingDecimal string")
combined_str += splitted[1] combined_str += splitted[1]
elif len(splitted) > 2: 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: 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 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) 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 return val
def _get_account_and_transaction(transaction_dict, private_key): def _get_account_and_transaction(transaction_dict, private_key):
""" """
Create account from private key and sanitize the transaction 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 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 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 account, sanitized_transaction = sanitize_transaction(
sanitized_transaction['directive'] = sanitized_transaction['directive'].value # convert to value, like in TypeScript 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 return account, sanitized_transaction
def _sign_transaction_generic(account, sanitized_transaction, parent_serializer): def _sign_transaction_generic(account, sanitized_transaction, parent_serializer):
""" """
Sign a generic staking transaction, given the serializer base class and account 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 rlp.exceptions.ObjectSerializationError, if data types are not as expected
""" """
# obtain the serializers # obtain the serializers
if sanitized_transaction.get('chainId', 0) == 0: if sanitized_transaction.get("chainId", 0) == 0:
unsigned_serializer, signed_serializer = parent_serializer.Unsigned(), parent_serializer.Signed() # unsigned, signed unsigned_serializer, signed_serializer = (
parent_serializer.Unsigned(),
parent_serializer.Signed(),
) # unsigned, signed
else: 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 # 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, sanitized_transaction,
dict, dict,
partial(merge, {'chainId': None}), partial(merge, {"chainId": None}),
chain_id_to_v, # will move chain id to v and add v/r/s chain_id_to_v, # will move chain id to v and add v/r/s
apply_formatters_to_dict(FORMATTERS) apply_formatters_to_dict(FORMATTERS),
) )
# get the unsigned transaction # get the unsigned transaction
for f, _ in unsigned_serializer._meta.fields: for f, _ in unsigned_serializer._meta.fields:
assert f in filled_transaction, f'Could not find {f} in transaction' assert f in filled_transaction, f"Could not find {f} in transaction"
unsigned_transaction = unsigned_serializer.from_dict(\ unsigned_transaction = unsigned_serializer.from_dict(
{f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields}) # drop extras silently {f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields}
) # drop extras silently
# sign the unsigned transaction # sign the unsigned transaction
if 'v' in unsigned_transaction.as_dict(): if "v" in unsigned_transaction.as_dict():
chain_id = unsigned_transaction.v chain_id = unsigned_transaction.v
else: else:
chain_id = None chain_id = None
transaction_hash = unsigned_transaction.hash() transaction_hash = unsigned_transaction.hash()
(v, r, s) = sign_transaction_hash( (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id)
account._key_obj, transaction_hash, chain_id)
chain_naive_transaction = dissoc( 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 # serialize it
signed_transaction = signed_serializer( 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, r=r,
s=s, # in the below statement, remove everything not expected by signed_serializer 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'}) **{
f: chain_naive_transaction[f]
for f, _ in signed_serializer._meta.fields
if f not in "vrs"
},
)
# encode it # encode it
encoded_transaction = rlp.encode(signed_transaction) encoded_transaction = rlp.encode(signed_transaction)
# hash it # hash it
@ -203,29 +217,34 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer)
v=v, v=v,
) )
def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate): def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate):
""" """
Sign a delegate or undelegate transaction Sign a delegate or undelegate transaction
See sign_staking_transaction for details See sign_staking_transaction for details
""" """
# preliminary steps # preliminary steps
if transaction_dict['directive'] not in [ Directive.Delegate, Directive.Undelegate ]: if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]:
raise TypeError('Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate') raise TypeError(
"Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate"
)
# first common step # 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 # encode the stakeMsg
sanitized_transaction['stakeMsg'] = \ sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence(
apply_formatters_to_sequence( [ [hexstr_if_str(to_bytes), hexstr_if_str(to_bytes), hexstr_if_str(to_int)],
hexstr_if_str(to_bytes), [
hexstr_if_str(to_bytes), convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")),
hexstr_if_str(to_int) convert_one_to_hex(sanitized_transaction.pop("validatorAddress")),
], [ sanitized_transaction.pop("amount"),
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
) )
return _sign_transaction_generic(account, sanitized_transaction, DelegateOrUndelegate)
def _sign_collect_rewards(transaction_dict, private_key): 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 See sign_staking_transaction for details
""" """
# preliminary steps # preliminary steps
if transaction_dict['directive'] != Directive.CollectRewards: if transaction_dict["directive"] != Directive.CollectRewards:
raise TypeError('Only CollectRewards is supported by _sign_collect_rewards') raise TypeError("Only CollectRewards is supported by _sign_collect_rewards")
# first common step # 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 # encode the stakeMsg
sanitized_transaction['stakeMsg'] = \ sanitized_transaction["stakeMsg"] = [
[hexstr_if_str(to_bytes)(convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')))] hexstr_if_str(to_bytes)(
convert_one_to_hex(sanitized_transaction.pop("delegatorAddress"))
)
]
return _sign_transaction_generic(account, sanitized_transaction, CollectRewards) return _sign_transaction_generic(account, sanitized_transaction, CollectRewards)
def _sign_create_validator(transaction_dict, private_key): def _sign_create_validator(transaction_dict, private_key):
""" """
Sign a create validator transaction Sign a create validator transaction
See sign_staking_transaction for details See sign_staking_transaction for details
""" """
# preliminary steps # preliminary steps
if transaction_dict['directive'] != Directive.CreateValidator: if transaction_dict["directive"] != Directive.CreateValidator:
raise TypeError('Only CreateValidator is supported by _sign_create_or_edit_validator') raise TypeError(
"Only CreateValidator is supported by _sign_create_or_edit_validator"
)
# first common step # 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 # encode the stakeMsg
description = [ description = [
sanitized_transaction.pop('name'), sanitized_transaction.pop("name"),
sanitized_transaction.pop('identity'), sanitized_transaction.pop("identity"),
sanitized_transaction.pop('website'), sanitized_transaction.pop("website"),
sanitized_transaction.pop('security-contact'), sanitized_transaction.pop("security-contact"),
sanitized_transaction.pop('details'), sanitized_transaction.pop("details"),
] ]
commission = apply_formatter_to_array( hexstr_if_str(to_int), # formatter 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("rate")),
_convert_staking_percentage_to_number(sanitized_transaction.pop('max-rate')), _convert_staking_percentage_to_number(
_convert_staking_percentage_to_number(sanitized_transaction.pop('max-change-rate')), sanitized_transaction.pop("max-rate")
] ),
_convert_staking_percentage_to_number(
sanitized_transaction.pop("max-change-rate")
),
],
) )
commission = [ [element] for element in commission ] commission = [[element] for element in commission]
bls_keys = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter bls_keys = apply_formatter_to_array(
sanitized_transaction.pop('bls-public-keys') 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 bls_key_sigs = apply_formatter_to_array(
sanitized_transaction.pop('bls-key-sigs') hexstr_if_str(to_bytes), sanitized_transaction.pop("bls-key-sigs") # formatter
) )
sanitized_transaction['stakeMsg'] = \ sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence(
apply_formatters_to_sequence( [ [
hexstr_if_str(to_bytes), # address hexstr_if_str(to_bytes), # address
identity, # description identity, # description
identity, # commission rates identity, # commission rates
hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped hexstr_if_str(
hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped to_int
identity, # bls public keys ), # min self delegation (in ONE), decimals are silently dropped
identity, # bls key sigs hexstr_if_str(
hexstr_if_str(to_int), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) to_int
], [ ), # max total delegation (in ONE), decimals are silently dropped
convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), 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, description,
commission, commission,
math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly math.floor(
math.floor(sanitized_transaction.pop('max-total-delegation')), sanitized_transaction.pop("min-self-delegation")
), # Decimal floors it correctly
math.floor(sanitized_transaction.pop("max-total-delegation")),
bls_keys, bls_keys,
bls_key_sigs, bls_key_sigs,
math.floor(sanitized_transaction.pop('amount')), math.floor(sanitized_transaction.pop("amount")),
] ],
) )
return _sign_transaction_generic(account, sanitized_transaction, CreateValidator) return _sign_transaction_generic(account, sanitized_transaction, CreateValidator)
def _sign_edit_validator(transaction_dict, private_key): def _sign_edit_validator(transaction_dict, private_key):
""" """
Sign an edit validator transaction Sign an edit validator transaction
See sign_staking_transaction for details See sign_staking_transaction for details
""" """
# preliminary steps # preliminary steps
if transaction_dict['directive'] != Directive.EditValidator: if transaction_dict["directive"] != Directive.EditValidator:
raise TypeError('Only EditValidator is supported by _sign_create_or_edit_validator') raise TypeError(
"Only EditValidator is supported by _sign_create_or_edit_validator"
)
# first common step # 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 # encode the stakeMsg
description = [ description = [
sanitized_transaction.pop('name'), sanitized_transaction.pop("name"),
sanitized_transaction.pop('identity'), sanitized_transaction.pop("identity"),
sanitized_transaction.pop('website'), sanitized_transaction.pop("website"),
sanitized_transaction.pop('security-contact'), sanitized_transaction.pop("security-contact"),
sanitized_transaction.pop('details'), sanitized_transaction.pop("details"),
] ]
sanitized_transaction['stakeMsg'] = \ sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence(
apply_formatters_to_sequence( [ [
hexstr_if_str(to_bytes), # address hexstr_if_str(to_bytes), # address
identity, # description identity, # description
identity, # new rate (it's in a list so can't do hexstr_if_str) 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(
hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped to_int
hexstr_if_str(to_bytes), # key to remove ), # min self delegation (in ONE), decimals are silently dropped
hexstr_if_str(to_bytes), # key to add hexstr_if_str(
], [ to_int
convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), ), # 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, description,
[ _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')) ], [_convert_staking_percentage_to_number(sanitized_transaction.pop("rate"))],
math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly math.floor(
math.floor(sanitized_transaction.pop('max-total-delegation')), sanitized_transaction.pop("min-self-delegation")
sanitized_transaction.pop('bls-key-to-remove'), ), # Decimal floors it correctly
sanitized_transaction.pop('bls-key-to-add') 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) return _sign_transaction_generic(account, sanitized_transaction, EditValidator)
def sign_staking_transaction(transaction_dict, private_key): def sign_staking_transaction(transaction_dict, private_key):
""" """
Sign a supplied transaction_dict with the 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 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' # 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 "chainId" in transaction_dict, "chainId missing"
assert 'directive' in transaction_dict, 'Staking transaction type not specified' assert "directive" in transaction_dict, "Staking transaction type not specified"
assert isinstance(transaction_dict['directive'], Directive), 'Unknown staking transaction type' assert isinstance(
if transaction_dict['directive'] == Directive.CollectRewards: transaction_dict["directive"], Directive
), "Unknown staking transaction type"
if transaction_dict["directive"] == Directive.CollectRewards:
return _sign_collect_rewards(transaction_dict, private_key) 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) 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) 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) 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) return _sign_edit_validator(transaction_dict, private_key)

@ -1,219 +1,294 @@
from enum import ( from enum import Enum, auto
Enum,
auto
)
from rlp.sedes import ( from rlp.sedes import big_endian_int, Binary, CountableList, List, Text
big_endian_int,
Binary,
CountableList,
List,
Text
)
from eth_rlp import ( from eth_rlp import HashableRLP
HashableRLP
)
from eth_utils.curried import ( from eth_utils.curried import (
to_int, to_int,
hexstr_if_str, hexstr_if_str,
) )
class StakingSettings: class StakingSettings:
PRECISION = 18 PRECISION = 18
MAX_DECIMAL = 1000000000000000000 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): def _generate_next_value_(name, start, count, last_values):
return count return count
CreateValidator = auto() CreateValidator = auto()
EditValidator = auto() EditValidator = auto()
Delegate = auto() Delegate = auto()
Undelegate = auto() Undelegate = auto()
CollectRewards = auto() CollectRewards = auto()
FORMATTERS = { FORMATTERS = {
'directive': hexstr_if_str(to_int), # delegatorAddress is already formatted before the call "directive": hexstr_if_str(
'nonce': hexstr_if_str(to_int), to_int
'gasPrice': hexstr_if_str(to_int), ), # delegatorAddress is already formatted before the call
'gasLimit': hexstr_if_str(to_int), "nonce": hexstr_if_str(to_int),
'chainId': 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: class CollectRewards:
@staticmethod @staticmethod
def UnsignedChainId(): def UnsignedChainId():
class UnsignedChainId(HashableRLP): class UnsignedChainId(HashableRLP):
fields = ( fields = (
('directive', big_endian_int), ("directive", big_endian_int),
('stakeMsg', CountableList(Binary.fixed_length(20, allow_empty=True))), ("stakeMsg", CountableList(Binary.fixed_length(20, allow_empty=True))),
('nonce', big_endian_int), ("nonce", big_endian_int),
('gasPrice', big_endian_int), ("gasPrice", big_endian_int),
('gasLimit', big_endian_int), ("gasLimit", big_endian_int),
('chainId', big_endian_int), ("chainId", big_endian_int),
) )
return UnsignedChainId return UnsignedChainId
@staticmethod @staticmethod
def SignedChainId(): def SignedChainId():
class SignedChainId(HashableRLP): class SignedChainId(HashableRLP):
fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId fields = CollectRewards.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
("v", big_endian_int), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return SignedChainId return SignedChainId
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned(HashableRLP): class Unsigned(HashableRLP):
fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId
return Unsigned return Unsigned
@staticmethod @staticmethod
def Signed(): def Signed():
class Signed(HashableRLP): 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), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return Signed return Signed
class DelegateOrUndelegate: class DelegateOrUndelegate:
@staticmethod @staticmethod
def UnsignedChainId(): def UnsignedChainId():
class UnsignedChainId(HashableRLP): class UnsignedChainId(HashableRLP):
fields = ( fields = (
('directive', 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), "stakeMsg",
('gasPrice', big_endian_int), List(
('gasLimit', big_endian_int), [
('chainId', big_endian_int), 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 return UnsignedChainId
@staticmethod @staticmethod
def SignedChainId(): def SignedChainId():
class SignedChainId(HashableRLP): class SignedChainId(HashableRLP):
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
("v", big_endian_int), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return SignedChainId return SignedChainId
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned(HashableRLP): class Unsigned(HashableRLP):
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] # drop chainId fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[
:-1
] # drop chainId
return Unsigned return Unsigned
@staticmethod @staticmethod
def Signed(): def Signed():
class Signed(HashableRLP): 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), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return Signed return Signed
class CreateValidator: class CreateValidator:
@staticmethod @staticmethod
def UnsignedChainId(): def UnsignedChainId():
class UnsignedChainId(HashableRLP): class UnsignedChainId(HashableRLP):
fields = ( fields = (
('directive', big_endian_int), ("directive", big_endian_int),
('stakeMsg', List([ # list with the following members (
Binary.fixed_length(20, allow_empty=True), # validatorAddress "stakeMsg",
List([Text()]*5,True), # description is Text of 5 elements List(
List([List([big_endian_int],True)]*3,True), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] [ # list with the following members
big_endian_int, # min self delegation Binary.fixed_length(
big_endian_int, # max total delegation 20, allow_empty=True
CountableList(Binary.fixed_length(48, allow_empty=True)), # bls-public-keys array of unspecified length, each key of 48 ), # validatorAddress
CountableList(Binary.fixed_length(96, allow_empty=True)), # bls-key-sigs array of unspecified length, each sig of 96 List(
big_endian_int, # amount [Text()] * 5, True
], True)), # strictly these number of elements ), # description is Text of 5 elements
('nonce', big_endian_int), List(
('gasPrice', big_endian_int), [List([big_endian_int], True)] * 3, True
('gasLimit', big_endian_int), ), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ]
('chainId', big_endian_int), 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 return UnsignedChainId
@staticmethod @staticmethod
def SignedChainId(): def SignedChainId():
class SignedChainId(HashableRLP): class SignedChainId(HashableRLP):
fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId fields = CreateValidator.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
("v", big_endian_int), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return SignedChainId return SignedChainId
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned(HashableRLP): class Unsigned(HashableRLP):
fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId
return Unsigned return Unsigned
@staticmethod @staticmethod
def Signed(): def Signed():
class Signed(HashableRLP): 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), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return Signed return Signed
class EditValidator: class EditValidator:
@staticmethod @staticmethod
def UnsignedChainId(): def UnsignedChainId():
class UnsignedChainId(HashableRLP): class UnsignedChainId(HashableRLP):
fields = ( fields = (
('directive', big_endian_int), ("directive", big_endian_int),
('stakeMsg', List([ # list with the following members (
Binary.fixed_length(20, allow_empty=True), # validatorAddress "stakeMsg",
List([Text()]*5,True), # description is Text of 5 elements List(
List([big_endian_int],True), # new rate is in a list [ # list with the following members
big_endian_int, # min self delegation Binary.fixed_length(
big_endian_int, # max total delegation 20, allow_empty=True
Binary.fixed_length(48, allow_empty=True), # slot key to remove ), # validatorAddress
Binary.fixed_length(48, allow_empty=True), # slot key to add List(
], True)), # strictly these number of elements [Text()] * 5, True
('nonce', big_endian_int), ), # description is Text of 5 elements
('gasPrice', big_endian_int), List([big_endian_int], True), # new rate is in a list
('gasLimit', big_endian_int), big_endian_int, # min self delegation
('chainId', big_endian_int), 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 return UnsignedChainId
@staticmethod @staticmethod
def SignedChainId(): def SignedChainId():
class SignedChainId(HashableRLP): class SignedChainId(HashableRLP):
fields = EditValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId fields = EditValidator.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
("v", big_endian_int), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return SignedChainId return SignedChainId
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned(HashableRLP): class Unsigned(HashableRLP):
fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId
return Unsigned return Unsigned
@staticmethod @staticmethod
def Signed(): def Signed():
class Signed(HashableRLP): 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), ("v", big_endian_int),
("r", big_endian_int), ("r", big_endian_int),
("s", big_endian_int), ("s", big_endian_int),
) )
return Signed return Signed

@ -1,21 +1,18 @@
from .rpc.request import ( from .rpc.request import rpc_request
rpc_request from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError
)
from .exceptions import (
TxConfirmationTimedoutError,
InvalidRPCReplyError
)
import time import time
import random import random
_default_endpoint = 'http://localhost:9500' _default_endpoint = "http://localhost:9500"
_default_timeout = 30 _default_timeout = 30
######################### #########################
# Transaction Pool RPCs # # 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 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 https://api.hmny.io/#de6c4a12-fa42-44e8-972f-801bfde1dd18
""" """
method = 'hmyv2_pendingTransactions' method = "hmyv2_pendingTransactions"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#9aedbc22-6262-44b1-8276-cd8ae19fa600
""" """
method = 'hmyv2_getCurrentTransactionErrorSink' method = "hmyv2_getCurrentTransactionErrorSink"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#de0235e4-f4c9-4a69-b6d2-b77dc1ba7b12
""" """
method = 'hmyv2_pendingStakingTransactions' method = "hmyv2_pendingStakingTransactions"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#bdd00e0f-2ba0-480e-b996-2ef13f10d75a
""" """
method = 'hmyv2_getCurrentStakingErrorSink' method = "hmyv2_getCurrentStakingErrorSink"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: 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 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 https://api.hmny.io/#7c2b9395-8f5e-4eb5-a687-2f1be683d83e
""" """
method = 'hmyv2_getPoolStats' method = "hmyv2_getPoolStats"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
#################### ####################
# Transaction RPCs # # 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 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 https://api.hmny.io/#117e84f6-a0ec-444e-abe0-455701310389
""" """
method = 'hmyv2_getTransactionByHash' method = "hmyv2_getTransactionByHash"
params = [ params = [tx_hash]
tx_hash
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 def get_transaction_by_block_hash_and_index(
) -> dict: 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 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 https://api.hmny.io/#7c7e8d90-4984-4ebe-bb7e-d7adec167503
""" """
method = 'hmyv2_getTransactionByBlockHashAndIndex' method = "hmyv2_getTransactionByBlockHashAndIndex"
params = [ params = [block_hash, tx_index]
block_hash,
tx_index
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 def get_transaction_by_block_number_and_index(
) -> dict: 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 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 https://api.hmny.io/#bcde8b1c-6ab9-4950-9835-3c7564e49c3e
""" """
method = 'hmyv2_getTransactionByBlockNumberAndIndex' method = "hmyv2_getTransactionByBlockNumberAndIndex"
params = [ params = [block_num, tx_index]
block_num,
tx_index
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#0c2799f8-bcdc-41a4-b362-c3a6a763bb5e
""" """
method = 'hmyv2_getTransactionReceipt' method = "hmyv2_getTransactionReceipt"
params = [ params = [tx_hash]
tx_hash
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0
""" """
params = [ params = [signed_tx]
signed_tx method = "hmyv2_sendRawTransaction"
]
method = 'hmyv2_sendRawTransaction'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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) tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint)
start_time = time.time() 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) tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint)
if tx_response is not None: if tx_response is not None:
block_hash = tx_response.get( "blockHash", "0x00" ) block_hash = tx_response.get("blockHash", "0x00")
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) unique_chars = "".join(set(list(block_hash[2:])))
if unique_chars != "0": if unique_chars != "0":
return tx_response return tx_response
time.sleep(random.uniform(0.2, 0.5)) time.sleep(random.uniform(0.2, 0.5))
raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.")
############################### ###############################
# CrossShard Transaction RPCs # # 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 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 https://api.hmny.io/#fe60070d-97b4-458d-9365-490b44c18851
""" """
method = 'hmyv2_getPendingCXReceipts' method = "hmyv2_getPendingCXReceipts"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#3d6ad045-800d-4021-aeb5-30a0fbf724fe
""" """
params = [ params = [cx_hash]
cx_hash method = "hmyv2_getCXReceiptByHash"
]
method = 'hmyv2_getCXReceiptByHash'
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#c658b56b-d20b-480d-b71a-b0bc505d2164
""" """
method = 'hmyv2_resendCx' method = "hmyv2_resendCx"
params = [ params = [cx_hash]
cx_hash
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e raise InvalidRPCReplyError(method, endpoint) from e
############################ ############################
# Staking Transaction RPCs # # 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 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 https://api.hmny.io/#296cb4d0-bce2-48e3-bab9-64c3734edd27
""" """
method = 'hmyv2_getStakingTransactionByHash' method = "hmyv2_getStakingTransactionByHash"
params = [ params = [tx_hash]
tx_hash
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 def get_staking_transaction_by_block_hash_and_index(
) -> dict: block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
""" """
Get staking transaction by block hash and transaction index 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 https://api.hmny.io/#ba96cf61-61fe-464a-aa06-2803bb4b358f
""" """
method = 'hmyv2_getStakingTransactionByBlockHashAndIndex' method = "hmyv2_getStakingTransactionByBlockHashAndIndex"
params = [ params = [block_hash, tx_index]
block_hash,
tx_index
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 def get_staking_transaction_by_block_number_and_index(
) -> dict: block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
""" """
Get staking transaction by block number and transaction index 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 https://api.hmny.io/#fb41d717-1645-4d3e-8071-6ce8e1b65dd3
""" """
method = 'hmyv2_getStakingTransactionByBlockNumberAndIndex' method = "hmyv2_getStakingTransactionByBlockNumberAndIndex"
params = [ params = [block_num, tx_index]
block_num,
tx_index
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 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 https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401
""" """
method = 'hmyv2_sendRawStakingTransaction' method = "hmyv2_sendRawStakingTransaction"
params = [ params = [raw_tx]
raw_tx
]
try: 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: except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from 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 Send signed staking transaction and wait for it to be confirmed
@ -782,11 +818,11 @@ def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoi
""" """
tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint) tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint)
start_time = time.time() 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) tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint)
if tx_response is not None: if tx_response is not None:
block_hash = tx_response.get( "blockHash", "0x00" ) block_hash = tx_response.get("blockHash", "0x00")
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) unique_chars = "".join(set(list(block_hash[2:])))
if unique_chars != "0": if unique_chars != "0":
return tx_response return tx_response
time.sleep(random.uniform(0.2, 0.5)) time.sleep(random.uniform(0.2, 0.5))

@ -6,9 +6,7 @@ import datetime
import requests import requests
from .blockchain import ( from .blockchain import get_latest_header
get_latest_header
)
from .rpc.exceptions import ( from .rpc.exceptions import (
RPCError, RPCError,
@ -16,63 +14,63 @@ from .rpc.exceptions import (
RequestsTimeoutError, RequestsTimeoutError,
) )
from .account import ( from .account import is_valid_address
is_valid_address
)
from .bech32.bech32 import ( from .bech32.bech32 import bech32_decode, bech32_encode, convertbits
bech32_decode,
bech32_encode,
convertbits
)
from eth_utils import to_checksum_address from eth_utils import to_checksum_address
datetime_format = "%Y-%m-%d %H:%M:%S.%f" datetime_format = "%Y-%m-%d %H:%M:%S.%f"
class Typgpy(str): class Typgpy(str):
""" """
Typography constants for pretty printing. Typography constants for pretty printing.
Note that an ENDC is needed to mark the end of a 'highlighted' text segment. Note that an ENDC is needed to mark the end of a 'highlighted' text segment.
""" """
HEADER = '\033[95m'
OKBLUE = '\033[94m' HEADER = "\033[95m"
OKGREEN = '\033[92m' OKBLUE = "\033[94m"
WARNING = '\033[93m' OKGREEN = "\033[92m"
FAIL = '\033[91m' WARNING = "\033[93m"
ENDC = '\033[0m' FAIL = "\033[91m"
BOLD = '\033[1m' ENDC = "\033[0m"
UNDERLINE = '\033[4m' BOLD = "\033[1m"
UNDERLINE = "\033[4m"
def chain_id_to_int(chainId): def chain_id_to_int(chainId):
chainIds = dict( chainIds = dict(
Default = 0, Default=0,
EthMainnet = 1, EthMainnet=1,
Morden = 2, Morden=2,
Ropsten = 3, Ropsten=3,
Rinkeby = 4, Rinkeby=4,
RootstockMainnet = 30, RootstockMainnet=30,
RootstockTestnet = 31, RootstockTestnet=31,
Kovan = 42, Kovan=42,
EtcMainnet = 61, EtcMainnet=61,
EtcTestnet = 62, EtcTestnet=62,
Geth = 1337, Geth=1337,
Ganache = 0, Ganache=0,
HmyMainnet = 1, HmyMainnet=1,
HmyTestnet = 2, HmyTestnet=2,
HmyLocal = 2, HmyLocal=2,
HmyPangaea = 3, HmyPangaea=3,
) )
# do not validate integer chainids, only known strings # do not validate integer chainids, only known strings
if isinstance(chainId, str): 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) return chainIds.get(chainId)
elif isinstance(chainId, int): elif isinstance(chainId, int):
return chainId return chainId
else: else:
raise TypeError( 'chainId must be str or int' ) raise TypeError("chainId must be str or int")
def get_gopath(): def get_gopath():
""" """
@ -87,6 +85,7 @@ def get_goversion():
""" """
return subprocess.check_output(["go", "version"]).decode().strip() return subprocess.check_output(["go", "version"]).decode().strip()
def convert_one_to_hex(addr): def convert_one_to_hex(addr):
""" """
Given a one address, convert it to hex checksum address 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) return to_checksum_address(addr)
hrp, data = bech32_decode(addr) hrp, data = bech32_decode(addr)
buf = convertbits(data, 5, 8, False) 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) return to_checksum_address(address)
def convert_hex_to_one(addr): def convert_hex_to_one(addr):
""" """
Given a hex address, convert it to a one address 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): if is_valid_address(addr):
return addr return addr
checksum_addr = to_checksum_address(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) buf = convertbits(data, 8, 5)
return bech32_encode("one", buf) return bech32_encode("one", buf)
def is_active_shard(endpoint, delay_tolerance=60): def is_active_shard(endpoint, delay_tolerance=60):
""" """
:param endpoint: The endpoint of the SHARD to check :param endpoint: The endpoint of the SHARD to check
@ -118,8 +121,10 @@ def is_active_shard(endpoint, delay_tolerance=60):
try: try:
curr_time = datetime.datetime.utcnow() curr_time = datetime.datetime.utcnow()
latest_header = get_latest_header(endpoint=endpoint) latest_header = get_latest_header(endpoint=endpoint)
time_str = latest_header["timestamp"][:19] + '.0' # Fit time format 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) timestamp = datetime.datetime.strptime(
time_str, "%Y-%m-%d %H:%M:%S.%f"
).replace(tzinfo=None)
time_delta = curr_time - timestamp time_delta = curr_time - timestamp
return abs(time_delta.seconds) < delay_tolerance return abs(time_delta.seconds) < delay_tolerance
except (RPCError, RequestsError, RequestsTimeoutError): except (RPCError, RequestsError, RequestsTimeoutError):
@ -137,7 +142,12 @@ def get_bls_build_variables():
""" """
variables = {} variables = {}
try: 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: except (IndexError, subprocess.CalledProcessError) as e:
raise RuntimeError("`openssl` not found") from e raise RuntimeError("`openssl` not found") from e
hmy_path = f"{get_gopath()}/src/github.com/harmony-one" 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(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}" assert os.path.exists(mcl_dir), f"Harmony MCL repo not found at {mcl_dir}"
if sys.platform.startswith("darwin"): 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["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["LD_LIBRARY_PATH"] = f"{bls_dir}/lib:{mcl_dir}/lib:{openssl_dir}/lib"
variables["DYLD_FALLBACK_LIBRARY_PATH"] = variables["LD_LIBRARY_PATH"] variables["DYLD_FALLBACK_LIBRARY_PATH"] = variables["LD_LIBRARY_PATH"]

@ -1,44 +1,27 @@
import json import json
from eth_account.datastructures import ( from eth_account.datastructures import SignedTransaction
SignedTransaction
)
from decimal import ( from decimal import Decimal, InvalidOperation
Decimal,
InvalidOperation
)
from .account import ( from .account import get_balance, is_valid_address
get_balance,
is_valid_address
)
from .numbers import ( from .numbers import convert_one_to_atto
convert_one_to_atto
)
from .exceptions import ( from .exceptions import (
InvalidValidatorError, InvalidValidatorError,
RPCError, RPCError,
RequestsError, RequestsError,
RequestsTimeoutError RequestsTimeoutError,
) )
from .staking import ( from .staking import get_all_validator_addresses, get_validator_information
get_all_validator_addresses,
get_validator_information
)
from .staking_structures import ( from .staking_structures import Directive
Directive
)
from .staking_signing import ( from .staking_signing import sign_staking_transaction
sign_staking_transaction
)
_default_endpoint = 'http://localhost:9500' _default_endpoint = "http://localhost:9500"
_default_timeout = 30 _default_timeout = 30
# TODO: Add unit testing # TODO: Add unit testing
@ -49,13 +32,13 @@ class Validator:
website_char_limit = 140 website_char_limit = 140
security_contact_char_limit = 140 security_contact_char_limit = 140
details_char_limit = 280 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): def __init__(self, address):
if not isinstance(address, str): 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): 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._address = address
self._bls_keys = [] self._bls_keys = []
self._bls_key_sigs = [] self._bls_key_sigs = []
@ -84,8 +67,11 @@ class Validator:
""" """
if check_str: if check_str:
if not isinstance(data, str): if not isinstance(data, str):
raise InvalidValidatorError(3, f'Expected data to be string to avoid floating point precision issues but got {data}') raise InvalidValidatorError(
return '' if not data else str(data) 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: def __str__(self) -> str:
""" """
@ -98,7 +84,7 @@ class Validator:
return json.dumps(info) return json.dumps(info)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<Validator: {hex(id(self))}>' return f"<Validator: {hex(id(self))}>"
def get_address(self) -> str: def get_address(self) -> str:
""" """
@ -209,7 +195,9 @@ class Validator:
""" """
name = self._sanitize_input(name) name = self._sanitize_input(name)
if len(name) > self.name_char_limit: 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 self._name = name
def get_name(self) -> str: def get_name(self) -> str:
@ -239,7 +227,9 @@ class Validator:
""" """
identity = self._sanitize_input(identity) identity = self._sanitize_input(identity)
if len(identity) > self.identity_char_limit: 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 self._identity = identity
def get_identity(self) -> str: def get_identity(self) -> str:
@ -269,7 +259,9 @@ class Validator:
""" """
website = self._sanitize_input(website) website = self._sanitize_input(website)
if len(website) > self.website_char_limit: 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 self._website = website
def get_website(self) -> str: def get_website(self) -> str:
@ -299,7 +291,10 @@ class Validator:
""" """
contact = self._sanitize_input(contact) contact = self._sanitize_input(contact)
if len(contact) > self.security_contact_char_limit: 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 self._security_contact = contact
def get_security_contact(self) -> str: def get_security_contact(self) -> str:
@ -329,7 +324,9 @@ class Validator:
""" """
details = self._sanitize_input(details) details = self._sanitize_input(details)
if len(details) > self.details_char_limit: 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 self._details = details
def get_details(self) -> str: def get_details(self) -> str:
@ -361,9 +358,14 @@ class Validator:
try: try:
delegation = Decimal(delegation) delegation = Decimal(delegation)
except (TypeError, InvalidOperation) as e: 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: 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 self._min_self_delegation = delegation
def get_min_self_delegation(self) -> Decimal: def get_min_self_delegation(self) -> Decimal:
@ -395,13 +397,20 @@ class Validator:
try: try:
max_delegation = Decimal(max_delegation) max_delegation = Decimal(max_delegation)
except (TypeError, InvalidOperation) as e: 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 self._min_self_delegation:
if max_delegation < 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: ' raise InvalidValidatorError(
'{self._min_self_delegation}') 3,
f"Max total delegation must be greater than min self delegation: "
"{self._min_self_delegation}",
)
else: 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 self._max_total_delegation = max_delegation
def get_max_total_delegation(self) -> Decimal: def get_max_total_delegation(self) -> Decimal:
@ -433,19 +442,29 @@ class Validator:
try: try:
amount = Decimal(amount) amount = Decimal(amount)
except (TypeError, InvalidOperation) as e: 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 self._min_self_delegation:
if amount < self._min_self_delegation: if amount < self._min_self_delegation:
raise InvalidValidatorError(3, 'Amount must be greater than min self delegation: ' raise InvalidValidatorError(
f'{self._min_self_delegation}') 3,
"Amount must be greater than min self delegation: "
f"{self._min_self_delegation}",
)
else: 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 self._max_total_delegation:
if amount > self._max_total_delegation: if amount > self._max_total_delegation:
raise InvalidValidatorError(3, 'Amount must be less than max total delegation: ' raise InvalidValidatorError(
f'{self._max_total_delegation}') 3,
"Amount must be less than max total delegation: "
f"{self._max_total_delegation}",
)
else: 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 self._inital_delegation = amount
def get_amount(self) -> Decimal: def get_amount(self) -> Decimal:
@ -477,9 +496,9 @@ class Validator:
try: try:
rate = Decimal(rate) rate = Decimal(rate)
except (TypeError, InvalidOperation) as e: 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: 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 self._max_rate = rate
def get_max_rate(self) -> Decimal: def get_max_rate(self) -> Decimal:
@ -511,14 +530,21 @@ class Validator:
try: try:
rate = Decimal(rate) rate = Decimal(rate)
except (TypeError, InvalidOperation) as e: 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: 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 self._max_rate:
if rate > 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: 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 self._max_change_rate = rate
def get_max_change_rate(self) -> Decimal: def get_max_change_rate(self) -> Decimal:
@ -550,14 +576,16 @@ class Validator:
try: try:
rate = Decimal(rate) rate = Decimal(rate)
except (TypeError, InvalidOperation) as e: 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: 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 self._max_rate:
if rate > 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: else:
raise InvalidValidatorError(4, 'Max rate must be set before rate') raise InvalidValidatorError(4, "Max rate must be set before rate")
self._rate = rate self._rate = rate
def get_rate(self) -> Decimal: def get_rate(self) -> Decimal:
@ -571,7 +599,9 @@ class Validator:
""" """
return self._rate 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 Check if validator exists on blockchain
@ -629,31 +659,33 @@ class Validator:
If input value is invalid If input value is invalid
""" """
try: try:
self.set_name(info['name']) self.set_name(info["name"])
self.set_identity(info['identity']) self.set_identity(info["identity"])
self.set_website(info['website']) self.set_website(info["website"])
self.set_details(info['details']) self.set_details(info["details"])
self.set_security_contact(info['security-contact']) self.set_security_contact(info["security-contact"])
self.set_min_self_delegation(info['min-self-delegation']) self.set_min_self_delegation(info["min-self-delegation"])
self.set_max_total_delegation(info['max-total-delegation']) self.set_max_total_delegation(info["max-total-delegation"])
self.set_amount(info['amount']) self.set_amount(info["amount"])
self.set_max_rate(info['max-rate']) self.set_max_rate(info["max-rate"])
self.set_max_change_rate(info['max-change-rate']) self.set_max_change_rate(info["max-change-rate"])
self.set_rate(info['rate']) self.set_rate(info["rate"])
self._bls_keys = [] self._bls_keys = []
for key in info['bls-public-keys']: for key in info["bls-public-keys"]:
self.add_bls_key(key) self.add_bls_key(key)
self._bls_key_sigs = [] self._bls_key_sigs = []
for key in info['bls-key-sigs']: for key in info["bls-key-sigs"]:
self.add_bls_key_sig(key) self.add_bls_key_sig(key)
except KeyError as e: 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 Import validator information from blockchain with given address
At the moment, this is unable to fetch the BLS Signature, which is not implemented At the moment, this is unable to fetch the BLS Signature, which is not implemented
@ -673,33 +705,43 @@ class Validator:
""" """
try: try:
if not self.does_validator_exist(endpoint, timeout): 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: 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: try:
validator_info = get_validator_information(self._address, endpoint, timeout) validator_info = get_validator_information(self._address, endpoint, timeout)
except (RPCError, RequestsError, RequestsTimeoutError) as e: 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 # Skip additional sanity checks when importing from chain
try: try:
info = validator_info['validator'] info = validator_info["validator"]
self._name = info['name'] self._name = info["name"]
self._identity = info['identity'] self._identity = info["identity"]
self._website = info['website'] self._website = info["website"]
self._details = info['details'] self._details = info["details"]
self._security_contact = info['security-contact'] self._security_contact = info["security-contact"]
self._min_self_delegation = info['min-self-delegation'] self._min_self_delegation = info["min-self-delegation"]
self._max_total_delegation = info['max-total-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._inital_delegation = (
self._min_self_delegation
self._max_rate = Decimal(info['max-rate']) ) # Since validator exists, set initial delegation to 0
self._max_change_rate = Decimal(info['max-change-rate'])
self._rate = Decimal(info['rate']) self._max_rate = Decimal(info["max-rate"])
self._bls_keys = info[ 'bls-public-keys' ] 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: 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: def export(self) -> dict:
""" """
@ -724,11 +766,13 @@ class Validator:
"max-rate": self._max_rate, "max-rate": self._max_rate,
"max-change-rate": self._max_change_rate, "max-change-rate": self._max_change_rate,
"bls-public-keys": self._bls_keys, "bls-public-keys": self._bls_keys,
"bls-key-sigs": self._bls_key_sigs "bls-key-sigs": self._bls_key_sigs,
} }
return info 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 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 https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L413
""" """
info = self.export().copy() info = self.export().copy()
info['directive'] = Directive.CreateValidator info["directive"] = Directive.CreateValidator
info['validatorAddress'] = info.pop('validator-addr') # change the key info["validatorAddress"] = info.pop("validator-addr") # change the key
info['nonce'] = nonce info["nonce"] = nonce
info['gasPrice'] = gas_price info["gasPrice"] = gas_price
info['gasLimit'] = gas_limit info["gasLimit"] = gas_limit
if chain_id: if chain_id:
info['chainId'] = chain_id info["chainId"] = chain_id
return sign_staking_transaction(info, private_key) 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 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.add_bls_key(bls_key_to_add)
self.remove_bls_key(bls_key_to_remove) self.remove_bls_key(bls_key_to_remove)
info = self.export().copy() info = self.export().copy()
info['directive'] = Directive.EditValidator info["directive"] = Directive.EditValidator
info['validatorAddress'] = info.pop('validator-addr') # change the key info["validatorAddress"] = info.pop("validator-addr") # change the key
info['nonce'] = nonce info["nonce"] = nonce
info['gasPrice'] = gas_price info["gasPrice"] = gas_price
info['gasLimit'] = gas_limit info["gasLimit"] = gas_limit
_ = info.pop('max-rate') # not needed _ = info.pop("max-rate") # not needed
_ = info.pop('max-change-rate') # not needed _ = info.pop("max-change-rate") # not needed
_ = info.pop('bls-public-keys') # remove this list _ = info.pop("bls-public-keys") # remove this list
_ = info.pop('amount') # also unused _ = info.pop("amount") # also unused
info['bls-key-to-remove'] = bls_key_to_remove info["bls-key-to-remove"] = bls_key_to_remove
info['bls-key-to-add'] = bls_key_to_add info["bls-key-to-add"] = bls_key_to_add
info['bls-key-to-add-sig'] = bls_key_to_add_sig info["bls-key-to-add-sig"] = bls_key_to_add_sig
if chain_id: if chain_id:
info['chainId'] = chain_id info["chainId"] = chain_id
return sign_staking_transaction(info, private_key) return sign_staking_transaction(info, private_key)

@ -1,10 +1,9 @@
from pyhmy.bech32 import ( from pyhmy.bech32 import bech32
bech32
)
def test_encode(): 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")

@ -14,11 +14,11 @@ def test_basic_logger():
logger.debug("test debug") logger.debug("test debug")
logger.error("test error") logger.error("test error")
logger.warning("test warning") 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() log_file_contents = f.readlines()
assert not log_file_contents assert not log_file_contents
logger.write() 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() log_file_contents = f.readlines()
for line in log_file_contents: for line in log_file_contents:
if "INFO" in line: if "INFO" in line:

@ -1,8 +1,6 @@
from decimal import Decimal from decimal import Decimal
from pyhmy import ( from pyhmy import numbers
numbers
)
def test_convert_atto_to_one(): 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) b = numbers.convert_atto_to_one(1e18 + 0.6)
assert Decimal(1) == b 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 assert Decimal(1) == c
d = numbers.convert_atto_to_one(Decimal(1e18)) d = numbers.convert_atto_to_one(Decimal(1e18))
assert Decimal(1) == d assert Decimal(1) == d
def test_convert_one_to_atto(): def test_convert_one_to_atto():
a = numbers.convert_one_to_atto(1e-18) a = numbers.convert_one_to_atto(1e-18)
assert Decimal(1) == a assert Decimal(1) == a
@ -25,7 +24,7 @@ def test_convert_one_to_atto():
b = numbers.convert_one_to_atto(1.5) b = numbers.convert_one_to_atto(1.5)
assert Decimal(1.5e18) == b assert Decimal(1.5e18) == b
c = numbers.convert_one_to_atto('1') c = numbers.convert_one_to_atto("1")
assert Decimal(1e18) == c assert Decimal(1e18) == c
d = numbers.convert_one_to_atto(Decimal(1)) d = numbers.convert_one_to_atto(Decimal(1))

@ -4,31 +4,27 @@ import socket
import pytest import pytest
import requests import requests
from pyhmy.rpc import ( from pyhmy.rpc import exceptions, request
exceptions,
request
)
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
def setup(): def setup():
endpoint = 'http://localhost:9500' endpoint = "http://localhost:9500"
timeout = 30 timeout = 30
method = 'hmyv2_getNodeMetadata' method = "hmyv2_getNodeMetadata"
params = [] params = []
payload = { payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params}
"id": "1", headers = {"Content-Type": "application/json"}
"jsonrpc": "2.0",
"method": method,
"params": params
}
headers = {
'Content-Type': 'application/json'
}
try: try:
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
except Exception as e: except Exception as e:
pytest.skip("can not connect to local blockchain", allow_module_level=True) pytest.skip("can not connect to local blockchain", allow_module_level=True)
@ -36,16 +32,18 @@ def setup():
def test_request_connection_error(): def test_request_connection_error():
# Find available port # Find available port
s = socket.socket() s = socket.socket()
s.bind(('localhost', 0)) s.bind(("localhost", 0))
port = s.getsockname()[1] port = s.getsockname()[1]
s.close() s.close()
if port == 0: if port == 0:
pytest.skip("could not find available port") pytest.skip("could not find available port")
bad_endpoint = f'http://localhost:{port}' bad_endpoint = f"http://localhost:{port}"
bad_request = None bad_request = None
try: 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: except Exception as e:
assert isinstance(e, exceptions.RequestsError) assert isinstance(e, exceptions.RequestsError)
assert bad_request is None assert bad_request is None
@ -54,7 +52,7 @@ def test_request_connection_error():
def test_request_rpc_error(): def test_request_rpc_error():
error_request = None error_request = None
try: try:
error_request = request.rpc_request('hmyv2_getBalance') error_request = request.rpc_request("hmyv2_getBalance")
except (exceptions.RequestsTimeoutError, exceptions.RequestsError) as err: except (exceptions.RequestsTimeoutError, exceptions.RequestsError) as err:
pytest.skip("can not connect to local blockchain", allow_module_level=True) pytest.skip("can not connect to local blockchain", allow_module_level=True)
except Exception as e: except Exception as e:
@ -63,24 +61,23 @@ def test_request_rpc_error():
def test_rpc_request(): def test_rpc_request():
endpoint = 'http://localhost:9500' endpoint = "http://localhost:9500"
timeout = 30 timeout = 30
method = 'hmyv2_getNodeMetadata' method = "hmyv2_getNodeMetadata"
params = [] params = []
payload = { payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params}
"id": "1", headers = {"Content-Type": "application/json"}
"jsonrpc": "2.0",
"method": method,
"params": params
}
headers = {
'Content-Type': 'application/json'
}
response = None response = None
try: try:
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
except: except:
pytest.skip("can not connect to local blockchain") pytest.skip("can not connect to local blockchain")
assert response is not None assert response is not None
@ -89,14 +86,14 @@ def test_rpc_request():
try: try:
resp = json.loads(response.content) resp = json.loads(response.content)
except json.decoder.JSONDecodeError as err: except json.decoder.JSONDecodeError as err:
pytest.skip('unable to decode response') pytest.skip("unable to decode response")
assert resp is not None assert resp is not None
rpc_response = None rpc_response = None
try: try:
rpc_response = request.rpc_request(method, params, endpoint, timeout) rpc_response = request.rpc_request(method, params, endpoint, timeout)
except exceptions.RPCError as e: except exceptions.RPCError as e:
assert 'error' in resp assert "error" in resp
if rpc_response is not None: if rpc_response is not None:
assert rpc_response == resp assert rpc_response == resp

@ -9,11 +9,9 @@ import requests
# 1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc / one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 (genesis) # 1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc / one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 (genesis)
# 3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65 / one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (transferred 503) # 3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65 / one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (transferred 503)
endpoint = 'http://localhost:9500' endpoint = "http://localhost:9500"
timeout = 30 timeout = 30
headers = { headers = {"Content-Type": "application/json"}
'Content-Type': 'application/json'
}
txs = [ txs = [
# same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce) # same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce)
"0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3", "0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3",
@ -37,8 +35,9 @@ stx_hashes = [
"0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968", "0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968",
"0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f", "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f",
] ]
assert len( txs ) == len( tx_hashes ), "Mismatch in tx and tx_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" assert len(stxs) == len(stx_hashes), "Mismatch in stx and stx_hash count"
@pytest.fixture(scope="session", autouse=True) @pytest.fixture(scope="session", autouse=True)
def setup_blockchain(): def setup_blockchain():
@ -47,38 +46,61 @@ def setup_blockchain():
metadata = _check_connection() metadata = _check_connection()
_check_staking_epoch(metadata) _check_staking_epoch(metadata)
for i in range( len( txs ) ): for i in range(len(txs)):
tx = txs[ i ] tx = txs[i]
tx_hash = tx_hashes[ i ] tx_hash = tx_hashes[i]
_send_transaction( tx, endpoint ) _send_transaction(tx, endpoint)
if not _wait_for_transaction_confirmed( tx_hash, 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) 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( 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(): def _check_connection():
try: try:
payload = { payload = {
"id": "1", "id": "1",
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": 'hmyv2_getNodeMetadata', "method": "hmyv2_getNodeMetadata",
"params": [] "params": [],
} }
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
metadata = json.loads(response.content) metadata = json.loads(response.content)
if 'error' in metadata: if "error" in metadata:
pytest.skip(f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}", allow_module_level=True) pytest.skip(
if 'chain-config' not in metadata['result']: f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}",
pytest.skip("Chain config not found in hmyv2_getNodeMetadata reply", allow_module_level=True) 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 return metadata
except Exception as e: 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): def _check_staking_epoch(metadata):
latest_header = None latest_header = None
@ -86,105 +108,163 @@ def _check_staking_epoch(metadata):
payload = { payload = {
"id": "1", "id": "1",
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": 'hmyv2_latestHeader', "method": "hmyv2_latestHeader",
"params": [] "params": [],
} }
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
latest_header = json.loads(response.content) latest_header = json.loads(response.content)
if 'error' in latest_header: if "error" in latest_header:
pytest.skip(f"Error in hmyv2_latestHeader reply: {latest_header['error']}", allow_module_level=True) pytest.skip(
f"Error in hmyv2_latestHeader reply: {latest_header['error']}",
allow_module_level=True,
)
except Exception as e: 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: if metadata and latest_header:
staking_epoch = metadata['result']['chain-config']['staking-epoch'] staking_epoch = metadata["result"]["chain-config"]["staking-epoch"]
current_epoch = latest_header['result']['epoch'] current_epoch = latest_header["result"]["epoch"]
if staking_epoch > current_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): def _send_transaction(raw_tx, endpoint):
try: try:
payload = { payload = {
"id": "1", "id": "1",
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": 'hmyv2_sendRawTransaction', "method": "hmyv2_sendRawTransaction",
"params": [raw_tx] "params": [raw_tx],
} }
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
tx = json.loads(response.content) tx = json.loads(response.content)
if 'error' in tx: if "error" in tx:
pytest.skip(f"Error in hmyv2_sendRawTransaction reply: {tx['error']}", allow_module_level=True) pytest.skip(
f"Error in hmyv2_sendRawTransaction reply: {tx['error']}",
allow_module_level=True,
)
except Exception as e: 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): def _check_transaction(tx_hash, endpoint):
try: try:
payload = { payload = {
"id": "1", "id": "1",
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": 'hmyv2_getTransactionByHash', "method": "hmyv2_getTransactionByHash",
"params": [tx_hash] "params": [tx_hash],
} }
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
tx_data = json.loads(response.content) tx_data = json.loads(response.content)
return tx_data return tx_data
except Exception as e: 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() start_time = time.time()
while((time.time() - start_time) <= timeout): while (time.time() - start_time) <= timeout:
tx_data = _check_transaction(tx_hash, endpoint) tx_data = _check_transaction(tx_hash, endpoint)
if tx_data is not None: if tx_data is not None:
block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) block_hash = tx_data["result"].get("blockHash", "0x00")
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) unique_chars = "".join(set(list(block_hash[2:])))
if unique_chars != "0": if unique_chars != "0":
return True return True
time.sleep(random.uniform(0.2, 0.5)) time.sleep(random.uniform(0.2, 0.5))
return False return False
def _send_staking_transaction(raw_tx, endpoint = endpoint):
def _send_staking_transaction(raw_tx, endpoint=endpoint):
try: try:
payload = { payload = {
"id": "1", "id": "1",
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": 'hmyv2_sendRawStakingTransaction', "method": "hmyv2_sendRawStakingTransaction",
"params": [raw_tx] "params": [raw_tx],
} }
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
staking_tx = json.loads(response.content) staking_tx = json.loads(response.content)
if 'error' in staking_tx: if "error" in staking_tx:
pytest.skip(f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}", allow_module_level=True) pytest.skip(
f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}",
allow_module_level=True,
)
except Exception as e: 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: try:
payload = { payload = {
"id": "1", "id": "1",
"jsonrpc": "2.0", "jsonrpc": "2.0",
"method": 'hmyv2_getStakingTransactionByHash', "method": "hmyv2_getStakingTransactionByHash",
"params": [stx_hash] "params": [stx_hash],
} }
response = requests.request('POST', endpoint, headers=headers, response = requests.request(
data=json.dumps(payload), timeout=timeout, allow_redirects=True) "POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
stx_data = json.loads(response.content) stx_data = json.loads(response.content)
return stx_data return stx_data
except Exception as e: 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 answer = False
start_time = time.time() start_time = time.time()
while((time.time() - start_time) <= timeout): while (time.time() - start_time) <= timeout:
tx_data = _check_staking_transaction(tx_hash, endpoint) tx_data = _check_staking_transaction(tx_hash, endpoint)
if tx_data is not None: if tx_data is not None:
block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) block_hash = tx_data["result"].get("blockHash", "0x00")
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) unique_chars = "".join(set(list(block_hash[2:])))
if unique_chars != "0": if unique_chars != "0":
answer = True answer = True
time.sleep(random.uniform(0.2, 0.5)) time.sleep(random.uniform(0.2, 0.5))

@ -1,33 +1,32 @@
import pytest import pytest
import requests import requests
from pyhmy import ( from pyhmy import account
account
)
from pyhmy.rpc import ( from pyhmy.rpc import exceptions
exceptions
)
explorer_endpoint = 'http://localhost:9700' explorer_endpoint = "http://localhost:9700"
endpoint_shard_one = 'http://localhost:9502' endpoint_shard_one = "http://localhost:9502"
local_test_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' local_test_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
test_validator_address = local_test_address test_validator_address = local_test_address
genesis_block_number = 0 genesis_block_number = 0
test_block_number = 1 test_block_number = 1
fake_shard = 'http://example.com' fake_shard = "http://example.com"
def _test_account_rpc(fn, *args, **kwargs): def _test_account_rpc(fn, *args, **kwargs):
if not callable(fn): if not callable(fn):
pytest.fail(f'Invalid function: {fn}') pytest.fail(f"Invalid function: {fn}")
try: try:
response = fn(*args, **kwargs) response = fn(*args, **kwargs)
except Exception as e: except Exception as e:
if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): if isinstance(
pytest.skip(f'{str(e)}') e, exceptions.RPCError
pytest.fail(f'Unexpected error: {e.__class__} {e}') ) 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 return response
@ -36,69 +35,102 @@ def test_get_balance(setup_blockchain):
assert isinstance(balance, int) assert isinstance(balance, int)
assert balance > 0 assert balance > 0
def test_get_balance_by_block(setup_blockchain): 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 isinstance(balance, int)
assert balance > 0 assert balance > 0
def test_get_account_nonce(setup_blockchain): 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) assert isinstance(true_nonce, int)
def test_get_transaction_history(setup_blockchain): 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 isinstance(tx_history, list)
assert len(tx_history) >= 0 assert len(tx_history) >= 0
def test_get_staking_transaction_history(setup_blockchain): 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 isinstance(staking_tx_history, list)
assert len(staking_tx_history) > 0 assert len(staking_tx_history) > 0
def test_get_balance_on_all_shards(setup_blockchain): def test_get_balance_on_all_shards(setup_blockchain):
balances = _test_account_rpc(account.get_balance_on_all_shards, local_test_address) balances = _test_account_rpc(account.get_balance_on_all_shards, local_test_address)
assert isinstance(balances, list) assert isinstance(balances, list)
assert len(balances) == 2 assert len(balances) == 2
def test_get_total_balance(setup_blockchain): def test_get_total_balance(setup_blockchain):
total_balance = _test_account_rpc(account.get_total_balance, local_test_address) total_balance = _test_account_rpc(account.get_total_balance, local_test_address)
assert isinstance(total_balance, int) assert isinstance(total_balance, int)
assert total_balance > 0 assert total_balance > 0
def test_is_valid_address(): def test_is_valid_address():
assert account.is_valid_address('one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur') assert account.is_valid_address("one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur")
assert not account.is_valid_address('one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0') assert not account.is_valid_address("one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0")
def test_get_transaction_count(setup_blockchain): 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 isinstance(tx_count, int)
assert tx_count > 0 assert tx_count > 0
def test_get_transactions_count(setup_blockchain): 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): 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) assert isinstance(tx_count, int)
def test_errors(): def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
account.get_balance('', fake_shard) account.get_balance("", fake_shard)
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
account.get_account_nonce('', 1, fake_shard) account.get_account_nonce("", 1, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
account.get_transaction_count('', 1, fake_shard) account.get_transaction_count("", 1, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
account.get_transactions_count('', 1, fake_shard) account.get_transactions_count("", 1, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
account.get_transactions_count('', 'ALL', fake_shard) account.get_transactions_count("", "ALL", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
account.get_transaction_history('', endpoint=fake_shard) account.get_transaction_history("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): 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): 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): with pytest.raises(exceptions.RPCError):
account.get_total_balance('', endpoint=fake_shard) account.get_total_balance("", endpoint=fake_shard)

@ -1,211 +1,264 @@
import pytest import pytest
import requests import requests
from pyhmy import ( from pyhmy import blockchain
blockchain
)
from pyhmy.rpc import ( from pyhmy.rpc import exceptions
exceptions
)
test_epoch_number = 0 test_epoch_number = 0
genesis_block_number = 0 genesis_block_number = 0
test_block_number = 1 test_block_number = 1
test_block_hash = None test_block_hash = None
fake_shard = 'http://example.com' fake_shard = "http://example.com"
address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
def _test_blockchain_rpc(fn, *args, **kwargs): def _test_blockchain_rpc(fn, *args, **kwargs):
if not callable(fn): if not callable(fn):
pytest.fail(f'Invalid function: {fn}') pytest.fail(f"Invalid function: {fn}")
try: try:
response = fn(*args, **kwargs) response = fn(*args, **kwargs)
except Exception as e: except Exception as e:
if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): if isinstance(
pytest.skip(f'{str(e)}') e, exceptions.RPCError
pytest.fail(f'Unexpected error: {e.__class__} {e}') ) 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 return response
def test_get_node_metadata(setup_blockchain): def test_get_node_metadata(setup_blockchain):
metadata = _test_blockchain_rpc(blockchain.get_node_metadata) metadata = _test_blockchain_rpc(blockchain.get_node_metadata)
assert isinstance(metadata, dict) assert isinstance(metadata, dict)
def test_get_sharding_structure(setup_blockchain): def test_get_sharding_structure(setup_blockchain):
sharding_structure = _test_blockchain_rpc(blockchain.get_sharding_structure) sharding_structure = _test_blockchain_rpc(blockchain.get_sharding_structure)
assert isinstance(sharding_structure, list) assert isinstance(sharding_structure, list)
assert len(sharding_structure) > 0 assert len(sharding_structure) > 0
def test_get_leader_address(setup_blockchain): def test_get_leader_address(setup_blockchain):
leader = _test_blockchain_rpc(blockchain.get_leader_address) leader = _test_blockchain_rpc(blockchain.get_leader_address)
assert isinstance(leader, str) assert isinstance(leader, str)
assert 'one1' in leader assert "one1" in leader
def test_get_block_number(setup_blockchain): def test_get_block_number(setup_blockchain):
current_block_number = _test_blockchain_rpc(blockchain.get_block_number) current_block_number = _test_blockchain_rpc(blockchain.get_block_number)
assert isinstance(current_block_number, int) assert isinstance(current_block_number, int)
def test_get_current_epoch(setup_blockchain): def test_get_current_epoch(setup_blockchain):
current_epoch = _test_blockchain_rpc(blockchain.get_current_epoch) current_epoch = _test_blockchain_rpc(blockchain.get_current_epoch)
assert isinstance(current_epoch, int) assert isinstance(current_epoch, int)
def tset_get_gas_price(setup_blockchain): def tset_get_gas_price(setup_blockchain):
gas = _test_blockchain_rpc(blockchain.get_gas_price) gas = _test_blockchain_rpc(blockchain.get_gas_price)
assert isinstance(gas, int) assert isinstance(gas, int)
def test_get_num_peers(setup_blockchain): def test_get_num_peers(setup_blockchain):
peers = _test_blockchain_rpc(blockchain.get_num_peers) peers = _test_blockchain_rpc(blockchain.get_num_peers)
assert isinstance(peers, int) assert isinstance(peers, int)
def test_get_latest_header(setup_blockchain): def test_get_latest_header(setup_blockchain):
header = _test_blockchain_rpc(blockchain.get_latest_header) header = _test_blockchain_rpc(blockchain.get_latest_header)
assert isinstance(header, dict) assert isinstance(header, dict)
def test_get_latest_chain_headers(setup_blockchain): def test_get_latest_chain_headers(setup_blockchain):
header_pair = _test_blockchain_rpc(blockchain.get_latest_chain_headers) header_pair = _test_blockchain_rpc(blockchain.get_latest_chain_headers)
assert isinstance(header_pair, dict) assert isinstance(header_pair, dict)
def test_get_block_by_number(setup_blockchain): def test_get_block_by_number(setup_blockchain):
global test_block_hash global test_block_hash
block = _test_blockchain_rpc(blockchain.get_block_by_number, test_block_number) block = _test_blockchain_rpc(blockchain.get_block_by_number, test_block_number)
assert isinstance(block, dict) assert isinstance(block, dict)
assert 'hash' in block.keys() assert "hash" in block.keys()
test_block_hash = block['hash'] test_block_hash = block["hash"]
def test_get_block_by_hash(setup_blockchain): def test_get_block_by_hash(setup_blockchain):
if not test_block_hash: 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) block = _test_blockchain_rpc(blockchain.get_block_by_hash, test_block_hash)
assert isinstance(block, dict) assert isinstance(block, dict)
def test_get_block_transaction_count_by_number(setup_blockchain): 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) assert isinstance(tx_count, int)
def test_get_block_transaction_count_by_hash(setup_blockchain): def test_get_block_transaction_count_by_hash(setup_blockchain):
if not test_block_hash: if not test_block_hash:
pytest.skip('Failed to get reference 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) tx_count = _test_blockchain_rpc(
blockchain.get_block_transaction_count_by_hash, test_block_hash
)
assert isinstance(tx_count, int) assert isinstance(tx_count, int)
def test_get_blocks(setup_blockchain): 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 isinstance(blocks, list)
assert len(blocks) == (test_block_number - genesis_block_number + 1) assert len(blocks) == (test_block_number - genesis_block_number + 1)
def test_get_block_signers(setup_blockchain): 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 isinstance(block_signers, list)
assert len(block_signers) > 0 assert len(block_signers) > 0
def test_get_validators(setup_blockchain): def test_get_validators(setup_blockchain):
validators = _test_blockchain_rpc(blockchain.get_validators, test_epoch_number) validators = _test_blockchain_rpc(blockchain.get_validators, test_epoch_number)
assert isinstance(validators, dict) assert isinstance(validators, dict)
assert 'validators' in validators.keys() assert "validators" in validators.keys()
assert len(validators['validators']) > 0 assert len(validators["validators"]) > 0
def test_get_shard(setup_blockchain): def test_get_shard(setup_blockchain):
shard = _test_blockchain_rpc(blockchain.get_shard) shard = _test_blockchain_rpc(blockchain.get_shard)
assert isinstance(shard, int) assert isinstance(shard, int)
assert shard == 0 assert shard == 0
def test_get_staking_epoch(setup_blockchain): def test_get_staking_epoch(setup_blockchain):
staking_epoch = _test_blockchain_rpc(blockchain.get_staking_epoch) staking_epoch = _test_blockchain_rpc(blockchain.get_staking_epoch)
assert isinstance(staking_epoch, int) assert isinstance(staking_epoch, int)
def test_get_prestaking_epoch(setup_blockchain): def test_get_prestaking_epoch(setup_blockchain):
prestaking_epoch = _test_blockchain_rpc(blockchain.get_prestaking_epoch) prestaking_epoch = _test_blockchain_rpc(blockchain.get_prestaking_epoch)
assert isinstance(prestaking_epoch, int) assert isinstance(prestaking_epoch, int)
def test_get_bad_blocks(setup_blockchain): def test_get_bad_blocks(setup_blockchain):
# TODO: Remove skip when RPC is fixed # TODO: Remove skip when RPC is fixed
pytest.skip("Known error with hmyv2_getCurrentBadBlocks") pytest.skip("Known error with hmyv2_getCurrentBadBlocks")
bad_blocks = _test_blockchain_rpc(blockchain.get_bad_blocks) bad_blocks = _test_blockchain_rpc(blockchain.get_bad_blocks)
assert isinstance(bad_blocks, list) assert isinstance(bad_blocks, list)
def test_get_validator_keys(setup_blockchain): def test_get_validator_keys(setup_blockchain):
keys = _test_blockchain_rpc(blockchain.get_validator_keys, test_epoch_number) keys = _test_blockchain_rpc(blockchain.get_validator_keys, test_epoch_number)
assert isinstance(keys, list) assert isinstance(keys, list)
assert len(keys) > 0 assert len(keys) > 0
def test_get_block_signers_keys(setup_blockchain): def test_get_block_signers_keys(setup_blockchain):
keys = _test_blockchain_rpc(blockchain.get_block_signers_keys, test_block_number) keys = _test_blockchain_rpc(blockchain.get_block_signers_keys, test_block_number)
assert isinstance(keys, list) assert isinstance(keys, list)
assert len(keys) > 0 assert len(keys) > 0
def test_chain_id(setup_blockchain): def test_chain_id(setup_blockchain):
chain_id = _test_blockchain_rpc(blockchain.chain_id) chain_id = _test_blockchain_rpc(blockchain.chain_id)
assert isinstance(chain_id, int) assert isinstance(chain_id, int)
def test_get_peer_info(setup_blockchain): def test_get_peer_info(setup_blockchain):
peer_info = _test_blockchain_rpc(blockchain.get_peer_info) peer_info = _test_blockchain_rpc(blockchain.get_peer_info)
assert isinstance(peer_info, dict) assert isinstance(peer_info, dict)
def test_protocol_version(setup_blockchain): def test_protocol_version(setup_blockchain):
protocol_version = _test_blockchain_rpc(blockchain.protocol_version) protocol_version = _test_blockchain_rpc(blockchain.protocol_version)
assert isinstance(protocol_version, int) assert isinstance(protocol_version, int)
def test_is_last_block(setup_blockchain): def test_is_last_block(setup_blockchain):
is_last_block = _test_blockchain_rpc(blockchain.is_last_block, 0) is_last_block = _test_blockchain_rpc(blockchain.is_last_block, 0)
assert isinstance(is_last_block, bool) assert isinstance(is_last_block, bool)
assert not is_last_block assert not is_last_block
def test_epoch_last_block(setup_blockchain): def test_epoch_last_block(setup_blockchain):
epoch_last_block = _test_blockchain_rpc(blockchain.epoch_last_block, 0) epoch_last_block = _test_blockchain_rpc(blockchain.epoch_last_block, 0)
assert isinstance(epoch_last_block, int) assert isinstance(epoch_last_block, int)
def test_get_circulating_supply(setup_blockchain): def test_get_circulating_supply(setup_blockchain):
circulating_supply = _test_blockchain_rpc(blockchain.get_circulating_supply) circulating_supply = _test_blockchain_rpc(blockchain.get_circulating_supply)
assert isinstance(circulating_supply, str) assert isinstance(circulating_supply, str)
def test_get_total_supply(setup_blockchain): def test_get_total_supply(setup_blockchain):
total_supply = _test_blockchain_rpc(blockchain.get_total_supply) total_supply = _test_blockchain_rpc(blockchain.get_total_supply)
assert isinstance(total_supply, str) or total_supply == None assert isinstance(total_supply, str) or total_supply == None
def test_get_last_cross_links(setup_blockchain): def test_get_last_cross_links(setup_blockchain):
last_cross_links = _test_blockchain_rpc(blockchain.get_last_cross_links) last_cross_links = _test_blockchain_rpc(blockchain.get_last_cross_links)
assert isinstance(last_cross_links, list) assert isinstance(last_cross_links, list)
def test_get_gas_price(setup_blockchain): def test_get_gas_price(setup_blockchain):
gas_price = _test_blockchain_rpc(blockchain.get_gas_price) gas_price = _test_blockchain_rpc(blockchain.get_gas_price)
assert isinstance(gas_price, int) assert isinstance(gas_price, int)
def test_get_version(setup_blockchain): def test_get_version(setup_blockchain):
version = _test_blockchain_rpc(blockchain.get_version) version = _test_blockchain_rpc(blockchain.get_version)
assert isinstance(version, int) assert isinstance(version, int)
def test_get_header_by_number(setup_blockchain): def test_get_header_by_number(setup_blockchain):
header_pair = _test_blockchain_rpc(blockchain.get_header_by_number, 0) header_pair = _test_blockchain_rpc(blockchain.get_header_by_number, 0)
assert isinstance(header_pair, dict) assert isinstance(header_pair, dict)
def test_get_block_staking_transaction_count_by_number(setup_blockchain): 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) assert isinstance(tx_count, int)
def test_get_block_staking_transaction_count_by_hash(setup_blockchain): def test_get_block_staking_transaction_count_by_hash(setup_blockchain):
if not test_block_hash: if not test_block_hash:
pytest.skip('Failed to get reference 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) tx_count = _test_blockchain_rpc(
blockchain.get_block_staking_transaction_count_by_hash, test_block_hash
)
assert isinstance(tx_count, int) assert isinstance(tx_count, int)
def test_is_block_signer(setup_blockchain): 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) assert isinstance(is_signer, bool)
def test_get_signed_blocks(setup_blockchain): def test_get_signed_blocks(setup_blockchain):
signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address) signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address)
assert isinstance(signed_blocks, int) assert isinstance(signed_blocks, int)
def test_in_sync(setup_blockchain): def test_in_sync(setup_blockchain):
in_sync = _test_blockchain_rpc(blockchain.in_sync) in_sync = _test_blockchain_rpc(blockchain.in_sync)
assert isinstance(in_sync, bool) assert isinstance(in_sync, bool)
def test_beacon_in_sync(setup_blockchain): def test_beacon_in_sync(setup_blockchain):
beacon_in_sync = _test_blockchain_rpc(blockchain.beacon_in_sync) beacon_in_sync = _test_blockchain_rpc(blockchain.beacon_in_sync)
assert isinstance(beacon_in_sync, bool) assert isinstance(beacon_in_sync, bool)
def test_errors(): def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
blockchain.chain_id(fake_shard) blockchain.chain_id(fake_shard)
@ -254,15 +307,15 @@ def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
blockchain.get_block_by_number(0, endpoint=fake_shard) blockchain.get_block_by_number(0, endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
blockchain.get_block_transaction_count_by_number(0, fake_shard) blockchain.get_block_transaction_count_by_number(0, fake_shard)
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
blockchain.get_block_staking_transaction_count_by_number(0, fake_shard) blockchain.get_block_staking_transaction_count_by_number(0, fake_shard)
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
blockchain.get_blocks(0, 1, endpoint=fake_shard) blockchain.get_blocks(0, 1, endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
@ -270,9 +323,9 @@ def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
blockchain.get_block_signers_keys(0, fake_shard) blockchain.get_block_signers_keys(0, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
blockchain.is_block_signer(0, '', fake_shard) blockchain.is_block_signer(0, "", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
blockchain.get_signed_blocks('', fake_shard) blockchain.get_signed_blocks("", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
blockchain.get_validators(1, fake_shard) blockchain.get_validators(1, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):

@ -1,71 +1,80 @@
import pytest import pytest
from pyhmy import ( from pyhmy import contract
contract
)
from pyhmy.rpc import ( from pyhmy.rpc import exceptions
exceptions
)
explorer_endpoint = 'http://localhost:9599' explorer_endpoint = "http://localhost:9599"
contract_tx_hash = '0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f' contract_tx_hash = "0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f"
# deployedBytecode from json file # deployedBytecode from json file
contract_code = '0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033' contract_code = "0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033"
contract_address = None contract_address = None
fake_shard = 'http://example.com' fake_shard = "http://example.com"
def _test_contract_rpc(fn, *args, **kwargs): def _test_contract_rpc(fn, *args, **kwargs):
if not callable(fn): if not callable(fn):
pytest.fail(f'Invalid function: {fn}') pytest.fail(f"Invalid function: {fn}")
try: try:
response = fn(*args, **kwargs) response = fn(*args, **kwargs)
except Exception as e: except Exception as e:
if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): if isinstance(
pytest.skip(f'{str(e)}') e, exceptions.RPCError
elif isinstance(e, exceptions.RPCError) and 'estimateGas returned' in str(e): ) and "does not exist/is not available" in str(e):
pytest.skip(f'{str(e)}') pytest.skip(f"{str(e)}")
pytest.fail(f'Unexpected error: {e.__class__} {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 return response
def test_get_contract_address_from_hash(setup_blockchain): def test_get_contract_address_from_hash(setup_blockchain):
global contract_address 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) assert isinstance(contract_address, str)
def test_call(setup_blockchain): def test_call(setup_blockchain):
if not contract_address: if not contract_address:
pytest.skip('Contract address not loaded yet') pytest.skip("Contract address not loaded yet")
called = _test_contract_rpc(contract.call, contract_address, 'latest') called = _test_contract_rpc(contract.call, contract_address, "latest")
assert isinstance(called, str) and called.startswith('0x') assert isinstance(called, str) and called.startswith("0x")
def test_estimate_gas(setup_blockchain): def test_estimate_gas(setup_blockchain):
if not contract_address: 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) gas = _test_contract_rpc(contract.estimate_gas, contract_address)
assert isinstance(gas, int) assert isinstance(gas, int)
def test_get_code(setup_blockchain): def test_get_code(setup_blockchain):
if not contract_address: if not contract_address:
pytest.skip('Contract address not loaded yet') pytest.skip("Contract address not loaded yet")
code = _test_contract_rpc(contract.get_code, contract_address, 'latest') code = _test_contract_rpc(contract.get_code, contract_address, "latest")
assert code == contract_code assert code == contract_code
def test_get_storage_at(setup_blockchain): def test_get_storage_at(setup_blockchain):
if not contract_address: if not contract_address:
pytest.skip('Contract address not loaded yet') pytest.skip("Contract address not loaded yet")
storage = _test_contract_rpc(contract.get_storage_at, contract_address, '0x0', 'latest') storage = _test_contract_rpc(
assert isinstance(storage, str) and storage.startswith('0x') contract.get_storage_at, contract_address, "0x0", "latest"
)
assert isinstance(storage, str) and storage.startswith("0x")
def test_errors(): def test_errors():
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
contract.call('', '', endpoint=fake_shard) contract.call("", "", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
contract.estimate_gas('', endpoint=fake_shard) contract.estimate_gas("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
contract.get_code('', 'latest', endpoint=fake_shard) contract.get_code("", "latest", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
contract.get_storage_at('', 1, 'latest', endpoint=fake_shard) contract.get_storage_at("", 1, "latest", endpoint=fake_shard)

@ -1,6 +1,4 @@
from pyhmy import ( from pyhmy import signing
signing
)
""" """
Test signature source (node.js) Test signature source (node.js)
@ -30,16 +28,25 @@ let signed = RLPSign(transaction, privateKey);
console.log( 'Signed transaction' ) console.log( 'Signed transaction' )
console.log(signed) console.log(signed)
""" """
def test_eth_transaction(): def test_eth_transaction():
transaction_dict = { transaction_dict = {
'nonce': 2, "nonce": 2,
'gasPrice': 1, "gasPrice": 1,
'gas': 100, # signing.py uses Ether, which by default calls it gas "gas": 100, # signing.py uses Ether, which by default calls it gas
'to': '0x14791697260e4c9a71f18484c9f997b308e59325', "to": "0x14791697260e4c9a71f18484c9f997b308e59325",
'value': 5, "value": 5,
} }
signed_tx = signing.sign_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') signed_tx = signing.sign_transaction(
assert signed_tx.rawTransaction.hex() == '0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1' transaction_dict,
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1"
)
""" """
Test signature source (node.js) Test signature source (node.js)
@ -71,16 +78,24 @@ let signed = RLPSign(transaction, privateKey);
console.log( 'Signed transaction' ) console.log( 'Signed transaction' )
console.log(signed) console.log(signed)
""" """
def test_hmy_transaction(): def test_hmy_transaction():
transaction_dict = { transaction_dict = {
'nonce': 2, "nonce": 2,
'gasPrice': 1, "gasPrice": 1,
'gas': 100, # signing.py uses Ether, which by default calls it gas "gas": 100, # signing.py uses Ether, which by default calls it gas
'to': '0x14791697260e4c9a71f18484c9f997b308e59325', "to": "0x14791697260e4c9a71f18484c9f997b308e59325",
'value': 5, "value": 5,
'shardID': 0, "shardID": 0,
'toShardID': 1, "toShardID": 1,
'chainId': 'HmyMainnet' "chainId": "HmyMainnet",
} }
signed_tx = signing.sign_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') signed_tx = signing.sign_transaction(
assert signed_tx.rawTransaction.hex() == '0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d' transaction_dict,
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d"
)

@ -1,135 +1,191 @@
import pytest import pytest
import requests import requests
from pyhmy import ( from pyhmy import staking
staking
)
from pyhmy.rpc import ( from pyhmy.rpc import exceptions
exceptions
)
explorer_endpoint = 'http://localhost:9700' explorer_endpoint = "http://localhost:9700"
test_validator_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' test_validator_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
fake_shard = 'http://example.com' fake_shard = "http://example.com"
def _test_staking_rpc(fn, *args, **kwargs): def _test_staking_rpc(fn, *args, **kwargs):
if not callable(fn): if not callable(fn):
pytest.fail(f'Invalid function: {fn}') pytest.fail(f"Invalid function: {fn}")
try: try:
response = fn(*args, **kwargs) response = fn(*args, **kwargs)
except Exception as e: except Exception as e:
if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): if isinstance(
pytest.skip(f'{str(e)}') e, exceptions.RPCError
pytest.fail(f'Unexpected error: {e.__class__} {e}') ) 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 return response
def test_get_all_validator_addresses(setup_blockchain): def test_get_all_validator_addresses(setup_blockchain):
validator_addresses = _test_staking_rpc(staking.get_all_validator_addresses) validator_addresses = _test_staking_rpc(staking.get_all_validator_addresses)
assert isinstance(validator_addresses, list) assert isinstance(validator_addresses, list)
assert len(validator_addresses) > 0 assert len(validator_addresses) > 0
assert test_validator_address in validator_addresses assert test_validator_address in validator_addresses
def test_get_validator_information(setup_blockchain): def test_get_validator_information(setup_blockchain):
info = _test_staking_rpc(staking.get_validator_information, test_validator_address) info = _test_staking_rpc(staking.get_validator_information, test_validator_address)
assert isinstance(info, dict) assert isinstance(info, dict)
def test_get_all_validator_information(setup_blockchain): def test_get_all_validator_information(setup_blockchain):
all_validator_information = _test_staking_rpc(staking.get_all_validator_information) all_validator_information = _test_staking_rpc(staking.get_all_validator_information)
assert isinstance(all_validator_information, list) assert isinstance(all_validator_information, list)
assert len(all_validator_information) > 0 assert len(all_validator_information) > 0
def test_get_delegations_by_delegator(setup_blockchain): 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 isinstance(delegations, list)
assert len(delegations) > 0 assert len(delegations) > 0
def test_get_delegations_by_validator(setup_blockchain): 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 isinstance(delegations, list)
assert len(delegations) > 0 assert len(delegations) > 0
def test_get_current_utility_metrics(setup_blockchain): def test_get_current_utility_metrics(setup_blockchain):
metrics = _test_staking_rpc(staking.get_current_utility_metrics) metrics = _test_staking_rpc(staking.get_current_utility_metrics)
assert isinstance(metrics, dict) assert isinstance(metrics, dict)
def test_get_staking_network_info(setup_blockchain): def test_get_staking_network_info(setup_blockchain):
info = _test_staking_rpc(staking.get_staking_network_info) info = _test_staking_rpc(staking.get_staking_network_info)
assert isinstance(info, dict) assert isinstance(info, dict)
def test_get_super_committees(setup_blockchain): def test_get_super_committees(setup_blockchain):
committee = _test_staking_rpc(staking.get_super_committees) committee = _test_staking_rpc(staking.get_super_committees)
assert isinstance(committee, dict) assert isinstance(committee, dict)
def test_get_raw_median_stake_snapshot(setup_blockchain): def test_get_raw_median_stake_snapshot(setup_blockchain):
median_stake = _test_staking_rpc(staking.get_raw_median_stake_snapshot) median_stake = _test_staking_rpc(staking.get_raw_median_stake_snapshot)
assert isinstance(median_stake, dict) assert isinstance(median_stake, dict)
def test_get_validator_information_by_block(setup_blockchain): 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 # 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) assert isinstance(info, dict)
def test_get_validator_information_by_block(setup_blockchain): 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 # 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) assert isinstance(info, list)
def test_get_delegations_by_delegator_by_block(setup_blockchain): 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) assert isinstance(delegations, list)
def test_get_elected_validator_addresses(setup_blockchain): def test_get_elected_validator_addresses(setup_blockchain):
validator_addresses = _test_staking_rpc(staking.get_elected_validator_addresses) validator_addresses = _test_staking_rpc(staking.get_elected_validator_addresses)
assert isinstance(validator_addresses, list) assert isinstance(validator_addresses, list)
assert len(validator_addresses) > 0 assert len(validator_addresses) > 0
def test_get_validators(setup_blockchain): def test_get_validators(setup_blockchain):
validators = _test_staking_rpc(staking.get_validators, 2) validators = _test_staking_rpc(staking.get_validators, 2)
assert isinstance(validators, dict) assert isinstance(validators, dict)
assert len(validators['validators']) > 0 assert len(validators["validators"]) > 0
def test_get_validator_keys(setup_blockchain): def test_get_validator_keys(setup_blockchain):
validators = _test_staking_rpc(staking.get_validator_keys, 2) validators = _test_staking_rpc(staking.get_validator_keys, 2)
assert isinstance(validators, list) assert isinstance(validators, list)
def test_get_validator_self_delegation(setup_blockchain): 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 isinstance(self_delegation, int)
assert self_delegation > 0 assert self_delegation > 0
def test_get_validator_total_delegation(setup_blockchain): 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 isinstance(total_delegation, int)
assert total_delegation > 0 assert total_delegation > 0
def test_get_all_delegation_information(setup_blockchain): 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 isinstance(delegation_information, list)
assert len(delegation_information) > 0 assert len(delegation_information) > 0
def test_get_delegation_by_delegator_and_validator(setup_blockchain): 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) assert isinstance(delegation_information, dict)
def test_get_available_redelegation_balance(setup_blockchain): 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 isinstance(redelgation_balance, int)
assert redelgation_balance == 0 assert redelgation_balance == 0
def test_get_total_staking(setup_blockchain): def test_get_total_staking(setup_blockchain):
total_staking = _test_staking_rpc(staking.get_total_staking) total_staking = _test_staking_rpc(staking.get_total_staking)
assert isinstance(total_staking, int) 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 assert total_staking > 0
def test_errors(): def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_all_validator_addresses(fake_shard) staking.get_all_validator_addresses(fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_validator_information('', fake_shard) staking.get_validator_information("", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_elected_validator_addresses(fake_shard) staking.get_elected_validator_addresses(fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
@ -137,27 +193,27 @@ def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_validator_keys(1, fake_shard) staking.get_validator_keys(1, fake_shard)
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
staking.get_all_validator_information(-1, fake_shard) staking.get_all_validator_information(-1, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_validator_self_delegation('', fake_shard) staking.get_validator_self_delegation("", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_validator_total_delegation('', fake_shard) staking.get_validator_total_delegation("", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_all_validator_information_by_block_number(1, 1, fake_shard) staking.get_all_validator_information_by_block_number(1, 1, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_all_delegation_information(1, fake_shard) staking.get_all_delegation_information(1, fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_delegations_by_delegator('', fake_shard) staking.get_delegations_by_delegator("", fake_shard)
with pytest.raises(exceptions.RPCError): 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): 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): with pytest.raises(exceptions.RPCError):
staking.get_available_redelegation_balance('', fake_shard) staking.get_available_redelegation_balance("", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_delegations_by_validator('', fake_shard) staking.get_delegations_by_validator("", fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
staking.get_current_utility_metrics(fake_shard) staking.get_current_utility_metrics(fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):

@ -1,11 +1,6 @@
from pyhmy import ( from pyhmy import staking_signing, staking_structures
staking_signing,
staking_structures
)
from pyhmy.numbers import ( from pyhmy.numbers import convert_one_to_atto
convert_one_to_atto
)
# other transactions (create/edit validator) are in test_validator.py # 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 # 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 transaction' )
console.log(signed) console.log(signed)
""" """
def test_collect_rewards_chain_id(): def test_collect_rewards_chain_id():
transaction_dict = { transaction_dict = {
'directive': staking_structures.Directive.CollectRewards, "directive": staking_structures.Directive.CollectRewards,
'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', "delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9",
'nonce': 2, "nonce": 2,
'gasPrice': int(convert_one_to_atto(1)), "gasPrice": int(convert_one_to_atto(1)),
'gasLimit': 100, "gasLimit": 100,
'chainId': 1, # with chainId for coverage "chainId": 1, # with chainId for coverage
} }
signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') signed_tx = staking_signing.sign_staking_transaction(
assert signed_tx.rawTransaction.hex() == '0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099' transaction_dict,
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099"
)
""" """
let stakingTx let stakingTx
@ -87,16 +91,24 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd
console.log( 'Signed transaction' ) console.log( 'Signed transaction' )
console.log(signed) console.log(signed)
""" """
def test_delegate(): def test_delegate():
transaction_dict = { transaction_dict = {
'directive': staking_structures.Directive.Delegate, "directive": staking_structures.Directive.Delegate,
'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', "delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9",
'validatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', "validatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9",
'amount': 5, "amount": 5,
'nonce': 2, "nonce": 2,
'gasPrice': int(convert_one_to_atto(1)), "gasPrice": int(convert_one_to_atto(1)),
'gasLimit': 100, "gasLimit": 100,
'chainId': 2, "chainId": 2,
} }
signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') signed_tx = staking_signing.sign_staking_transaction(
assert signed_tx.rawTransaction.hex() == '0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6' transaction_dict,
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6"
)

@ -1,157 +1,205 @@
import pytest import pytest
from pyhmy import ( from pyhmy import transaction
transaction
)
from pyhmy.rpc import ( from pyhmy.rpc import exceptions
exceptions
)
endpoint = 'http://localhost:9500' endpoint = "http://localhost:9500"
endpoint_shard_one = 'http://localhost:9502' endpoint_shard_one = "http://localhost:9502"
fake_shard = 'http://example.com' fake_shard = "http://example.com"
# previously sent txs to get and check # previously sent txs to get and check
tx_hash = '0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43' tx_hash = "0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43"
tx_block_num = None tx_block_num = None
tx_block_hash = None tx_block_hash = None
tx_index = None tx_index = None
cx_hash = '0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf' cx_hash = "0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf"
stx_hash = '0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f' stx_hash = "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f"
stx_block_num = None stx_block_num = None
stx_block_hash = None stx_block_hash = None
stx_index = None stx_index = None
# new txs to send and check # new txs to send and check
raw_tx = '0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0' raw_tx = "0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0"
raw_tx_hash = '0x7ccd80f8513f76ec58b357c7a82a12a95e025d88f1444e953f90e3d86e222571' 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): def _test_transaction_rpc(fn, *args, **kwargs):
if not callable(fn): if not callable(fn):
pytest.fail(f'Invalid function: {fn}') pytest.fail(f"Invalid function: {fn}")
try: try:
response = fn(*args, **kwargs) response = fn(*args, **kwargs)
except Exception as e: except Exception as e:
if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): if isinstance(
pytest.skip(f'{str(e)}') e, exceptions.RPCError
pytest.fail(f'Unexpected error: {e.__class__} {e}') ) 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 return response
def test_get_pending_transactions(setup_blockchain): def test_get_pending_transactions(setup_blockchain):
pool = _test_transaction_rpc(transaction.get_pending_transactions) pool = _test_transaction_rpc(transaction.get_pending_transactions)
assert isinstance(pool, list) assert isinstance(pool, list)
def test_get_transaction_by_hash(setup_blockchain): 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 tx
assert isinstance(tx, dict) assert isinstance(tx, dict)
assert 'blockNumber' in tx.keys() assert "blockNumber" in tx.keys()
assert 'blockHash' in tx.keys() assert "blockHash" in tx.keys()
global tx_block_num global tx_block_num
tx_block_num = int(tx['blockNumber']) tx_block_num = int(tx["blockNumber"])
global tx_block_hash global tx_block_hash
tx_block_hash = tx['blockHash'] tx_block_hash = tx["blockHash"]
global tx_index global tx_index
tx_index = int(tx[ 'transactionIndex' ]) tx_index = int(tx["transactionIndex"])
def test_get_transaction_by_block_hash_and_index(setup_blockchain): def test_get_transaction_by_block_hash_and_index(setup_blockchain):
if not tx_block_hash: if not tx_block_hash:
pytest.skip('Failed to get reference block hash') pytest.skip("Failed to get reference block hash")
tx = _test_transaction_rpc(transaction.get_transaction_by_block_hash_and_index, tx = _test_transaction_rpc(
tx_block_hash, tx_index, endpoint=endpoint) transaction.get_transaction_by_block_hash_and_index,
tx_block_hash,
tx_index,
endpoint=endpoint,
)
assert tx assert tx
assert isinstance(tx, dict) assert isinstance(tx, dict)
def test_get_transaction_by_block_number_and_index(setup_blockchain): def test_get_transaction_by_block_number_and_index(setup_blockchain):
if not tx_block_num: if not tx_block_num:
pytest.skip('Failed to get reference 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, tx = _test_transaction_rpc(
endpoint=endpoint) transaction.get_transaction_by_block_number_and_index,
tx_block_num,
tx_index,
endpoint=endpoint,
)
assert tx assert tx
assert isinstance(tx, dict) assert isinstance(tx, dict)
def test_get_transaction_receipt(setup_blockchain): 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 tx_receipt
assert isinstance(tx_receipt, dict) assert isinstance(tx_receipt, dict)
def test_get_transaction_error_sink(setup_blockchain): def test_get_transaction_error_sink(setup_blockchain):
errors = _test_transaction_rpc(transaction.get_transaction_error_sink) errors = _test_transaction_rpc(transaction.get_transaction_error_sink)
assert isinstance(errors, list) assert isinstance(errors, list)
def test_send_and_confirm_raw_transaction(setup_blockchain): 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. # 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, test_tx = _test_transaction_rpc(
raw_tx) transaction.send_and_confirm_raw_transaction, raw_tx
)
assert isinstance(test_tx, dict) 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): def test_get_pending_cx_receipts(setup_blockchain):
pending = _test_transaction_rpc(transaction.get_pending_cx_receipts) pending = _test_transaction_rpc(transaction.get_pending_cx_receipts)
assert isinstance(pending, list) assert isinstance(pending, list)
def test_get_cx_receipt_by_hash(setup_blockchain): 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 cx
assert isinstance(cx, dict) assert isinstance(cx, dict)
def test_resend_cx_receipt(setup_blockchain): def test_resend_cx_receipt(setup_blockchain):
sent = _test_transaction_rpc(transaction.resend_cx_receipt, cx_hash) sent = _test_transaction_rpc(transaction.resend_cx_receipt, cx_hash)
assert isinstance(sent, bool) assert isinstance(sent, bool)
assert sent assert sent
def test_get_staking_transaction_by_hash(setup_blockchain): 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 staking_tx
assert isinstance(staking_tx, dict) assert isinstance(staking_tx, dict)
assert 'blockNumber' in staking_tx.keys() assert "blockNumber" in staking_tx.keys()
assert 'blockHash' in staking_tx.keys() assert "blockHash" in staking_tx.keys()
global stx_block_num global stx_block_num
stx_block_num = int(staking_tx['blockNumber']) stx_block_num = int(staking_tx["blockNumber"])
global stx_block_hash global stx_block_hash
stx_block_hash = staking_tx['blockHash'] stx_block_hash = staking_tx["blockHash"]
global stx_index 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): def test_get_transaction_by_block_hash_and_index(setup_blockchain):
if not stx_block_hash: if not stx_block_hash:
pytest.skip('Failed to get reference 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) stx = _test_transaction_rpc(
transaction.get_staking_transaction_by_block_hash_and_index,
stx_block_hash,
stx_index,
)
assert stx assert stx
assert isinstance(stx, dict) assert isinstance(stx, dict)
def test_get_transaction_by_block_number_and_index(setup_blockchain): def test_get_transaction_by_block_number_and_index(setup_blockchain):
if not stx_block_num: if not stx_block_num:
pytest.skip('Failed to get reference 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) stx = _test_transaction_rpc(
transaction.get_staking_transaction_by_block_number_and_index,
stx_block_num,
stx_index,
)
assert stx assert stx
assert isinstance(stx, dict) assert isinstance(stx, dict)
def test_get_staking_transaction_error_sink(setup_blockchain): def test_get_staking_transaction_error_sink(setup_blockchain):
errors = _test_transaction_rpc(transaction.get_staking_transaction_error_sink) errors = _test_transaction_rpc(transaction.get_staking_transaction_error_sink)
assert isinstance(errors, list) assert isinstance(errors, list)
def test_send_raw_staking_transaction(setup_blockchain): 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 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): 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) assert isinstance(test_pool_stats, dict)
def test_get_pending_staking_transactions(setup_blockchain): 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) assert isinstance(pending_staking_transactions, list)
def test_errors(): def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
transaction.get_pending_transactions(fake_shard) transaction.get_pending_transactions(fake_shard)
@ -160,30 +208,34 @@ def test_errors():
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
transaction.get_pool_stats(fake_shard) transaction.get_pool_stats(fake_shard)
with pytest.raises(exceptions.RPCError): 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): 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): with pytest.raises(exceptions.RPCError):
transaction.get_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard) transaction.get_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
transaction.get_transaction_receipt('', endpoint=fake_shard) transaction.get_transaction_receipt("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
transaction.send_raw_transaction('', endpoint=fake_shard) transaction.send_raw_transaction("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): with pytest.raises(exceptions.RPCError):
transaction.get_pending_cx_receipts(fake_shard) transaction.get_pending_cx_receipts(fake_shard)
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
transaction.resend_cx_receipt('', endpoint=fake_shard) transaction.resend_cx_receipt("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): 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): 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): 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): with pytest.raises(exceptions.RPCError):
transaction.get_staking_transaction_error_sink(endpoint=fake_shard) transaction.get_staking_transaction_error_sink(endpoint=fake_shard)
with pytest.raises(exceptions.RPCError): 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): with pytest.raises(exceptions.RPCError):
transaction.get_pending_staking_transactions(endpoint=fake_shard) transaction.get_pending_staking_transactions(endpoint=fake_shard)

@ -1,19 +1,11 @@
import pytest import pytest
from decimal import ( from decimal import Decimal
Decimal
)
from pyhmy import ( from pyhmy import validator
validator
)
from pyhmy.numbers import ( from pyhmy.numbers import convert_one_to_atto
convert_one_to_atto
)
from pyhmy.exceptions import ( from pyhmy.exceptions import InvalidValidatorError
InvalidValidatorError
)
test_epoch_number = 0 test_epoch_number = 0
genesis_block_number = 0 genesis_block_number = 0
@ -21,33 +13,42 @@ test_block_number = 1
test_validator_object = None test_validator_object = None
test_validator_loaded = False test_validator_loaded = False
def test_instantiate_validator(setup_blockchain): def test_instantiate_validator(setup_blockchain):
global test_validator_object global test_validator_object
test_validator_object = validator.Validator('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') test_validator_object = validator.Validator(
"one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
)
assert isinstance(test_validator_object, validator.Validator) assert isinstance(test_validator_object, validator.Validator)
def test_load_validator(setup_blockchain): def test_load_validator(setup_blockchain):
if not test_validator_object: if not test_validator_object:
pytest.skip('Validator not instantiated yet') pytest.skip("Validator not instantiated yet")
info = { info = {
'name': 'Alice', "name": "Alice",
'identity': 'alice', "identity": "alice",
'website': 'alice.harmony.one', "website": "alice.harmony.one",
'details': "Don't mess with me!!!", "details": "Don't mess with me!!!",
'security-contact': 'Bob', "security-contact": "Bob",
'min-self-delegation': convert_one_to_atto(10000), "min-self-delegation": convert_one_to_atto(10000),
'amount': convert_one_to_atto(10001), "amount": convert_one_to_atto(10001),
'max-rate': '0.9', "max-rate": "0.9",
'max-change-rate': '0.05', "max-change-rate": "0.05",
'rate': '0.01', "rate": "0.01",
'bls-public-keys': ['0x30b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202'], "bls-public-keys": [
'bls-key-sigs': ['0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414'], "0x30b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202"
'max-total-delegation': convert_one_to_atto(40000) ],
"bls-key-sigs": [
"0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414"
],
"max-total-delegation": convert_one_to_atto(40000),
} }
test_validator_object.load(info) test_validator_object.load(info)
global test_validator_loaded global test_validator_loaded
test_validator_loaded = True test_validator_loaded = True
""" """
TypeScript signature source (is outdated because the JS SDK has not been updated for SlotKeySigs) 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 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 transaction' )
console.log(signed) console.log(signed)
""" """
def test_create_validator_sign(setup_blockchain): def test_create_validator_sign(setup_blockchain):
if not (test_validator_object or test_validator_loaded): 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( signed_hash = test_validator_object.sign_create_validator_transaction(
2, 2,
int(convert_one_to_atto(1)), int(convert_one_to_atto(1)),
100, 100,
'4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
2).rawTransaction.hex() 2,
assert signed_hash == '0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd' ).rawTransaction.hex()
assert (
signed_hash
== "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd"
)
""" """
Signature matched from TypeScript (is outdated because the JS SDK has not been updated for SlotKeyToAddSig) 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 transaction' )
console.log(signed) console.log(signed)
""" """
def test_edit_validator_sign(setup_blockchain): def test_edit_validator_sign(setup_blockchain):
if not (test_validator_object or test_validator_loaded): 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( signed_hash = test_validator_object.sign_edit_validator_transaction(
2, 2,
int(convert_one_to_atto(1)), int(convert_one_to_atto(1)),
100, 100,
'0.06', "0.06",
'0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612', # remove key "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612", # remove key
"0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # add key "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # add key
"0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414", # add key sig "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414", # add key sig
'4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
2).rawTransaction.hex() 2,
assert signed_hash == '0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995' ).rawTransaction.hex()
assert (
signed_hash
== "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995"
)
def test_invalid_validator(setup_blockchain): def test_invalid_validator(setup_blockchain):
if not (test_validator_object or test_validator_loaded): 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): with pytest.raises(InvalidValidatorError):
info = { info = {
'name': 'Alice', "name": "Alice",
} }
test_validator_object.load(info) test_validator_object.load(info)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_name('a'*141) test_validator_object.set_name("a" * 141)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_identity('a'*141) test_validator_object.set_identity("a" * 141)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_website('a'*141) test_validator_object.set_website("a" * 141)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_security_contact('a'*141) test_validator_object.set_security_contact("a" * 141)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_details('a'*281) test_validator_object.set_details("a" * 281)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_min_self_delegation(1) test_validator_object.set_min_self_delegation(1)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
@ -167,22 +182,26 @@ def test_invalid_validator(setup_blockchain):
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_amount(1) test_validator_object.set_amount(1)
with pytest.raises(InvalidValidatorError): with pytest.raises(InvalidValidatorError):
test_validator_object.set_max_rate('2.0') test_validator_object.set_max_rate("2.0")
with pytest.raises(InvalidValidatorError): 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): with pytest.raises(InvalidValidatorError):
test_validator_object.set_rate('-2.0') test_validator_object.set_rate("-2.0")
def test_validator_getters(setup_blockchain): def test_validator_getters(setup_blockchain):
if not (test_validator_object or test_validator_loaded): if not (test_validator_object or test_validator_loaded):
pytest.skip('Validator not instantiated yet') pytest.skip("Validator not instantiated yet")
assert test_validator_object.get_address() == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' assert (
assert test_validator_object.add_bls_key('5') test_validator_object.get_address()
assert test_validator_object.remove_bls_key('5') == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
assert test_validator_object.get_name() == 'Alice' )
assert test_validator_object.get_identity() == 'alice' assert test_validator_object.add_bls_key("5")
assert test_validator_object.get_website() == 'alice.harmony.one' assert test_validator_object.remove_bls_key("5")
assert test_validator_object.get_security_contact() == 'Bob' 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 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_min_self_delegation(), Decimal)
assert isinstance(test_validator_object.get_max_total_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 isinstance(test_validator_object.get_rate(), Decimal)
assert len(test_validator_object.get_bls_keys()) > 0 assert len(test_validator_object.get_bls_keys()) > 0
def test_validator_load_from_blockchain(setup_blockchain): 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() test_validator_object2.load_from_blockchain()

@ -19,41 +19,52 @@ def setup():
def test_json_load(): 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 isinstance(dec, decimal.Decimal)
assert float(dec) == 1.1 assert float(dec) == 1.1
ref_dict = { ref_dict = {"test": "val", "arr": [1, 2, 3]}
'test': 'val',
'arr': [
1,
2,
3
]
}
loaded_dict = util.json_load(json.dumps(ref_dict)) loaded_dict = util.json_load(json.dumps(ref_dict))
assert str(ref_dict) == str(loaded_dict) assert str(ref_dict) == str(loaded_dict)
def test_chain_id_to_int(): def test_chain_id_to_int():
assert util.chain_id_to_int(2) == 2 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(): def test_get_gopath():
assert isinstance(util.get_gopath(), str) assert isinstance(util.get_gopath(), str)
def test_get_goversion(): def test_get_goversion():
assert isinstance(util.get_goversion(), str) assert isinstance(util.get_goversion(), str)
def test_convert_one_to_hex(): def test_convert_one_to_hex():
assert util.convert_one_to_hex('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' assert (
assert util.convert_one_to_hex('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' util.convert_one_to_hex("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58")
== "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58"
)
assert (
util.convert_one_to_hex("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9")
== "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58"
)
def test_convert_hex_to_one(): def test_convert_hex_to_one():
assert util.convert_hex_to_one('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' assert (
assert util.convert_hex_to_one('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' util.convert_hex_to_one("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58")
== "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
)
assert (
util.convert_hex_to_one("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9")
== "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
)
def test_get_bls_build_variables(): def test_get_bls_build_variables():
assert isinstance(util.get_bls_build_variables(), dict) assert isinstance(util.get_bls_build_variables(), dict)
def test_is_active_shard(): def test_is_active_shard():
assert isinstance(util.is_active_shard(''), bool) assert isinstance(util.is_active_shard(""), bool)

Loading…
Cancel
Save