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. 219
      pyhmy/account.py
  4. 13
      pyhmy/bech32/bech32.py
  5. 413
      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. 137
      pyhmy/signing.py
  13. 280
      pyhmy/staking.py
  14. 325
      pyhmy/staking_signing.py
  15. 203
      pyhmy/staking_structures.py
  16. 242
      pyhmy/transaction.py
  17. 66
      pyhmy/util.py
  18. 287
      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. 182
      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. 134
      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 .util import (
Typgpy,
get_gopath,
get_goversion,
get_bls_build_variables,
json_load
)
from .util import Typgpy, get_gopath, get_goversion, get_bls_build_variables, json_load
if sys.version_info.major < 3:
warnings.simplefilter("always", DeprecationWarning)
warnings.warn(
DeprecationWarning(
"`pyhmy` does not support Python 2. Please use Python 3."
)
DeprecationWarning("`pyhmy` does not support Python 2. Please use Python 3.")
)
warnings.resetwarnings()
if sys.platform.startswith('win32') or sys.platform.startswith('cygwin'):
if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"):
warnings.simplefilter("always", ImportWarning)
warnings.warn(
ImportWarning(
"`pyhmy` does not work on Windows or Cygwin."
)
)
warnings.warn(ImportWarning("`pyhmy` does not work on Windows or Cygwin."))
warnings.resetwarnings()

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

@ -1,26 +1,14 @@
from .rpc.request import (
rpc_request
)
from .rpc.request import rpc_request
from .rpc.exceptions import (
RPCError,
RequestsError,
RequestsTimeoutError
)
from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError
from .exceptions import (
InvalidRPCReplyError
)
from .exceptions import InvalidRPCReplyError
from .blockchain import (
get_sharding_structure
)
from .blockchain import get_sharding_structure
from .bech32.bech32 import (
bech32_decode
)
from .bech32.bech32 import bech32_decode
_default_endpoint = 'http://localhost:9500'
_default_endpoint = "http://localhost:9500"
_default_timeout = 30
_address_length = 42
@ -40,13 +28,14 @@ def is_valid_address(address) -> bool:
bool
Is valid address
"""
if not address.startswith('one1'):
if not address.startswith("one1"):
return False
hrp, _ = bech32_decode(address)
if not hrp:
return False
return True
def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get current account balance
@ -74,17 +63,20 @@ def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -
-------------
https://api.hmny.io/#da8901d2-d237-4c3b-9d7d-10af9def05c4
"""
method = 'hmyv2_getBalance'
params = [
address
]
method = "hmyv2_getBalance"
params = [address]
try:
balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
balance = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(balance) # v2 returns the result as it is
except TypeError as e: # check will work if rpc returns None
raise InvalidRPCReplyError(method, endpoint) from e
def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_balance_by_block(
address, block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get account balance for address at a given block number
@ -114,18 +106,20 @@ def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout
https://api.hmny.io/#9aeae4b8-1a09-4ed2-956b-d7c96266dd33
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92
"""
method = 'hmyv2_getBalanceByBlockNumber'
params = [
address,
block_num
]
method = "hmyv2_getBalanceByBlockNumber"
params = [address, block_num]
try:
balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
balance = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(balance)
except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_account_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_account_nonce(
address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get the account nonce
@ -154,24 +148,29 @@ def get_account_nonce(address, block_num='latest', endpoint=_default_endpoint, t
-------------
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L51
"""
method = 'hmyv2_getAccountNonce'
params = [
address,
block_num
]
method = "hmyv2_getAccountNonce"
params = [address, block_num]
try:
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
return int(nonce)
except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_nonce(
address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
See get_account_nonce
"""
return get_account_nonce(address, block_num, endpoint, timeout)
def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_transaction_count(
address, block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get the number of transactions the given address has sent for the given block number
Legacy for apiv1. For apiv2, please use get_account_nonce/get_transactions_count/get_staking_transactions_count apis for
@ -202,18 +201,20 @@ def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeou
-------------
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L69
"""
method = 'hmyv2_getTransactionCount'
params = [
address,
block_num
]
method = "hmyv2_getTransactionCount"
params = [address, block_num]
try:
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
return int(nonce)
except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_transactions_count(
address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get the number of regular transactions from genesis of input type
@ -244,18 +245,20 @@ def get_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout
https://api.hmny.io/#fc97aed2-e65e-4cf4-bc01-8dadb76732c0
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114
"""
method = 'hmyv2_getTransactionsCount'
params = [
address,
tx_type
]
method = "hmyv2_getTransactionsCount"
params = [address, tx_type]
try:
tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
tx_count = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(tx_count)
except TypeError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_staking_transactions_count(
address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL")
@ -286,19 +289,26 @@ def get_staking_transactions_count(address, tx_type, endpoint=_default_endpoint,
https://api.hmny.io/#ddc1b029-f341-4c4d-ba19-74b528d6e5e5
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134
"""
method = 'hmyv2_getStakingTransactionsCount'
params = [
address,
tx_type
]
method = "hmyv2_getStakingTransactionsCount"
params = [address, tx_type]
try:
tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
tx_count = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(tx_count)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL',
order='ASC', endpoint=_default_endpoint, timeout=_default_timeout
def get_transaction_history(
address,
page=0,
page_size=1000,
include_full_tx=False,
tx_type="ALL",
order="ASC",
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> list:
"""
Get list of transactions sent and/or received by the account
@ -345,23 +355,33 @@ def get_transaction_history(address, page=0, page_size=1000, include_full_tx=Fal
"""
params = [
{
'address': address,
'pageIndex': page,
'pageSize': page_size,
'fullTx': include_full_tx,
'txType': tx_type,
'order': order
"address": address,
"pageIndex": page,
"pageSize": page_size,
"fullTx": include_full_tx,
"txType": tx_type,
"order": order,
}
]
method = 'hmyv2_getTransactionsHistory'
method = "hmyv2_getTransactionsHistory"
try:
tx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)
return tx_history['result']['transactions']
tx_history = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)
return tx_history["result"]["transactions"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL',
order='ASC', endpoint=_default_endpoint, timeout=_default_timeout
def get_staking_transaction_history(
address,
page=0,
page_size=1000,
include_full_tx=False,
tx_type="ALL",
order="ASC",
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> list:
"""
Get list of staking transactions sent by the account
@ -419,23 +439,28 @@ def get_staking_transaction_history(address, page=0, page_size=1000, include_ful
"""
params = [
{
'address': address,
'pageIndex': page,
'pageSize': page_size,
'fullTx': include_full_tx,
'txType': tx_type,
'order': order
"address": address,
"pageIndex": page,
"pageSize": page_size,
"fullTx": include_full_tx,
"txType": tx_type,
"order": order,
}
]
# Using v2 API, because getStakingTransactionHistory not implemented in v1
method = 'hmyv2_getStakingTransactionsHistory'
method = "hmyv2_getStakingTransactionsHistory"
try:
stx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return stx_history['staking_transactions']
stx_history = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return stx_history["staking_transactions"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_balance_on_all_shards(address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_balance_on_all_shards(
address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get current account balance in all shards & optionally report errors getting account balance for a shard
@ -467,19 +492,23 @@ def get_balance_on_all_shards(address, skip_error=True, endpoint=_default_endpoi
sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout)
for shard in sharding_structure:
try:
balances.append({
'shard': shard['shardID'],
'balance': get_balance(address, endpoint=shard['http'], timeout=timeout)
})
balances.append(
{
"shard": shard["shardID"],
"balance": get_balance(
address, endpoint=shard["http"], timeout=timeout
),
}
)
except (KeyError, RPCError, RequestsError, RequestsTimeoutError):
if not skip_error:
balances.append({
'shard': shard['shardID'],
'balance': None
})
balances.append({"shard": shard["shardID"], "balance": None})
return balances
def get_total_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_total_balance(
address, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get total account balance on all shards
@ -507,7 +536,9 @@ def get_total_balance(address, endpoint=_default_endpoint, timeout=_default_time
get_balance_on_all_shards
"""
try:
balances = get_balance_on_all_shards(address, skip_error=False, endpoint=endpoint, timeout=timeout)
return sum(b['balance'] for b in balances)
balances = get_balance_on_all_shards(
address, skip_error=False, endpoint=endpoint, timeout=timeout
)
return sum(b["balance"] for b in balances)
except TypeError as e:
raise RuntimeError from e

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

@ -1,12 +1,8 @@
from .rpc.request import (
rpc_request
)
from .rpc.request import rpc_request
from .exceptions import (
InvalidRPCReplyError
)
from .exceptions import InvalidRPCReplyError
_default_endpoint = 'http://localhost:9500'
_default_endpoint = "http://localhost:9500"
_default_timeout = 30
#############################
@ -37,12 +33,13 @@ def get_bad_blocks(endpoint=_default_endpoint, timeout=_default_timeout) -> list
-------------
https://api.hmny.io/#0ba3c7b6-6aa9-46b8-9c84-f8782e935951
"""
method = 'hmyv2_getCurrentBadBlocks'
method = "hmyv2_getCurrentBadBlocks"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
"""
Chain id of the chain
@ -67,13 +64,14 @@ def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
-------------
https://github.com/harmony-one/harmony/blob/343dbe89b3c105f8104ab877769070ba6fdd0133/rpc/blockchain.go#L44
"""
method = 'hmyv2_chainId'
method = "hmyv2_chainId"
try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)
return data['result']
return data["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
"""
Get config for the node
@ -143,13 +141,14 @@ def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> d
https://github.com/harmony-one/harmony/blob/v1.10.2/internal/params/config.go#L233 for chain-config dict
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/node/api.go#L110 for consensus dict
"""
method = 'hmyv2_getNodeMetadata'
method = "hmyv2_getNodeMetadata"
try:
metadata = rpc_request(method, endpoint=endpoint, timeout=timeout)
return metadata['result']
return metadata["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
"""
Get peer info for the node
@ -181,12 +180,13 @@ def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
--------
get_node_metadata
"""
method = 'hmyv2_getPeerInfo'
method = "hmyv2_getPeerInfo"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get the current Harmony protocol version this node supports
@ -212,13 +212,14 @@ def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> in
-------------
https://api.hmny.io/#cab9fcc2-e3cd-4bc9-b62a-13e4e046e2fd
"""
method = 'hmyv2_protocolVersion'
method = "hmyv2_protocolVersion"
try:
value = rpc_request(method, endpoint=endpoint, timeout=timeout)
return value['result']
return value["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get number of peers connected to the node
@ -244,12 +245,15 @@ def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
-------------
https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1
"""
method = 'net_peerCount'
method = "net_peerCount"
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:
raise InvalidRPCReplyError(method, endpoint) from e
def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get version of the EVM network (https://chainid.network/)
@ -275,12 +279,15 @@ def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
-------------
https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1
"""
method = 'net_version'
method = "net_version"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'], 16) # this is hexadecimal
return int(
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16
) # this is hexadecimal
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
"""
Whether the shard chain is in sync or syncing (not out of sync)
@ -305,12 +312,13 @@ def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L690
"""
method = 'hmyv2_inSync'
method = "hmyv2_inSync"
try:
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'])
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
"""
Whether the beacon chain is in sync or syncing (not out of sync)
@ -335,12 +343,13 @@ def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L695
"""
method = 'hmyv2_beaconInSync'
method = "hmyv2_beaconInSync"
try:
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'])
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get epoch number when blockchain switches to EPoS election
@ -370,13 +379,14 @@ def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i
------
get_node_metadata
"""
method = 'hmyv2_getNodeMetadata'
method = "hmyv2_getNodeMetadata"
try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return int(data['chain-config']['staking-epoch'])
data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
return int(data["chain-config"]["staking-epoch"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get epoch number when blockchain switches to allow staking features without election
@ -406,13 +416,14 @@ def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -
------
get_node_metadata
"""
method = 'hmyv2_getNodeMetadata'
method = "hmyv2_getNodeMetadata"
try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return int(data['chain-config']['prestaking-epoch'])
data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
return int(data["chain-config"]["prestaking-epoch"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
########################
# Sharding information #
########################
@ -441,13 +452,18 @@ def get_shard(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
--------
get_node_metadata
"""
method = 'hmyv2_getNodeMetadata'
method = "hmyv2_getNodeMetadata"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']['shard-id']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"][
"shard-id"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_sharding_structure(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_sharding_structure(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get network sharding structure
@ -475,12 +491,13 @@ def get_sharding_structure(endpoint=_default_endpoint, timeout=_default_timeout)
-------------
https://api.hmny.io/#9669d49e-43c1-47d9-a3fd-e7786e5879df
"""
method = 'hmyv2_getShardingStructure'
method = "hmyv2_getShardingStructure"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
#############################
# Current status of network #
#############################
@ -509,13 +526,16 @@ def get_leader_address(endpoint=_default_endpoint, timeout=_default_timeout) ->
-------------
https://api.hmny.io/#8b08d18c-017b-4b44-a3c3-356f9c12dacd
"""
method = 'hmyv2_getLeader'
method = "hmyv2_getLeader"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def is_last_block(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
def is_last_block(
block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> bool:
"""
If the block at block_num is the last block
@ -544,13 +564,20 @@ def is_last_block(block_num, endpoint=_default_endpoint, timeout=_default_timeou
params = [
block_num,
]
method = 'hmyv2_isLastBlock'
method = "hmyv2_isLastBlock"
try:
return bool(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return bool(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def epoch_last_block(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def epoch_last_block(
epoch, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Returns the number of the last block in the epoch
@ -579,12 +606,17 @@ def epoch_last_block(epoch, endpoint=_default_endpoint, timeout=_default_timeout
params = [
epoch,
]
method = 'hmyv2_epochLastBlock'
method = "hmyv2_epochLastBlock"
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get current circulation supply of tokens in ONE
@ -610,12 +642,13 @@ def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout)
-------------
https://api.hmny.io/#8398e818-ac2d-4ad8-a3b4-a00927395044
"""
method = 'hmyv2_getCirculatingSupply'
method = "hmyv2_getCirculatingSupply"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get total number of pre-mined tokens
@ -641,12 +674,13 @@ def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> in
-------------
https://api.hmny.io/#3dcea518-9e9a-4a20-84f4-c7a0817b2196
"""
method = 'hmyv2_getTotalSupply'
method = "hmyv2_getTotalSupply"
try:
rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get current block number
@ -672,12 +706,13 @@ def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> in
-------------
https://api.hmny.io/#2602b6c4-a579-4b7c-bce8-85331e0db1a7
"""
method = 'hmyv2_blockNumber'
method = "hmyv2_blockNumber"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'])
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get current epoch number
@ -703,12 +738,13 @@ def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i
-------------
https://api.hmny.io/#9b8e98b0-46d1-4fa0-aaa6-317ff1ddba59
"""
method = 'hmyv2_getEpoch'
method = "hmyv2_getEpoch"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'])
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
"""
Get last cross shard links
@ -741,12 +777,13 @@ def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) -
-------------
https://api.hmny.io/#4994cdf9-38c4-4b1d-90a8-290ddaa3040e
"""
method = 'hmyv2_getLastCrossLinks'
method = "hmyv2_getLastCrossLinks"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get network gas price
@ -772,12 +809,13 @@ def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
-------------
https://api.hmny.io/#1d53fd59-a89f-436c-a171-aec9d9623f48
"""
method = 'hmyv2_gasPrice'
method = "hmyv2_gasPrice"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'])
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
##############
# Block RPCs #
##############
@ -822,13 +860,16 @@ def get_latest_header(endpoint=_default_endpoint, timeout=_default_timeout) -> d
-------------
https://api.hmny.io/#73fc9b97-b048-4b85-8a93-4d2bf1da54a6
"""
method = 'hmyv2_latestHeader'
method = "hmyv2_latestHeader"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_header_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_header_by_number(
block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get block header of block at block_num
@ -854,16 +895,19 @@ def get_header_by_number(block_num, endpoint=_default_endpoint, timeout=_default
-------------
https://api.hmny.io/#01148e4f-72bb-426d-a123-718a161eaec0
"""
method = 'hmyv2_getHeaderByNumber'
params = [
block_num
]
method = "hmyv2_getHeaderByNumber"
params = [block_num]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_latest_chain_headers(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_latest_chain_headers(
endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get block header of latest block for beacon chain & shard chain
@ -908,14 +952,22 @@ def get_latest_chain_headers(endpoint=_default_endpoint, timeout=_default_timeou
-------------
https://api.hmny.io/#7625493d-16bf-4611-8009-9635d063b4c0
"""
method = 'hmyv2_getLatestChainHeaders'
method = "hmyv2_getLatestChainHeaders"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_by_number(block_num, full_tx=False, include_tx=False, include_staking_tx=False,
include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_block_by_number(
block_num,
full_tx=False,
include_tx=False,
include_staking_tx=False,
include_signers=False,
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> dict:
"""
Get block by number
@ -979,20 +1031,30 @@ def get_block_by_number(block_num, full_tx=False, include_tx=False, include_stak
params = [
block_num,
{
'inclTx': include_tx,
'fullTx': full_tx,
'inclStaking': include_staking_tx,
'withSigners': include_signers,
"inclTx": include_tx,
"fullTx": full_tx,
"inclStaking": include_staking_tx,
"withSigners": include_signers,
},
]
method = 'hmyv2_getBlockByNumber'
method = "hmyv2_getBlockByNumber"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_by_hash(block_hash, full_tx=False, include_tx=False, include_staking_tx=False,
include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_block_by_hash(
block_hash,
full_tx=False,
include_tx=False,
include_staking_tx=False,
include_signers=False,
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> dict:
"""
Get block by hash
@ -1027,19 +1089,24 @@ def get_block_by_hash(block_hash, full_tx=False, include_tx=False, include_staki
params = [
block_hash,
{
'inclTx': include_tx,
'fullTx': full_tx,
'inclStaking': include_staking_tx,
'withSigners': include_signers,
"inclTx": include_tx,
"fullTx": full_tx,
"inclStaking": include_staking_tx,
"withSigners": include_signers,
},
]
method = 'hmyv2_getBlockByHash'
method = "hmyv2_getBlockByHash"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_transaction_count_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_block_transaction_count_by_number(
block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get transaction count for specific block number
@ -1068,16 +1135,21 @@ def get_block_transaction_count_by_number(block_num, endpoint=_default_endpoint,
-------------
https://api.hmny.io/#26c5adfb-d757-4595-9eb7-c6efef63df32
"""
params = [
block_num
]
method = 'hmyv2_getBlockTransactionCountByNumber'
params = [block_num]
method = "hmyv2_getBlockTransactionCountByNumber"
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_block_transaction_count_by_hash(
block_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get transaction count for specific block hash
@ -1106,16 +1178,21 @@ def get_block_transaction_count_by_hash(block_hash, endpoint=_default_endpoint,
-------------
https://api.hmny.io/#66c68844-0208-49bb-a83b-08722bc113eb
"""
params = [
block_hash
]
method = 'hmyv2_getBlockTransactionCountByHash'
params = [block_hash]
method = "hmyv2_getBlockTransactionCountByHash"
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_staking_transaction_count_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_block_staking_transaction_count_by_number(
block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get staking transaction count for specific block number
@ -1144,16 +1221,21 @@ def get_block_staking_transaction_count_by_number(block_num, endpoint=_default_e
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L494
"""
params = [
block_num
]
method = 'hmyv2_getBlockStakingTransactionCountByNumber'
params = [block_num]
method = "hmyv2_getBlockStakingTransactionCountByNumber"
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_staking_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_block_staking_transaction_count_by_hash(
block_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get staking transaction count for specific block hash
@ -1182,17 +1264,27 @@ def get_block_staking_transaction_count_by_hash(block_hash, endpoint=_default_en
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L523
"""
params = [
block_hash
]
method = 'hmyv2_getBlockStakingTransactionCountByHash'
params = [block_hash]
method = "hmyv2_getBlockStakingTransactionCountByHash"
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_blocks(start_block, end_block, full_tx=False, include_tx=False, include_staking_tx=False,
include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout
def get_blocks(
start_block,
end_block,
full_tx=False,
include_tx=False,
include_staking_tx=False,
include_signers=False,
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> list:
"""
Get list of blocks from a range
@ -1233,19 +1325,24 @@ def get_blocks(start_block, end_block, full_tx=False, include_tx=False, include_
start_block,
end_block,
{
'withSigners': include_signers,
'fullTx': full_tx,
'inclStaking': include_staking_tx,
'inclTx': include_tx
"withSigners": include_signers,
"fullTx": full_tx,
"inclStaking": include_staking_tx,
"inclTx": include_tx,
},
]
method = 'hmyv2_getBlocks'
method = "hmyv2_getBlocks"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_signers(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_block_signers(
block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of block signers for specific block number
@ -1272,16 +1369,19 @@ def get_block_signers(block_num, endpoint=_default_endpoint, timeout=_default_ti
-------------
https://api.hmny.io/#1e4b5f41-9db6-4dea-92fb-4408db78e622
"""
params = [
block_num
]
method = 'hmyv2_getBlockSigners'
params = [block_num]
method = "hmyv2_getBlockSigners"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_block_signers_keys(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_block_signers_keys(
block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of block signer public bls keys for specific block number
@ -1308,16 +1408,19 @@ def get_block_signers_keys(block_num, endpoint=_default_endpoint, timeout=_defau
-------------
https://api.hmny.io/#9f9c8298-1a4e-4901-beac-f34b59ed02f1
"""
params = [
block_num
]
method = 'hmyv2_getBlockSignerKeys'
params = [block_num]
method = "hmyv2_getBlockSignerKeys"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def is_block_signer(block_num, address, endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
def is_block_signer(
block_num, address, endpoint=_default_endpoint, timeout=_default_timeout
) -> bool:
"""
Determine if the account at address is a signer for the block at block_num
@ -1345,17 +1448,19 @@ def is_block_signer(block_num, address, endpoint=_default_endpoint, timeout=_def
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L368
"""
params = [
block_num,
address
]
method = 'hmyv2_isBlockSigner'
params = [block_num, address]
method = "hmyv2_isBlockSigner"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_signed_blocks(address, endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
def get_signed_blocks(
address, endpoint=_default_endpoint, timeout=_default_timeout
) -> bool:
"""
The number of blocks a particular validator signed for last blocksPeriod (1 epoch)
@ -1381,15 +1486,18 @@ def get_signed_blocks(address, endpoint=_default_endpoint, timeout=_default_time
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L406
"""
params = [
address
]
method = 'hmyv2_getSignedBlocks'
params = [address]
method = "hmyv2_getSignedBlocks"
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
"""
Get list of validators for specific epoch number
@ -1420,16 +1528,19 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout)
-------------
https://api.hmny.io/#4dfe91ad-71fa-4c7d-83f3-d1c86a804da5
"""
params = [
epoch
]
method = 'hmyv2_getValidators'
params = [epoch]
method = "hmyv2_getValidators"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_validator_keys(
epoch, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of validator public bls keys for specific epoch number
@ -1456,11 +1567,11 @@ def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeo
-------------
https://api.hmny.io/#1439b580-fa3c-4d44-a79d-303390997a8c
"""
params = [
epoch
]
method = 'hmyv2_getValidatorKeys'
params = [epoch]
method = "hmyv2_getValidatorKeys"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e

@ -54,9 +54,21 @@ import requests
from .util import get_bls_build_variables, get_gopath
if sys.platform.startswith("linux"):
_libs = {"libbls384_256.so", "libcrypto.so.10", "libgmp.so.10", "libgmpxx.so.4", "libmcl.so"}
_libs = {
"libbls384_256.so",
"libcrypto.so.10",
"libgmp.so.10",
"libgmpxx.so.4",
"libmcl.so",
}
else:
_libs = {"libbls384_256.dylib", "libcrypto.1.0.0.dylib", "libgmp.10.dylib", "libgmpxx.4.dylib", "libmcl.dylib"}
_libs = {
"libbls384_256.dylib",
"libcrypto.1.0.0.dylib",
"libgmp.10.dylib",
"libgmpxx.4.dylib",
"libmcl.dylib",
}
_accounts = {} # Internal accounts keystore, make sure to sync when needed.
_account_keystore_path = "~/.hmy/account-keys" # Internal path to account keystore, will match the current binary.
_binary_path = "hmy" # Internal binary path.
@ -149,14 +161,16 @@ def _make_call_command(command):
if isinstance(command, list):
command_toks = command
else:
all_strings = sorted(re.findall(r'"(.*?)"', command), key=lambda e: len(e), reverse=True)
all_strings = sorted(
re.findall(r'"(.*?)"', command), key=lambda e: len(e), reverse=True
)
for i, string in enumerate(all_strings):
command = command.replace(string, f"{_arg_prefix}_{i}")
command_toks_prefix = [el for el in command.split(" ") if el]
command_toks = []
for el in command_toks_prefix:
if el.startswith(f'"{_arg_prefix}_') and el.endswith(f'"'):
index = int(el.replace(f'"{_arg_prefix}_', '').replace('"', ''))
index = int(el.replace(f'"{_arg_prefix}_', "").replace('"', ""))
command_toks.append(all_strings[index])
else:
command_toks.append(el)
@ -183,8 +197,12 @@ def is_valid_binary(path):
path = os.path.realpath(path)
os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
try:
proc = subprocess.Popen([path, "version"], env=environment,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc = subprocess.Popen(
[path, "version"],
env=environment,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out, err = proc.communicate()
if not err:
return False
@ -223,12 +241,18 @@ def get_version():
"""
:return: The version string of the CLI binary.
"""
proc = subprocess.Popen([_binary_path, "version"], env=environment,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc = subprocess.Popen(
[_binary_path, "version"],
env=environment,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
out, err = proc.communicate()
if not err:
raise RuntimeError(f"Could not get version.\n"
f"\tGot exit code {proc.returncode}. Expected non-empty error message.")
raise RuntimeError(
f"Could not get version.\n"
f"\tGot exit code {proc.returncode}. Expected non-empty error message."
)
return err.decode().strip()
@ -280,8 +304,9 @@ def remove_account(name):
try:
shutil.rmtree(keystore_path)
except (shutil.Error, FileNotFoundError) as err:
raise RuntimeError(f"Failed to delete dir: {keystore_path}\n"
f"\tException: {err}") from err
raise RuntimeError(
f"Failed to delete dir: {keystore_path}\n" f"\tException: {err}"
) from err
_sync_accounts()
@ -304,11 +329,14 @@ def single_call(command, timeout=60, error_ok=False):
"""
command_toks = [_binary_path] + _make_call_command(command)
try:
return subprocess.check_output(command_toks, env=environment, timeout=timeout).decode()
return subprocess.check_output(
command_toks, env=environment, timeout=timeout
).decode()
except subprocess.CalledProcessError as err:
if not error_ok:
raise RuntimeError(f"Bad CLI args: `{command}`\n "
f"\tException: {err}") from err
raise RuntimeError(
f"Bad CLI args: `{command}`\n " f"\tException: {err}"
) from err
return err.output.decode()
@ -321,11 +349,14 @@ def expect_call(command, timeout=60):
"""
command_toks = _make_call_command(command)
try:
proc = pexpect.spawn(f"{_binary_path}", command_toks, env=environment, timeout=timeout)
proc = pexpect.spawn(
f"{_binary_path}", command_toks, env=environment, timeout=timeout
)
proc.delaybeforesend = None
except pexpect.ExceptionPexpect as err:
raise RuntimeError(f"Bad CLI args: `{command}`\n "
f"\tException: {err}") from err
raise RuntimeError(
f"Bad CLI args: `{command}`\n " f"\tException: {err}"
) from err
return proc
@ -341,28 +372,43 @@ def download(path="./bin/hmy", replace=True, verbose=True):
"""
path = os.path.realpath(path)
parent_dir = Path(path).parent
assert not os.path.isdir(path), f"path `{path}` must specify a file, not a directory."
assert not os.path.isdir(
path
), f"path `{path}` must specify a file, not a directory."
if not os.path.exists(path) or replace:
old_cwd = os.getcwd()
os.makedirs(parent_dir, exist_ok=True)
os.chdir(parent_dir)
hmy_script_path = os.path.join(parent_dir, "hmy.sh")
with open(hmy_script_path, 'w') as f:
f.write(requests.get("https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh")
.content.decode())
with open(hmy_script_path, "w") as f:
f.write(
requests.get(
"https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh"
).content.decode()
)
os.chmod(hmy_script_path, os.stat(hmy_script_path).st_mode | stat.S_IEXEC)
same_name_file = False
if os.path.exists(os.path.join(parent_dir, "hmy")) and Path(path).name != "hmy": # Save same name file.
if (
os.path.exists(os.path.join(parent_dir, "hmy")) and Path(path).name != "hmy"
): # Save same name file.
same_name_file = True
os.rename(os.path.join(parent_dir, "hmy"), os.path.join(parent_dir, ".hmy_tmp"))
os.rename(
os.path.join(parent_dir, "hmy"), os.path.join(parent_dir, ".hmy_tmp")
)
if verbose:
subprocess.call([hmy_script_path, '-d'])
subprocess.call([hmy_script_path, "-d"])
else:
subprocess.call([hmy_script_path, '-d'], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
subprocess.call(
[hmy_script_path, "-d"],
stdout=open(os.devnull, "w"),
stderr=subprocess.STDOUT,
)
os.rename(os.path.join(parent_dir, "hmy"), path)
if same_name_file:
os.rename(os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy"))
os.rename(
os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy")
)
if verbose:
print(f"Saved harmony binary to: `{path}`")
os.chdir(old_cwd)
@ -373,11 +419,16 @@ def download(path="./bin/hmy", replace=True, verbose=True):
files_in_parent_dir = set(os.listdir(parent_dir))
if files_in_parent_dir.intersection(_libs) == _libs:
env["DYLD_FALLBACK_LIBRARY_PATH"] = parent_dir
elif os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/bls") \
and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"):
elif os.path.exists(
f"{get_gopath()}/src/github.com/harmony-one/bls"
) and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"):
env.update(get_bls_build_variables())
else:
raise RuntimeWarning(f"Could not get environment for downloaded hmy CLI at `{path}`")
raise RuntimeWarning(
f"Could not get environment for downloaded hmy CLI at `{path}`"
)
except Exception as e:
raise RuntimeWarning(f"Could not get environment for downloaded hmy CLI at `{path}`") from e
raise RuntimeWarning(
f"Could not get environment for downloaded hmy CLI at `{path}`"
) from e
return env

@ -1,25 +1,28 @@
from .rpc.request import (
rpc_request
)
from .rpc.request import rpc_request
from .transaction import (
get_transaction_receipt
)
from .transaction import get_transaction_receipt
from .exceptions import (
InvalidRPCReplyError
)
from .exceptions import InvalidRPCReplyError
_default_endpoint = 'http://localhost:9500'
_default_endpoint = "http://localhost:9500"
_default_timeout = 30
#########################
# Smart contract RPCs
#########################
def call(to, block_num, from_address=None, gas=None, gas_price=None, value=None, data=None,
endpoint=_default_endpoint, timeout=_default_timeout) -> str:
def call(
to,
block_num,
from_address=None,
gas=None,
gas_price=None,
value=None,
data=None,
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> str:
"""
Execute a smart contract without saving state
@ -60,23 +63,34 @@ def call(to, block_num, from_address=None, gas=None, gas_price=None, value=None,
"""
params = [
{
'to': to,
'from': from_address,
'gas': gas,
'gasPrice': gas_price,
'value': value,
'data': data
"to": to,
"from": from_address,
"gas": gas,
"gasPrice": gas_price,
"value": value,
"data": data,
},
block_num
block_num,
]
method = 'hmyv2_call'
method = "hmyv2_call"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def estimate_gas(to, from_address=None, gas=None, gas_price=None, value=None, data=None,
endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def estimate_gas(
to,
from_address=None,
gas=None,
gas_price=None,
value=None,
data=None,
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> int:
"""
Estimate the gas price needed for a smart contract call
@ -113,21 +127,31 @@ def estimate_gas(to, from_address=None, gas=None, gas_price=None, value=None, da
-------------
https://api.hmny.io/?version=latest#b9bbfe71-8127-4dda-b26c-ff95c4c22abd
"""
params = [ {
'to': to,
'from': from_address,
'gas': gas,
'gasPrice': gas_price,
'value': value,
'data': data
} ]
method = 'hmyv2_estimateGas'
params = [
{
"to": to,
"from": from_address,
"gas": gas,
"gasPrice": gas_price,
"value": value,
"data": data,
}
]
method = "hmyv2_estimateGas"
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'], 16)
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
],
16,
)
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_code(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> str:
def get_code(
address, block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> str:
"""
Get the code stored at the given address in the state for the given block number
@ -157,17 +181,19 @@ def get_code(address, block_num, endpoint=_default_endpoint, timeout=_default_ti
https://api.hmny.io/?version=latest#e13e9d78-9322-4dc8-8917-f2e721a8e556
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L59
"""
params = [
address,
block_num
]
method = 'hmyv2_getCode'
params = [address, block_num]
method = "hmyv2_getCode"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_storage_at(address, key, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> str:
def get_storage_at(
address, key, block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> str:
"""
Get the storage from the state at the given address, the key and the block number
@ -199,18 +225,19 @@ def get_storage_at(address, key, block_num, endpoint=_default_endpoint, timeout=
https://api.hmny.io/?version=latest#fa8ac8bd-952d-4149-968c-857ca76da43f
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L84
"""
params = [
address,
key,
block_num
]
method = 'hmyv2_getStorageAt'
params = [address, key, block_num]
method = "hmyv2_getStorageAt"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_contract_address_from_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> str:
def get_contract_address_from_hash(
tx_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> str:
"""
Get address of the contract which was deployed in the transaction
represented by tx_hash

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

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

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

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

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

@ -1,18 +1,16 @@
from .rpc.request import (
rpc_request
)
from .rpc.request import rpc_request
from .exceptions import (
InvalidRPCReplyError
)
from .exceptions import InvalidRPCReplyError
_default_endpoint = 'http://localhost:9500'
_default_endpoint = "http://localhost:9500"
_default_timeout = 30
##################
# Validator RPCs #
##################
def get_all_validator_addresses(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_all_validator_addresses(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of all created validator addresses on chain
@ -37,13 +35,16 @@ def get_all_validator_addresses(endpoint=_default_endpoint, timeout=_default_tim
-------------
https://api.hmny.io/#69b93657-8d3c-4d20-9c9f-e51f08c9b3f5
"""
method = 'hmyv2_getAllValidatorAddresses'
method = "hmyv2_getAllValidatorAddresses"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validator_information(validator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_validator_information(
validator_addr, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get validator information for validator address
@ -112,16 +113,19 @@ def get_validator_information(validator_addr, endpoint=_default_endpoint, timeou
-------------
https://api.hmny.io/#659ad999-14ca-4498-8f74-08ed347cab49
"""
method = 'hmyv2_getValidatorInformation'
params = [
validator_addr
]
method = "hmyv2_getValidatorInformation"
params = [validator_addr]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_elected_validator_addresses(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_elected_validator_addresses(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of elected validator addresses
@ -147,12 +151,13 @@ def get_elected_validator_addresses(endpoint=_default_endpoint, timeout=_default
-------------
https://api.hmny.io/#e90a6131-d67c-4110-96ef-b283d452632d
"""
method = 'hmyv2_getElectedValidatorAddresses'
method = "hmyv2_getElectedValidatorAddresses"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
"""
Get validators list for a particular epoch
@ -183,16 +188,19 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout)
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152
"""
method = 'hmyv2_getValidators'
params = [
epoch
]
method = "hmyv2_getValidators"
params = [epoch]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_validator_keys(
epoch, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get validator BLS keys in the committee for a particular epoch
@ -218,16 +226,19 @@ def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeo
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152
"""
method = 'hmyv2_getValidatorKeys'
params = [
epoch
]
method = "hmyv2_getValidatorKeys"
params = [epoch]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validator_information_by_block_number(validator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout):
def get_validator_information_by_block_number(
validator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout
):
"""
Get validator information for validator address at a block
@ -255,17 +266,19 @@ def get_validator_information_by_block_number(validator_addr, block_num, endpoin
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L319
"""
method = 'hmyv2_getValidatorInformationByBlockNumber'
params = [
validator_addr,
block_num
]
method = "hmyv2_getValidatorInformationByBlockNumber"
params = [validator_addr, block_num]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_all_validator_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_all_validator_information(
page=0, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get validator information for all validators on chain
@ -291,16 +304,19 @@ def get_all_validator_information(page=0, endpoint=_default_endpoint, timeout=_d
-------------
https://api.hmny.io/#df5f1631-7397-48e8-87b4-8dd873235b9c
"""
method = 'hmyv2_getAllValidatorInformation'
params = [
page
]
method = "hmyv2_getAllValidatorInformation"
params = [page]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validator_self_delegation(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_validator_self_delegation(
address, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get the amount self delegated by validator
@ -326,16 +342,21 @@ def get_validator_self_delegation(address, endpoint=_default_endpoint, timeout=_
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L352
"""
method = 'hmyv2_getValidatorSelfDelegation'
params = [
address
]
method = "hmyv2_getValidatorSelfDelegation"
params = [address]
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_validator_total_delegation(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_validator_total_delegation(
address, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get the total amount delegated t ovalidator (including self delegated)
@ -361,16 +382,21 @@ def get_validator_total_delegation(address, endpoint=_default_endpoint, timeout=
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L379
"""
method = 'hmyv2_getValidatorTotalDelegation'
params = [
address
]
method = "hmyv2_getValidatorTotalDelegation"
params = [address]
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_all_validator_information_by_block_number(block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_all_validator_information_by_block_number(
block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get validator information at block number for all validators on chain
@ -399,20 +425,22 @@ def get_all_validator_information_by_block_number(block_num, page=0, endpoint=_d
-------------
https://api.hmny.io/#a229253f-ca76-4b9d-88f5-9fd96e40d583
"""
method = 'hmyv2_getAllValidatorInformationByBlockNumber'
params = [
page,
block_num
]
method = "hmyv2_getAllValidatorInformationByBlockNumber"
params = [page, block_num]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
###################
# Delegation RPCs #
###################
def get_all_delegation_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_all_delegation_information(
page=0, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get delegation information for all delegators on chain
@ -440,16 +468,21 @@ def get_all_delegation_information(page=0, endpoint=_default_endpoint, timeout=_
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L413
"""
method = 'hmyv2_getAllDelegationInformation'
method = "hmyv2_getAllDelegationInformation"
params = [
page,
]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_delegations_by_delegator(delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_delegations_by_delegator(
delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of delegations by a delegator
@ -482,16 +515,19 @@ def get_delegations_by_delegator(delegator_addr, endpoint=_default_endpoint, tim
-------------
https://api.hmny.io/#454b032c-6072-4ecb-bf24-38b3d6d2af69
"""
method = 'hmyv2_getDelegationsByDelegator'
params = [
delegator_addr
]
method = "hmyv2_getDelegationsByDelegator"
params = [delegator_addr]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_delegations_by_delegator_by_block_number(delegator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_delegations_by_delegator_by_block_number(
delegator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of delegations by a delegator at a specific block
@ -519,18 +555,22 @@ def get_delegations_by_delegator_by_block_number(delegator_addr, block_num, endp
-------------
https://api.hmny.io/#8ce13bda-e768-47b9-9dbe-193aba410b0a
"""
method = 'hmyv2_getDelegationsByDelegatorByBlockNumber'
params = [
delegator_addr,
block_num
]
method = "hmyv2_getDelegationsByDelegatorByBlockNumber"
params = [delegator_addr, block_num]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_delegation_by_delegator_and_validator(delegator_addr, validator_address,
endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_delegation_by_delegator_and_validator(
delegator_addr,
validator_address,
endpoint=_default_endpoint,
timeout=_default_timeout,
) -> dict:
"""
Get list of delegations by a delegator at a specific block
@ -558,17 +598,19 @@ def get_delegation_by_delegator_and_validator(delegator_addr, validator_address,
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L605
"""
method = 'hmyv2_getDelegationByDelegatorAndValidator'
params = [
delegator_addr,
validator_address
]
method = "hmyv2_getDelegationByDelegatorAndValidator"
params = [delegator_addr, validator_address]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_available_redelegation_balance(delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> int:
def get_available_redelegation_balance(
delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout
) -> int:
"""
Get amount of locked undelegated tokens
@ -594,16 +636,21 @@ def get_available_redelegation_balance(delegator_addr, endpoint=_default_endpoin
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L653
"""
method = 'hmyv2_getAvailableRedelegationBalance'
params = [
delegator_addr
]
method = "hmyv2_getAvailableRedelegationBalance"
params = [delegator_addr]
try:
return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'])
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
)
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_delegations_by_validator(validator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_delegations_by_validator(
validator_addr, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of delegations to a validator
@ -629,19 +676,22 @@ def get_delegations_by_validator(validator_addr, endpoint=_default_endpoint, tim
-------------
https://api.hmny.io/#2e02d8db-8fec-41d9-a672-2c9862f63f39
"""
method = 'hmyv2_getDelegationsByValidator'
params = [
validator_addr
]
method = "hmyv2_getDelegationsByValidator"
params = [validator_addr]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
########################
# Staking Network RPCs #
########################
def get_current_utility_metrics(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_current_utility_metrics(
endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get current utility metrics of network
@ -669,13 +719,16 @@ def get_current_utility_metrics(endpoint=_default_endpoint, timeout=_default_tim
-------------
https://api.hmny.io/#78dd2d94-9ff1-4e0c-bbac-b4eec1cdf10b
"""
method = 'hmyv2_getCurrentUtilityMetrics'
method = "hmyv2_getCurrentUtilityMetrics"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_network_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_staking_network_info(
endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get staking network information
@ -704,12 +757,13 @@ def get_staking_network_info(endpoint=_default_endpoint, timeout=_default_timeou
-------------
https://api.hmny.io/#4a10fce0-2aa4-4583-bdcb-81ee0800993b
"""
method = 'hmyv2_getStakingNetworkInfo'
method = "hmyv2_getStakingNetworkInfo"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
"""
Get voting committees for current & previous epoch
@ -757,12 +811,13 @@ def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) -
-------------
https://api.hmny.io/#8eef2fc4-92db-4610-a9cd-f7b75cfbd080
"""
method = 'hmyv2_getSuperCommittees'
method = "hmyv2_getSuperCommittees"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> int:
"""
Get total staking by validators, only meant to be called on beaconchain
@ -787,13 +842,16 @@ def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> i
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L102
"""
method = 'hmyv2_getTotalStaking'
method = "hmyv2_getTotalStaking"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'])
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_raw_median_stake_snapshot(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_raw_median_stake_snapshot(
endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get median stake & additional committee data of the current epoch
@ -830,8 +888,8 @@ def get_raw_median_stake_snapshot(endpoint=_default_endpoint, timeout=_default_t
-------------
https://api.hmny.io/#bef93b3f-6763-4121-9c17-f0b0d9e5cc40
"""
method = 'hmyv2_getMedianRawStakeSnapshot'
method = "hmyv2_getMedianRawStakeSnapshot"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e

@ -6,29 +6,19 @@ from cytoolz import (
identity,
)
from hexbytes import (
HexBytes
)
from hexbytes import HexBytes
import rlp
import math
from decimal import (
Decimal
)
from decimal import Decimal
from eth_account.datastructures import (
SignedTransaction
)
from eth_account.datastructures import SignedTransaction
from eth_account._utils.signing import (
sign_transaction_hash
)
from eth_account._utils.signing import sign_transaction_hash
from eth_account._utils.legacy_transactions import (
chain_id_to_v
)
from eth_account._utils.legacy_transactions import chain_id_to_v
from eth_utils.curried import (
hexstr_if_str,
@ -37,12 +27,10 @@ from eth_utils.curried import (
apply_formatters_to_dict,
to_int,
apply_formatters_to_sequence,
apply_formatter_to_array
apply_formatter_to_array,
)
from .signing import (
sanitize_transaction
)
from .signing import sanitize_transaction
from .staking_structures import (
FORMATTERS,
@ -51,14 +39,15 @@ from .staking_structures import (
CreateValidator,
EditValidator,
DelegateOrUndelegate,
CollectRewards
CollectRewards,
)
from .util import (
convert_one_to_hex
)
from .util import convert_one_to_hex
def _convert_staking_percentage_to_number(value): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335
def _convert_staking_percentage_to_number(
value,
): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335
"""
Convert from staking percentage to integer
For example, 0.1 becomes 1000000000000000000
@ -77,38 +66,43 @@ def _convert_staking_percentage_to_number(value): # https://github.com/ha
AssertionError, if data types are not as expected
ValueError, if the input type is not supported
"""
assert isinstance(value, (str, Decimal)), 'Only strings or decimals are supported'
assert isinstance(value, (str, Decimal)), "Only strings or decimals are supported"
if isinstance(value, Decimal):
value = str(value)
value1 = value;
if value[0] == '-':
raise ValueError('Negative numbers are not accepted')
if value[0] == '+':
value1 = value
if value[0] == "-":
raise ValueError("Negative numbers are not accepted")
if value[0] == "+":
value1 = value[1:]
if len(value1) == 0:
raise ValueError('StakingDecimal string is empty')
spaced = value1.split(' ')
raise ValueError("StakingDecimal string is empty")
spaced = value1.split(" ")
if len(spaced) > 1:
raise ValueError('Bad decimal string')
splitted = value1.split('.')
raise ValueError("Bad decimal string")
splitted = value1.split(".")
combined_str = splitted[0]
if len(splitted) == 2:
length = len(splitted[1])
if length == 0 or len(combined_str) == 0:
raise ValueError('Bad StakingDecimal length')
if splitted[1][0] == '-':
raise ValueError('Bad StakingDecimal string')
raise ValueError("Bad StakingDecimal length")
if splitted[1][0] == "-":
raise ValueError("Bad StakingDecimal string")
combined_str += splitted[1]
elif len(splitted) > 2:
raise ValueError('Too many periods to be a StakingDecimal string')
raise ValueError("Too many periods to be a StakingDecimal string")
if length > StakingSettings.PRECISION:
raise ValueError('Too much precision, must be less than {StakingSettings.PRECISION}')
raise ValueError(
"Too much precision, must be less than {StakingSettings.PRECISION}"
)
zeroes_to_add = StakingSettings.PRECISION - length
combined_str += '0' * zeroes_to_add # This will not have any periods, so it is effectively a large integer
combined_str += (
"0" * zeroes_to_add
) # This will not have any periods, so it is effectively a large integer
val = int(combined_str)
assert val <= StakingSettings.MAX_DECIMAL, 'Staking percentage is too large'
assert val <= StakingSettings.MAX_DECIMAL, "Staking percentage is too large"
return val
def _get_account_and_transaction(transaction_dict, private_key):
"""
Create account from private key and sanitize the transaction
@ -132,10 +126,15 @@ def _get_account_and_transaction(transaction_dict, private_key):
AssertionError, if chainId is not present in util.chain_id_to_int
TypeError, if the value of 'from' key is not the same as account address
"""
account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) # remove from, convert chain id (if present) to integer
sanitized_transaction['directive'] = sanitized_transaction['directive'].value # convert to value, like in TypeScript
account, sanitized_transaction = sanitize_transaction(
transaction_dict, private_key
) # remove from, convert chain id (if present) to integer
sanitized_transaction["directive"] = sanitized_transaction[
"directive"
].value # convert to value, like in TypeScript
return account, sanitized_transaction
def _sign_transaction_generic(account, sanitized_transaction, parent_serializer):
"""
Sign a generic staking transaction, given the serializer base class and account
@ -157,39 +156,54 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer)
rlp.exceptions.ObjectSerializationError, if data types are not as expected
"""
# obtain the serializers
if sanitized_transaction.get('chainId', 0) == 0:
unsigned_serializer, signed_serializer = parent_serializer.Unsigned(), parent_serializer.Signed() # unsigned, signed
if sanitized_transaction.get("chainId", 0) == 0:
unsigned_serializer, signed_serializer = (
parent_serializer.Unsigned(),
parent_serializer.Signed(),
) # unsigned, signed
else:
unsigned_serializer, signed_serializer = parent_serializer.SignedChainId(), parent_serializer.SignedChainId() # since chain_id_to_v adds v/r/s, unsigned is not used here
unsigned_serializer, signed_serializer = (
parent_serializer.SignedChainId(),
parent_serializer.SignedChainId(),
) # since chain_id_to_v adds v/r/s, unsigned is not used here
# fill the transaction
filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39
sanitized_transaction,
dict,
partial(merge, {'chainId': None}),
partial(merge, {"chainId": None}),
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
for f, _ in unsigned_serializer._meta.fields:
assert f in filled_transaction, f'Could not find {f} in transaction'
unsigned_transaction = unsigned_serializer.from_dict(\
{f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields}) # drop extras silently
assert f in filled_transaction, f"Could not find {f} in transaction"
unsigned_transaction = unsigned_serializer.from_dict(
{f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields}
) # drop extras silently
# sign the unsigned transaction
if 'v' in unsigned_transaction.as_dict():
if "v" in unsigned_transaction.as_dict():
chain_id = unsigned_transaction.v
else:
chain_id = None
transaction_hash = unsigned_transaction.hash()
(v, r, s) = sign_transaction_hash(
account._key_obj, transaction_hash, chain_id)
(v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id)
chain_naive_transaction = dissoc(
unsigned_transaction.as_dict(), 'v', 'r', 's') # remove extra v/r/s added by chain_id_to_v
unsigned_transaction.as_dict(), "v", "r", "s"
) # remove extra v/r/s added by chain_id_to_v
# serialize it
signed_transaction = signed_serializer(
v=v + (8 if chain_id is None else 0), # copied from https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207
v=v
+ (
8 if chain_id is None else 0
), # copied from https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207
r=r,
s=s, # in the below statement, remove everything not expected by signed_serializer
**{f: chain_naive_transaction[f] for f, _ in signed_serializer._meta.fields if f not in 'vrs'})
**{
f: chain_naive_transaction[f]
for f, _ in signed_serializer._meta.fields
if f not in "vrs"
},
)
# encode it
encoded_transaction = rlp.encode(signed_transaction)
# hash it
@ -203,29 +217,34 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer)
v=v,
)
def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate):
"""
Sign a delegate or undelegate transaction
See sign_staking_transaction for details
"""
# preliminary steps
if transaction_dict['directive'] not in [ Directive.Delegate, Directive.Undelegate ]:
raise TypeError('Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate')
if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]:
raise TypeError(
"Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate"
)
# first common step
account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key)
account, sanitized_transaction = _get_account_and_transaction(
transaction_dict, private_key
)
# encode the stakeMsg
sanitized_transaction['stakeMsg'] = \
apply_formatters_to_sequence( [
hexstr_if_str(to_bytes),
hexstr_if_str(to_bytes),
hexstr_if_str(to_int)
], [
convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')),
convert_one_to_hex(sanitized_transaction.pop('validatorAddress')),
sanitized_transaction.pop('amount'),
]
sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence(
[hexstr_if_str(to_bytes), hexstr_if_str(to_bytes), hexstr_if_str(to_int)],
[
convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")),
convert_one_to_hex(sanitized_transaction.pop("validatorAddress")),
sanitized_transaction.pop("amount"),
],
)
return _sign_transaction_generic(
account, sanitized_transaction, DelegateOrUndelegate
)
return _sign_transaction_generic(account, sanitized_transaction, DelegateOrUndelegate)
def _sign_collect_rewards(transaction_dict, private_key):
"""
@ -233,109 +252,147 @@ def _sign_collect_rewards(transaction_dict, private_key):
See sign_staking_transaction for details
"""
# preliminary steps
if transaction_dict['directive'] != Directive.CollectRewards:
raise TypeError('Only CollectRewards is supported by _sign_collect_rewards')
if transaction_dict["directive"] != Directive.CollectRewards:
raise TypeError("Only CollectRewards is supported by _sign_collect_rewards")
# first common step
account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key)
account, sanitized_transaction = _get_account_and_transaction(
transaction_dict, private_key
)
# encode the stakeMsg
sanitized_transaction['stakeMsg'] = \
[hexstr_if_str(to_bytes)(convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')))]
sanitized_transaction["stakeMsg"] = [
hexstr_if_str(to_bytes)(
convert_one_to_hex(sanitized_transaction.pop("delegatorAddress"))
)
]
return _sign_transaction_generic(account, sanitized_transaction, CollectRewards)
def _sign_create_validator(transaction_dict, private_key):
"""
Sign a create validator transaction
See sign_staking_transaction for details
"""
# preliminary steps
if transaction_dict['directive'] != Directive.CreateValidator:
raise TypeError('Only CreateValidator is supported by _sign_create_or_edit_validator')
if transaction_dict["directive"] != Directive.CreateValidator:
raise TypeError(
"Only CreateValidator is supported by _sign_create_or_edit_validator"
)
# first common step
account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key)
account, sanitized_transaction = _get_account_and_transaction(
transaction_dict, private_key
)
# encode the stakeMsg
description = [
sanitized_transaction.pop('name'),
sanitized_transaction.pop('identity'),
sanitized_transaction.pop('website'),
sanitized_transaction.pop('security-contact'),
sanitized_transaction.pop('details'),
sanitized_transaction.pop("name"),
sanitized_transaction.pop("identity"),
sanitized_transaction.pop("website"),
sanitized_transaction.pop("security-contact"),
sanitized_transaction.pop("details"),
]
commission = apply_formatter_to_array( hexstr_if_str(to_int), # formatter
commission = apply_formatter_to_array(
hexstr_if_str(to_int), # formatter
[
_convert_staking_percentage_to_number(sanitized_transaction.pop('rate')),
_convert_staking_percentage_to_number(sanitized_transaction.pop('max-rate')),
_convert_staking_percentage_to_number(sanitized_transaction.pop('max-change-rate')),
]
_convert_staking_percentage_to_number(sanitized_transaction.pop("rate")),
_convert_staking_percentage_to_number(
sanitized_transaction.pop("max-rate")
),
_convert_staking_percentage_to_number(
sanitized_transaction.pop("max-change-rate")
),
],
)
commission = [[element] for element in commission]
bls_keys = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter
sanitized_transaction.pop('bls-public-keys')
bls_keys = apply_formatter_to_array(
hexstr_if_str(to_bytes), # formatter
sanitized_transaction.pop("bls-public-keys"),
)
bls_key_sigs = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter
sanitized_transaction.pop('bls-key-sigs')
bls_key_sigs = apply_formatter_to_array(
hexstr_if_str(to_bytes), sanitized_transaction.pop("bls-key-sigs") # formatter
)
sanitized_transaction['stakeMsg'] = \
apply_formatters_to_sequence( [
sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence(
[
hexstr_if_str(to_bytes), # address
identity, # description
identity, # commission rates
hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped
hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped
hexstr_if_str(
to_int
), # min self delegation (in ONE), decimals are silently dropped
hexstr_if_str(
to_int
), # max total delegation (in ONE), decimals are silently dropped
identity, # bls public keys
identity, # bls key sigs
hexstr_if_str(to_int), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too)
], [
convert_one_to_hex(sanitized_transaction.pop('validatorAddress')),
hexstr_if_str(
to_int
), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too)
],
[
convert_one_to_hex(sanitized_transaction.pop("validatorAddress")),
description,
commission,
math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly
math.floor(sanitized_transaction.pop('max-total-delegation')),
math.floor(
sanitized_transaction.pop("min-self-delegation")
), # Decimal floors it correctly
math.floor(sanitized_transaction.pop("max-total-delegation")),
bls_keys,
bls_key_sigs,
math.floor(sanitized_transaction.pop('amount')),
]
math.floor(sanitized_transaction.pop("amount")),
],
)
return _sign_transaction_generic(account, sanitized_transaction, CreateValidator)
def _sign_edit_validator(transaction_dict, private_key):
"""
Sign an edit validator transaction
See sign_staking_transaction for details
"""
# preliminary steps
if transaction_dict['directive'] != Directive.EditValidator:
raise TypeError('Only EditValidator is supported by _sign_create_or_edit_validator')
if transaction_dict["directive"] != Directive.EditValidator:
raise TypeError(
"Only EditValidator is supported by _sign_create_or_edit_validator"
)
# first common step
account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key)
account, sanitized_transaction = _get_account_and_transaction(
transaction_dict, private_key
)
# encode the stakeMsg
description = [
sanitized_transaction.pop('name'),
sanitized_transaction.pop('identity'),
sanitized_transaction.pop('website'),
sanitized_transaction.pop('security-contact'),
sanitized_transaction.pop('details'),
sanitized_transaction.pop("name"),
sanitized_transaction.pop("identity"),
sanitized_transaction.pop("website"),
sanitized_transaction.pop("security-contact"),
sanitized_transaction.pop("details"),
]
sanitized_transaction['stakeMsg'] = \
apply_formatters_to_sequence( [
sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence(
[
hexstr_if_str(to_bytes), # address
identity, # description
identity, # new rate (it's in a list so can't do hexstr_if_str)
hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped
hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped
hexstr_if_str(
to_int
), # min self delegation (in ONE), decimals are silently dropped
hexstr_if_str(
to_int
), # max total delegation (in ONE), decimals are silently dropped
hexstr_if_str(to_bytes), # key to remove
hexstr_if_str(to_bytes), # key to add
], [
convert_one_to_hex(sanitized_transaction.pop('validatorAddress')),
],
[
convert_one_to_hex(sanitized_transaction.pop("validatorAddress")),
description,
[ _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')) ],
math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly
math.floor(sanitized_transaction.pop('max-total-delegation')),
sanitized_transaction.pop('bls-key-to-remove'),
sanitized_transaction.pop('bls-key-to-add')
]
[_convert_staking_percentage_to_number(sanitized_transaction.pop("rate"))],
math.floor(
sanitized_transaction.pop("min-self-delegation")
), # Decimal floors it correctly
math.floor(sanitized_transaction.pop("max-total-delegation")),
sanitized_transaction.pop("bls-key-to-remove"),
sanitized_transaction.pop("bls-key-to-add"),
],
)
return _sign_transaction_generic(account, sanitized_transaction, EditValidator)
def sign_staking_transaction(transaction_dict, private_key):
"""
Sign a supplied transaction_dict with the private_key
@ -404,18 +461,22 @@ def sign_staking_transaction(transaction_dict, private_key):
-------------
https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts
"""
assert isinstance(transaction_dict, dict), 'Only dictionaries are supported' # OrderedDict is a subclass
assert isinstance(
transaction_dict, dict
), "Only dictionaries are supported" # OrderedDict is a subclass
# chain_id missing => 'rlp: input string too long for uint64, decoding into (types.StakingTransaction)(types.txdata).GasLimit'
assert 'chainId' in transaction_dict, 'chainId missing'
assert 'directive' in transaction_dict, 'Staking transaction type not specified'
assert isinstance(transaction_dict['directive'], Directive), 'Unknown staking transaction type'
if transaction_dict['directive'] == Directive.CollectRewards:
assert "chainId" in transaction_dict, "chainId missing"
assert "directive" in transaction_dict, "Staking transaction type not specified"
assert isinstance(
transaction_dict["directive"], Directive
), "Unknown staking transaction type"
if transaction_dict["directive"] == Directive.CollectRewards:
return _sign_collect_rewards(transaction_dict, private_key)
elif transaction_dict['directive'] == Directive.Delegate:
elif transaction_dict["directive"] == Directive.Delegate:
return _sign_delegate_or_undelegate(transaction_dict, private_key, True)
elif transaction_dict['directive'] == Directive.Undelegate:
elif transaction_dict["directive"] == Directive.Undelegate:
return _sign_delegate_or_undelegate(transaction_dict, private_key, False)
elif transaction_dict['directive'] == Directive.CreateValidator:
elif transaction_dict["directive"] == Directive.CreateValidator:
return _sign_create_validator(transaction_dict, private_key)
elif transaction_dict['directive'] == Directive.EditValidator:
elif transaction_dict["directive"] == Directive.EditValidator:
return _sign_edit_validator(transaction_dict, private_key)

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

@ -1,21 +1,18 @@
from .rpc.request import (
rpc_request
)
from .exceptions import (
TxConfirmationTimedoutError,
InvalidRPCReplyError
)
from .rpc.request import rpc_request
from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError
import time
import random
_default_endpoint = 'http://localhost:9500'
_default_endpoint = "http://localhost:9500"
_default_timeout = 30
#########################
# Transaction Pool RPCs #
#########################
def get_pending_transactions(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_pending_transactions(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of pending transactions
@ -39,13 +36,16 @@ def get_pending_transactions(endpoint=_default_endpoint, timeout=_default_timeou
-------------
https://api.hmny.io/#de6c4a12-fa42-44e8-972f-801bfde1dd18
"""
method = 'hmyv2_pendingTransactions'
method = "hmyv2_pendingTransactions"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_transaction_error_sink(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get current transactions error sink
@ -72,13 +72,16 @@ def get_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_time
-------------
https://api.hmny.io/#9aedbc22-6262-44b1-8276-cd8ae19fa600
"""
method = 'hmyv2_getCurrentTransactionErrorSink'
method = "hmyv2_getCurrentTransactionErrorSink"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_pending_staking_transactions(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_pending_staking_transactions(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of pending staking transactions
@ -102,14 +105,16 @@ def get_pending_staking_transactions(endpoint=_default_endpoint, timeout=_defaul
-------------
https://api.hmny.io/#de0235e4-f4c9-4a69-b6d2-b77dc1ba7b12
"""
method = 'hmyv2_pendingStakingTransactions'
method = "hmyv2_pendingStakingTransactions"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_staking_transaction_error_sink(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get current staking transactions error sink
@ -137,12 +142,13 @@ def get_staking_transaction_error_sink(endpoint=_default_endpoint, timeout=_defa
-------------
https://api.hmny.io/#bdd00e0f-2ba0-480e-b996-2ef13f10d75a
"""
method = 'hmyv2_getCurrentStakingErrorSink'
method = "hmyv2_getCurrentStakingErrorSink"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
"""
Get stats of the pool, that is, number of pending and queued (non-executable) transactions
@ -169,16 +175,19 @@ def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict
-------------
https://api.hmny.io/#7c2b9395-8f5e-4eb5-a687-2f1be683d83e
"""
method = 'hmyv2_getPoolStats'
method = "hmyv2_getPoolStats"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
####################
# Transaction RPCs #
####################
def get_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_transaction_by_hash(
tx_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get transaction by hash
@ -224,17 +233,18 @@ def get_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_defaul
-------------
https://api.hmny.io/#117e84f6-a0ec-444e-abe0-455701310389
"""
method = 'hmyv2_getTransactionByHash'
params = [
tx_hash
]
method = "hmyv2_getTransactionByHash"
params = [tx_hash]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_transaction_by_block_hash_and_index(block_hash, tx_index,
endpoint=_default_endpoint, timeout=_default_timeout
def get_transaction_by_block_hash_and_index(
block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get transaction based on index in list of transactions in a block by block hash
@ -263,18 +273,18 @@ def get_transaction_by_block_hash_and_index(block_hash, tx_index,
-------------
https://api.hmny.io/#7c7e8d90-4984-4ebe-bb7e-d7adec167503
"""
method = 'hmyv2_getTransactionByBlockHashAndIndex'
params = [
block_hash,
tx_index
]
method = "hmyv2_getTransactionByBlockHashAndIndex"
params = [block_hash, tx_index]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_transaction_by_block_number_and_index(block_num, tx_index,
endpoint=_default_endpoint, timeout=_default_timeout
def get_transaction_by_block_number_and_index(
block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get transaction based on index in list of transactions in a block by block number
@ -303,17 +313,19 @@ def get_transaction_by_block_number_and_index(block_num, tx_index,
-------------
https://api.hmny.io/#bcde8b1c-6ab9-4950-9835-3c7564e49c3e
"""
method = 'hmyv2_getTransactionByBlockNumberAndIndex'
params = [
block_num,
tx_index
]
method = "hmyv2_getTransactionByBlockNumberAndIndex"
params = [block_num, tx_index]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_transaction_receipt(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_transaction_receipt(
tx_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get transaction receipt corresponding to tx_hash
@ -353,16 +365,19 @@ def get_transaction_receipt(tx_hash, endpoint=_default_endpoint, timeout=_defaul
-------------
https://api.hmny.io/#0c2799f8-bcdc-41a4-b362-c3a6a763bb5e
"""
method = 'hmyv2_getTransactionReceipt'
params = [
tx_hash
]
method = "hmyv2_getTransactionReceipt"
params = [tx_hash]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def send_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> str:
def send_raw_transaction(
signed_tx, endpoint=_default_endpoint, timeout=_default_timeout
) -> str:
"""
Send signed transaction
@ -391,16 +406,19 @@ def send_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default
-------------
https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0
"""
params = [
signed_tx
]
method = 'hmyv2_sendRawTransaction'
params = [signed_tx]
method = "hmyv2_sendRawTransaction"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def send_and_confirm_raw_transaction(
signed_tx, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Send signed transaction and wait for it to be confirmed
@ -433,7 +451,7 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time
"""
tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint)
start_time = time.time()
while((time.time() - start_time) <= timeout):
while (time.time() - start_time) <= timeout:
tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint)
if tx_response is not None:
block_hash = tx_response.get("blockHash", "0x00")
@ -443,10 +461,13 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time
time.sleep(random.uniform(0.2, 0.5))
raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.")
###############################
# CrossShard Transaction RPCs #
###############################
def get_pending_cx_receipts(endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def get_pending_cx_receipts(
endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Get list of pending cross shard transactions
@ -493,13 +514,16 @@ def get_pending_cx_receipts(endpoint=_default_endpoint, timeout=_default_timeout
-------------
https://api.hmny.io/#fe60070d-97b4-458d-9365-490b44c18851
"""
method = 'hmyv2_getPendingCXReceipts'
method = "hmyv2_getPendingCXReceipts"
try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_cx_receipt_by_hash(cx_hash, endpoint = _default_endpoint, timeout = _default_timeout) -> dict:
def get_cx_receipt_by_hash(
cx_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get cross shard receipt by hash on the receiving shard end point
@ -534,16 +558,19 @@ def get_cx_receipt_by_hash(cx_hash, endpoint = _default_endpoint, timeout = _def
-------------
https://api.hmny.io/#3d6ad045-800d-4021-aeb5-30a0fbf724fe
"""
params = [
cx_hash
]
method = 'hmyv2_getCXReceiptByHash'
params = [cx_hash]
method = "hmyv2_getCXReceiptByHash"
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def resend_cx_receipt(cx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> bool:
def resend_cx_receipt(
cx_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> bool:
"""
Resend the cross shard receipt to the receiving shard to re-process if the transaction did not pay out
@ -570,19 +597,22 @@ def resend_cx_receipt(cx_hash, endpoint=_default_endpoint, timeout=_default_time
-------------
https://api.hmny.io/#c658b56b-d20b-480d-b71a-b0bc505d2164
"""
method = 'hmyv2_resendCx'
params = [
cx_hash
]
method = "hmyv2_resendCx"
params = [cx_hash]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
############################
# Staking Transaction RPCs #
############################
def get_staking_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict:
def get_staking_transaction_by_hash(
tx_hash, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get staking transaction by hash
@ -622,17 +652,18 @@ def get_staking_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout
-------------
https://api.hmny.io/#296cb4d0-bce2-48e3-bab9-64c3734edd27
"""
method = 'hmyv2_getStakingTransactionByHash'
params = [
tx_hash
]
method = "hmyv2_getStakingTransactionByHash"
params = [tx_hash]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_transaction_by_block_hash_and_index(block_hash, tx_index,
endpoint=_default_endpoint, timeout=_default_timeout
def get_staking_transaction_by_block_hash_and_index(
block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get staking transaction by block hash and transaction index
@ -661,18 +692,18 @@ def get_staking_transaction_by_block_hash_and_index(block_hash, tx_index,
-------------
https://api.hmny.io/#ba96cf61-61fe-464a-aa06-2803bb4b358f
"""
method = 'hmyv2_getStakingTransactionByBlockHashAndIndex'
params = [
block_hash,
tx_index
]
method = "hmyv2_getStakingTransactionByBlockHashAndIndex"
params = [block_hash, tx_index]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def get_staking_transaction_by_block_number_and_index(block_num, tx_index,
endpoint=_default_endpoint, timeout=_default_timeout
def get_staking_transaction_by_block_number_and_index(
block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout
) -> dict:
"""
Get staking transaction by block number and transaction index
@ -701,17 +732,19 @@ def get_staking_transaction_by_block_number_and_index(block_num, tx_index,
-------------
https://api.hmny.io/#fb41d717-1645-4d3e-8071-6ce8e1b65dd3
"""
method = 'hmyv2_getStakingTransactionByBlockNumberAndIndex'
params = [
block_num,
tx_index
]
method = "hmyv2_getStakingTransactionByBlockNumberAndIndex"
params = [block_num, tx_index]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def send_raw_staking_transaction(raw_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> str:
def send_raw_staking_transaction(
raw_tx, endpoint=_default_endpoint, timeout=_default_timeout
) -> str:
"""
Send signed staking transaction
@ -740,16 +773,19 @@ def send_raw_staking_transaction(raw_tx, endpoint=_default_endpoint, timeout=_de
-------------
https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401
"""
method = 'hmyv2_sendRawStakingTransaction'
params = [
raw_tx
]
method = "hmyv2_sendRawStakingTransaction"
params = [raw_tx]
try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
except KeyError as e:
raise InvalidRPCReplyError(method, endpoint) from e
def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> list:
def send_and_confirm_raw_staking_transaction(
signed_tx, endpoint=_default_endpoint, timeout=_default_timeout
) -> list:
"""
Send signed staking transaction and wait for it to be confirmed
@ -782,7 +818,7 @@ def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoi
"""
tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint)
start_time = time.time()
while((time.time() - start_time) <= timeout):
while (time.time() - start_time) <= timeout:
tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint)
if tx_response is not None:
block_hash = tx_response.get("blockHash", "0x00")

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

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

@ -1,10 +1,9 @@
from pyhmy.bech32 import (
bech32
)
from pyhmy.bech32 import bech32
def test_encode():
bech32.encode('one', 5, [121, 161])
bech32.encode("one", 5, [121, 161])
def test_decode():
bech32.decode('one', 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9')
def test_decode():
bech32.decode("one", "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9")

@ -14,11 +14,11 @@ def test_basic_logger():
logger.debug("test debug")
logger.error("test error")
logger.warning("test warning")
with open(f"{os.getcwd()}/logs/pytest.log", 'r') as f:
with open(f"{os.getcwd()}/logs/pytest.log", "r") as f:
log_file_contents = f.readlines()
assert not log_file_contents
logger.write()
with open(f"{os.getcwd()}/logs/pytest.log", 'r') as f:
with open(f"{os.getcwd()}/logs/pytest.log", "r") as f:
log_file_contents = f.readlines()
for line in log_file_contents:
if "INFO" in line:

@ -1,8 +1,6 @@
from decimal import Decimal
from pyhmy import (
numbers
)
from pyhmy import numbers
def test_convert_atto_to_one():
@ -12,12 +10,13 @@ def test_convert_atto_to_one():
b = numbers.convert_atto_to_one(1e18 + 0.6)
assert Decimal(1) == b
c = numbers.convert_atto_to_one('1' + ('0' * 18))
c = numbers.convert_atto_to_one("1" + ("0" * 18))
assert Decimal(1) == c
d = numbers.convert_atto_to_one(Decimal(1e18))
assert Decimal(1) == d
def test_convert_one_to_atto():
a = numbers.convert_one_to_atto(1e-18)
assert Decimal(1) == a
@ -25,7 +24,7 @@ def test_convert_one_to_atto():
b = numbers.convert_one_to_atto(1.5)
assert Decimal(1.5e18) == b
c = numbers.convert_one_to_atto('1')
c = numbers.convert_one_to_atto("1")
assert Decimal(1e18) == c
d = numbers.convert_one_to_atto(Decimal(1))

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

@ -9,11 +9,9 @@ import requests
# 1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc / one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 (genesis)
# 3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65 / one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (transferred 503)
endpoint = 'http://localhost:9500'
endpoint = "http://localhost:9500"
timeout = 30
headers = {
'Content-Type': 'application/json'
}
headers = {"Content-Type": "application/json"}
txs = [
# same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce)
"0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3",
@ -40,6 +38,7 @@ stx_hashes = [
assert len(txs) == len(tx_hashes), "Mismatch in tx and tx_hash count"
assert len(stxs) == len(stx_hashes), "Mismatch in stx and stx_hash count"
@pytest.fixture(scope="session", autouse=True)
def setup_blockchain():
# return
@ -52,33 +51,56 @@ def setup_blockchain():
tx_hash = tx_hashes[i]
_send_transaction(tx, endpoint)
if not _wait_for_transaction_confirmed(tx_hash, endpoint):
pytest.skip("Could not confirm initial transaction #{} on chain".format( i ), allow_module_level = True)
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)
pytest.skip(
"Could not confirm initial staking transaction #{} on chain".format(i),
allow_module_level=True,
)
def _check_connection():
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": 'hmyv2_getNodeMetadata',
"params": []
"method": "hmyv2_getNodeMetadata",
"params": [],
}
response = requests.request('POST', endpoint, headers=headers,
data=json.dumps(payload), timeout=timeout, allow_redirects=True)
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
metadata = json.loads(response.content)
if 'error' in metadata:
pytest.skip(f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}", allow_module_level=True)
if 'chain-config' not in metadata['result']:
pytest.skip("Chain config not found in hmyv2_getNodeMetadata reply", allow_module_level=True)
if "error" in metadata:
pytest.skip(
f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}",
allow_module_level=True,
)
if "chain-config" not in metadata["result"]:
pytest.skip(
"Chain config not found in hmyv2_getNodeMetadata reply",
allow_module_level=True,
)
return metadata
except Exception as e:
pytest.skip('Can not connect to local blockchain or bad hmyv2_getNodeMetadata reply', allow_module_level=True)
pytest.skip(
"Can not connect to local blockchain or bad hmyv2_getNodeMetadata reply",
allow_module_level=True,
)
def _check_staking_epoch(metadata):
latest_header = None
@ -86,57 +108,91 @@ def _check_staking_epoch(metadata):
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": 'hmyv2_latestHeader',
"params": []
"method": "hmyv2_latestHeader",
"params": [],
}
response = requests.request('POST', endpoint, headers=headers,
data=json.dumps(payload), timeout=timeout, allow_redirects=True)
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
latest_header = json.loads(response.content)
if 'error' in latest_header:
pytest.skip(f"Error in hmyv2_latestHeader reply: {latest_header['error']}", allow_module_level=True)
if "error" in latest_header:
pytest.skip(
f"Error in hmyv2_latestHeader reply: {latest_header['error']}",
allow_module_level=True,
)
except Exception as e:
pytest.skip('Failed to get hmyv2_latestHeader reply', allow_module_level=True)
pytest.skip("Failed to get hmyv2_latestHeader reply", allow_module_level=True)
if metadata and latest_header:
staking_epoch = metadata['result']['chain-config']['staking-epoch']
current_epoch = latest_header['result']['epoch']
staking_epoch = metadata["result"]["chain-config"]["staking-epoch"]
current_epoch = latest_header["result"]["epoch"]
if staking_epoch > current_epoch:
pytest.skip(f'Not staking epoch: current {current_epoch}, staking {staking_epoch}', allow_module_level=True)
pytest.skip(
f"Not staking epoch: current {current_epoch}, staking {staking_epoch}",
allow_module_level=True,
)
def _send_transaction(raw_tx, endpoint):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": 'hmyv2_sendRawTransaction',
"params": [raw_tx]
"method": "hmyv2_sendRawTransaction",
"params": [raw_tx],
}
response = requests.request('POST', endpoint, headers=headers,
data=json.dumps(payload), timeout=timeout, allow_redirects=True)
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
tx = json.loads(response.content)
if 'error' in tx:
pytest.skip(f"Error in hmyv2_sendRawTransaction reply: {tx['error']}", allow_module_level=True)
if "error" in tx:
pytest.skip(
f"Error in hmyv2_sendRawTransaction reply: {tx['error']}",
allow_module_level=True,
)
except Exception as e:
pytest.skip('Failed to get hmyv2_sendRawTransaction reply', allow_module_level=True)
pytest.skip(
"Failed to get hmyv2_sendRawTransaction reply", allow_module_level=True
)
def _check_transaction(tx_hash, endpoint):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": 'hmyv2_getTransactionByHash',
"params": [tx_hash]
"method": "hmyv2_getTransactionByHash",
"params": [tx_hash],
}
response = requests.request('POST', endpoint, headers=headers,
data=json.dumps(payload), timeout=timeout, allow_redirects=True)
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
tx_data = json.loads(response.content)
return tx_data
except Exception as e:
pytest.skip('Failed to get hmyv2_getTransactionByHash reply', allow_module_level=True)
pytest.skip(
"Failed to get hmyv2_getTransactionByHash reply", allow_module_level=True
)
def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout=30):
start_time = time.time()
while((time.time() - start_time) <= timeout):
while (time.time() - start_time) <= timeout:
tx_data = _check_transaction(tx_hash, endpoint)
if tx_data is not None:
block_hash = tx_data["result"].get("blockHash", "0x00")
@ -146,41 +202,65 @@ def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout = 30):
time.sleep(random.uniform(0.2, 0.5))
return False
def _send_staking_transaction(raw_tx, endpoint=endpoint):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": 'hmyv2_sendRawStakingTransaction',
"params": [raw_tx]
"method": "hmyv2_sendRawStakingTransaction",
"params": [raw_tx],
}
response = requests.request('POST', endpoint, headers=headers,
data=json.dumps(payload), timeout=timeout, allow_redirects=True)
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
staking_tx = json.loads(response.content)
if 'error' in staking_tx:
pytest.skip(f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}", allow_module_level=True)
if "error" in staking_tx:
pytest.skip(
f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}",
allow_module_level=True,
)
except Exception as e:
pytest.skip('Failed to get hmyv2_sendRawStakingTransaction reply', allow_module_level=True)
pytest.skip(
"Failed to get hmyv2_sendRawStakingTransaction reply",
allow_module_level=True,
)
def _check_staking_transaction(stx_hash, endpoint=endpoint):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": 'hmyv2_getStakingTransactionByHash',
"params": [stx_hash]
"method": "hmyv2_getStakingTransactionByHash",
"params": [stx_hash],
}
response = requests.request('POST', endpoint, headers=headers,
data=json.dumps(payload), timeout=timeout, allow_redirects=True)
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
)
stx_data = json.loads(response.content)
return stx_data
except Exception as e:
pytest.skip('Failed to get hmyv2_getStakingTransactionByHash reply', allow_module_level=True)
pytest.skip(
"Failed to get hmyv2_getStakingTransactionByHash reply",
allow_module_level=True,
)
def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout=30):
answer = False
start_time = time.time()
while((time.time() - start_time) <= timeout):
while (time.time() - start_time) <= timeout:
tx_data = _check_staking_transaction(tx_hash, endpoint)
if tx_data is not None:
block_hash = tx_data["result"].get("blockHash", "0x00")

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

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

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

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

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

@ -1,11 +1,6 @@
from pyhmy import (
staking_signing,
staking_structures
)
from pyhmy import staking_signing, staking_structures
from pyhmy.numbers import (
convert_one_to_atto
)
from pyhmy.numbers import convert_one_to_atto
# other transactions (create/edit validator) are in test_validator.py
# test_delegate is the same as test_undelegate (except the directive) so it has been omitted
@ -56,17 +51,26 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd
console.log( 'Signed transaction' )
console.log(signed)
"""
def test_collect_rewards_chain_id():
transaction_dict = {
'directive': staking_structures.Directive.CollectRewards,
'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9',
'nonce': 2,
'gasPrice': int(convert_one_to_atto(1)),
'gasLimit': 100,
'chainId': 1, # with chainId for coverage
"directive": staking_structures.Directive.CollectRewards,
"delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9",
"nonce": 2,
"gasPrice": int(convert_one_to_atto(1)),
"gasLimit": 100,
"chainId": 1, # with chainId for coverage
}
signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48')
assert signed_tx.rawTransaction.hex() == '0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099'
signed_tx = staking_signing.sign_staking_transaction(
transaction_dict,
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099"
)
"""
let stakingTx
@ -87,16 +91,24 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd
console.log( 'Signed transaction' )
console.log(signed)
"""
def test_delegate():
transaction_dict = {
'directive': staking_structures.Directive.Delegate,
'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9',
'validatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9',
'amount': 5,
'nonce': 2,
'gasPrice': int(convert_one_to_atto(1)),
'gasLimit': 100,
'chainId': 2,
"directive": staking_structures.Directive.Delegate,
"delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9",
"validatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9",
"amount": 5,
"nonce": 2,
"gasPrice": int(convert_one_to_atto(1)),
"gasLimit": 100,
"chainId": 2,
}
signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48')
assert signed_tx.rawTransaction.hex() == '0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6'
signed_tx = staking_signing.sign_staking_transaction(
transaction_dict,
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6"
)

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

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

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

Loading…
Cancel
Save