chore(yapf): run yapf

pull/35/head
MaxMustermann2 2 years ago
parent 98514c7a2c
commit ca6a6ae1a8
No known key found for this signature in database
GPG Key ID: 4F4AB9DB6FF24C94
  1. 404
      .style.yapf
  2. 14
      pyhmy/__init__.py
  3. 4
      pyhmy/_version.py
  4. 185
      pyhmy/account.py
  5. 116
      pyhmy/bech32/bech32.py
  6. 541
      pyhmy/blockchain.py
  7. 240
      pyhmy/cli.py
  8. 2
      pyhmy/constants.py
  9. 72
      pyhmy/contract.py
  10. 23
      pyhmy/exceptions.py
  11. 98
      pyhmy/logging.py
  12. 19
      pyhmy/numbers.py
  13. 24
      pyhmy/rpc/exceptions.py
  14. 45
      pyhmy/rpc/request.py
  15. 132
      pyhmy/signing.py
  16. 323
      pyhmy/staking.py
  17. 323
      pyhmy/staking_signing.py
  18. 103
      pyhmy/staking_structures.py
  19. 290
      pyhmy/transaction.py
  20. 136
      pyhmy/util.py
  21. 388
      pyhmy/validator.py
  22. 4
      tests/bech32-pyhmy/test_bech32.py
  23. 57
      tests/cli-pyhmy/test_cli.py
  24. 20
      tests/logging-pyhmy/test_logging.py
  25. 32
      tests/numbers-pyhmy/test_numbers.py
  26. 75
      tests/request-pyhmy/test_request.py
  27. 180
      tests/sdk-pyhmy/conftest.py
  28. 145
      tests/sdk-pyhmy/test_account.py
  29. 457
      tests/sdk-pyhmy/test_blockchain.py
  30. 84
      tests/sdk-pyhmy/test_contract.py
  31. 9
      tests/sdk-pyhmy/test_signing.py
  32. 272
      tests/sdk-pyhmy/test_staking.py
  33. 12
      tests/sdk-pyhmy/test_staking_signing.py
  34. 235
      tests/sdk-pyhmy/test_transaction.py
  35. 132
      tests/sdk-pyhmy/test_validator.py
  36. 43
      tests/util-pyhmy/test_util.py

@ -0,0 +1,404 @@
[style]
# Align closing bracket with visual indentation.
align_closing_bracket_with_visual_indent=True
# Allow dictionary keys to exist on multiple lines. For example:
#
# x = {
# ('this is the first element of a tuple',
# 'this is the second element of a tuple'):
# value,
# }
allow_multiline_dictionary_keys=False
# Allow lambdas to be formatted on more than one line.
allow_multiline_lambdas=False
# Allow splitting before a default / named assignment in an argument list.
allow_split_before_default_or_named_assigns=True
# Allow splits before the dictionary value.
allow_split_before_dict_value=False
# Let spacing indicate operator precedence. For example:
#
# a = 1 * 2 + 3 / 4
# b = 1 / 2 - 3 * 4
# c = (1 + 2) * (3 - 4)
# d = (1 - 2) / (3 + 4)
# e = 1 * 2 - 3
# f = 1 + 2 + 3 + 4
#
# will be formatted as follows to indicate precedence:
#
# a = 1*2 + 3/4
# b = 1/2 - 3*4
# c = (1+2) * (3-4)
# d = (1-2) / (3+4)
# e = 1*2 - 3
# f = 1 + 2 + 3 + 4
#
arithmetic_precedence_indication=False
# Number of blank lines surrounding top-level function and class
# definitions.
blank_lines_around_top_level_definition=2
# Number of blank lines between top-level imports and variable
# definitions.
blank_lines_between_top_level_imports_and_variables=1
# Insert a blank line before a class-level docstring.
blank_line_before_class_docstring=False
# Insert a blank line before a module docstring.
blank_line_before_module_docstring=False
# Insert a blank line before a 'def' or 'class' immediately nested
# within another 'def' or 'class'. For example:
#
# class Foo:
# # <------ this blank line
# def method():
# ...
blank_line_before_nested_class_or_def=False
# Do not split consecutive brackets. Only relevant when
# dedent_closing_brackets is set. For example:
#
# call_func_that_takes_a_dict(
# {
# 'key1': 'value1',
# 'key2': 'value2',
# }
# )
#
# would reformat to:
#
# call_func_that_takes_a_dict({
# 'key1': 'value1',
# 'key2': 'value2',
# })
coalesce_brackets=False
# The column limit.
column_limit=80
# The style for continuation alignment. Possible values are:
#
# - SPACE: Use spaces for continuation alignment. This is default behavior.
# - FIXED: Use fixed number (CONTINUATION_INDENT_WIDTH) of columns
# (ie: CONTINUATION_INDENT_WIDTH/INDENT_WIDTH tabs or
# CONTINUATION_INDENT_WIDTH spaces) for continuation alignment.
# - VALIGN-RIGHT: Vertically align continuation lines to multiple of
# INDENT_WIDTH columns. Slightly right (one tab or a few spaces) if
# cannot vertically align continuation lines with indent characters.
continuation_align_style=SPACE
# Indent width used for line continuations.
continuation_indent_width=4
# Put closing brackets on a separate line, dedented, if the bracketed
# expression can't fit in a single line. Applies to all kinds of brackets,
# including function definitions and calls. For example:
#
# config = {
# 'key1': 'value1',
# 'key2': 'value2',
# } # <--- this bracket is dedented and on a separate line
#
# time_series = self.remote_client.query_entity_counters(
# entity='dev3246.region1',
# key='dns.query_latency_tcp',
# transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
# start_ts=now()-timedelta(days=3),
# end_ts=now(),
# ) # <--- this bracket is dedented and on a separate line
dedent_closing_brackets=True
# Disable the heuristic which places each list element on a separate line
# if the list is comma-terminated.
disable_ending_comma_heuristic=True
# Place each dictionary entry onto its own line.
each_dict_entry_on_separate_line=True
# Require multiline dictionary even if it would normally fit on one line.
# For example:
#
# config = {
# 'key1': 'value1'
# }
force_multiline_dict=True
# The regex for an i18n comment. The presence of this comment stops
# reformatting of that line, because the comments are required to be
# next to the string they translate.
i18n_comment=#\..*
# The i18n function call names. The presence of this function stops
# reformattting on that line, because the string it has cannot be moved
# away from the i18n comment.
i18n_function_call=N_, _
# Indent blank lines.
indent_blank_lines=False
# Put closing brackets on a separate line, indented, if the bracketed
# expression can't fit in a single line. Applies to all kinds of brackets,
# including function definitions and calls. For example:
#
# config = {
# 'key1': 'value1',
# 'key2': 'value2',
# } # <--- this bracket is indented and on a separate line
#
# time_series = self.remote_client.query_entity_counters(
# entity='dev3246.region1',
# key='dns.query_latency_tcp',
# transform=Transformation.AVERAGE(window=timedelta(seconds=60)),
# start_ts=now()-timedelta(days=3),
# end_ts=now(),
# ) # <--- this bracket is indented and on a separate line
indent_closing_brackets=False
# Indent the dictionary value if it cannot fit on the same line as the
# dictionary key. For example:
#
# config = {
# 'key1':
# 'value1',
# 'key2': value1 +
# value2,
# }
indent_dictionary_value=False
# The number of columns to use for indentation.
indent_width=4
# Join short lines into one line. E.g., single line 'if' statements.
join_multiple_lines=False
# Do not include spaces around selected binary operators. For example:
#
# 1 + 2 * 3 - 4 / 5
#
# will be formatted as follows when configured with "*,/":
#
# 1 + 2*3 - 4/5
no_spaces_around_selected_binary_operators=
# Use spaces around default or named assigns.
spaces_around_default_or_named_assign=True
# Adds a space after the opening '{' and before the ending '}' dict
# delimiters.
#
# {1: 2}
#
# will be formatted as:
#
# { 1: 2 }
spaces_around_dict_delimiters=True
# Adds a space after the opening '[' and before the ending ']' list
# delimiters.
#
# [1, 2]
#
# will be formatted as:
#
# [ 1, 2 ]
spaces_around_list_delimiters=True
# Use spaces around the power operator.
spaces_around_power_operator=False
# Use spaces around the subscript / slice operator. For example:
#
# my_list[1 : 10 : 2]
spaces_around_subscript_colon=True
# Adds a space after the opening '(' and before the ending ')' tuple
# delimiters.
#
# (1, 2, 3)
#
# will be formatted as:
#
# ( 1, 2, 3 )
spaces_around_tuple_delimiters=True
# The number of spaces required before a trailing comment.
# This can be a single value (representing the number of spaces
# before each trailing comment) or list of values (representing
# alignment column values; trailing comments within a block will
# be aligned to the first column value that is greater than the maximum
# line length within the block). For example:
#
# With spaces_before_comment=5:
#
# 1 + 1 # Adding values
#
# will be formatted as:
#
# 1 + 1 # Adding values <-- 5 spaces between the end of the
# # statement and comment
#
# With spaces_before_comment=15, 20:
#
# 1 + 1 # Adding values
# two + two # More adding
#
# longer_statement # This is a longer statement
# short # This is a shorter statement
#
# a_very_long_statement_that_extends_beyond_the_final_column # Comment
# short # This is a shorter statement
#
# will be formatted as:
#
# 1 + 1 # Adding values <-- end of line comments in block
# # aligned to col 15
# two + two # More adding
#
# longer_statement # This is a longer statement <-- end of line
# # comments in block aligned to col 20
# short # This is a shorter statement
#
# a_very_long_statement_that_extends_beyond_the_final_column # Comment <-- the end of line comments are aligned based on the line length
# short # This is a shorter statement
#
spaces_before_comment=2
# Insert a space between the ending comma and closing bracket of a list,
# etc.
space_between_ending_comma_and_closing_bracket=True
# Use spaces inside brackets, braces, and parentheses. For example:
#
# method_call( 1 )
# my_dict[ 3 ][ 1 ][ get_index( *args, **kwargs ) ]
# my_set = { 1, 2, 3 }
space_inside_brackets=True
# Split before arguments
split_all_comma_separated_values=True
# Split before arguments, but do not split all subexpressions recursively
# (unless needed).
split_all_top_level_comma_separated_values=False
# Split before arguments if the argument list is terminated by a
# comma.
split_arguments_when_comma_terminated=True
# Set to True to prefer splitting before '+', '-', '*', '/', '//', or '@'
# rather than after.
split_before_arithmetic_operator=False
# Set to True to prefer splitting before '&', '|' or '^' rather than
# after.
split_before_bitwise_operator=False
# Split before the closing bracket if a list or dict literal doesn't fit on
# a single line.
split_before_closing_bracket=True
# Split before a dictionary or set generator (comp_for). For example, note
# the split before the 'for':
#
# foo = {
# variable: 'Hello world, have a nice day!'
# for variable in bar if variable != 42
# }
split_before_dict_set_generator=True
# Split before the '.' if we need to split a longer expression:
#
# foo = ('This is a really long string: {}, {}, {}, {}'.format(a, b, c, d))
#
# would reformat to something like:
#
# foo = ('This is a really long string: {}, {}, {}, {}'
# .format(a, b, c, d))
split_before_dot=True
# Split after the opening paren which surrounds an expression if it doesn't
# fit on a single line.
split_before_expression_after_opening_paren=False
# If an argument / parameter list is going to be split, then split before
# the first argument.
split_before_first_argument=False
# Set to True to prefer splitting before 'and' or 'or' rather than
# after.
split_before_logical_operator=False
# Split named assignments onto individual lines.
split_before_named_assigns=True
# Set to True to split list comprehensions and generators that have
# non-trivial expressions and multiple clauses before each of these
# clauses. For example:
#
# result = [
# a_long_var + 100 for a_long_var in xrange(1000)
# if a_long_var % 10]
#
# would reformat to something like:
#
# result = [
# a_long_var + 100
# for a_long_var in xrange(1000)
# if a_long_var % 10]
split_complex_comprehension=True
# The penalty for splitting right after the opening bracket.
split_penalty_after_opening_bracket=300
# The penalty for splitting the line after a unary operator.
split_penalty_after_unary_operator=10000
# The penalty of splitting the line around the '+', '-', '*', '/', '//',
# ``%``, and '@' operators.
split_penalty_arithmetic_operator=300
# The penalty for splitting right before an if expression.
split_penalty_before_if_expr=0
# The penalty of splitting the line around the '&', '|', and '^'
# operators.
split_penalty_bitwise_operator=300
# The penalty for splitting a list comprehension or generator
# expression.
split_penalty_comprehension=2100
# The penalty for characters over the column limit.
split_penalty_excess_character=7000
# The penalty incurred by adding a line split to the logical line. The
# more line splits added the higher the penalty.
split_penalty_for_added_line_split=30
# The penalty of splitting a list of "import as" names. For example:
#
# from a_very_long_or_indented_module_name_yada_yad import (long_argument_1,
# long_argument_2,
# long_argument_3)
#
# would reformat to something like:
#
# from a_very_long_or_indented_module_name_yada_yad import (
# long_argument_1, long_argument_2, long_argument_3)
split_penalty_import_names=0
# The penalty of splitting the line around the 'and' and 'or'
# operators.
split_penalty_logical_operator=300
# Use the Tab character for indentation.
use_tabs=False

@ -7,13 +7,17 @@ import warnings
from ._version import __version__
if sys.version_info.major < 3:
warnings.simplefilter("always", DeprecationWarning)
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"):
warnings.simplefilter("always", ImportWarning)
warnings.warn(ImportWarning("`pyhmy` does not work on Windows or 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.resetwarnings()

@ -5,5 +5,5 @@
from incremental import Version
__version__ = Version("pyhmy", 20, 5, 20)
__all__ = ["__version__"]
__version__ = Version( "pyhmy", 20, 5, 20 )
__all__ = [ "__version__" ]

@ -13,7 +13,8 @@ from .bech32.bech32 import bech32_decode
from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT
def is_valid_address(address) -> bool:
def is_valid_address( address ) -> bool:
"""
Check if given string is valid one address
NOTE: This function is NOT thread safe due to the C function used by the bech32 library.
@ -28,15 +29,19 @@ 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)
hrp, _ = bech32_decode( address )
if not hrp:
return False
return True
def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_balance(
address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get current account balance.
Parameters
@ -63,18 +68,24 @@ def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
https://api.hmny.io/#da8901d2-d237-4c3b-9d7d-10af9def05c4
"""
method = "hmyv2_getBalance"
params = [address]
params = [ address ]
try:
balance = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(balance) # v2 returns the result as it is
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( balance ) # v2 returns the result as it is
except TypeError as exception: # check will work if rpc returns None
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_balance_by_block(
address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get account balance for address at a given block number.
@ -105,18 +116,24 @@ def get_balance_by_block(
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92
"""
method = "hmyv2_getBalanceByBlockNumber"
params = [address, block_num]
params = [ address, block_num ]
try:
balance = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(balance)
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( balance )
except TypeError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_account_nonce(
address, block_num="latest", endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
block_num = "latest",
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get the account nonce.
@ -146,25 +163,34 @@ def get_account_nonce(
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L51
"""
method = "hmyv2_getAccountNonce"
params = [address, block_num]
params = [ address, block_num ]
try:
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
return int(nonce)
nonce = rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( nonce )
except TypeError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_nonce(
address, block_num="latest", endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
block_num = "latest",
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""See get_account_nonce."""
return get_account_nonce(address, block_num, endpoint, timeout)
return get_account_nonce( address, block_num, endpoint, timeout )
def get_transaction_count(
address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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
@ -197,18 +223,24 @@ def get_transaction_count(
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L69
"""
method = "hmyv2_getTransactionCount"
params = [address, block_num]
params = [ address, block_num ]
try:
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
return int(nonce)
nonce = rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( nonce )
except TypeError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transactions_count(
address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
tx_type,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get the number of regular transactions from genesis of input type.
@ -240,18 +272,24 @@ def get_transactions_count(
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114
"""
method = "hmyv2_getTransactionsCount"
params = [address, tx_type]
params = [ address, tx_type ]
try:
tx_count = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(tx_count)
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( tx_count )
except TypeError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_transactions_count(
address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
tx_type,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get the number of staking transactions from genesis of input type
("SENT", "RECEIVED", "ALL")
@ -284,14 +322,17 @@ def get_staking_transactions_count(
https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134
"""
method = "hmyv2_getStakingTransactionsCount"
params = [address, tx_type]
params = [ address, tx_type ]
try:
tx_count = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return int(tx_count)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( tx_count )
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_history( # pylint: disable=too-many-arguments
@ -359,11 +400,14 @@ def get_transaction_history( # pylint: disable=too-many-arguments
method = "hmyv2_getTransactionsHistory"
try:
tx_history = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
method,
params = params,
endpoint = endpoint,
timeout = timeout
)
return tx_history["result"]["transactions"]
return tx_history[ "result" ][ "transactions" ]
except KeyError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_transaction_history( # pylint: disable=too-many-arguments
@ -445,15 +489,21 @@ def get_staking_transaction_history( # pylint: disable=too-many-arguments
method = "hmyv2_getStakingTransactionsHistory"
try:
stx_history = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout
)["result"]
return stx_history["staking_transactions"]
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return stx_history[ "staking_transactions" ]
except KeyError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_balance_on_all_shards(
address, skip_error=True, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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.
@ -483,25 +533,37 @@ def get_balance_on_all_shards(
]
"""
balances = []
sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout)
sharding_structure = get_sharding_structure(
endpoint = endpoint,
timeout = timeout
)
for shard in sharding_structure:
try:
balances.append(
{
"shard": shard["shardID"],
"shard": shard[ "shardID" ],
"balance": get_balance(
address, endpoint=shard["http"], timeout=timeout
address,
endpoint = shard[ "http" ],
timeout = timeout
),
}
)
except (KeyError, RPCError, RequestsError, RequestsTimeoutError):
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
address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get total account balance on all shards.
@ -530,8 +592,11 @@ def get_total_balance(
"""
try:
balances = get_balance_on_all_shards(
address, skip_error=False, endpoint=endpoint, timeout=timeout
address,
skip_error = False,
endpoint = endpoint,
timeout = timeout
)
return sum(b["balance"] for b in balances)
return sum( b[ "balance" ] for b in balances )
except TypeError as exception:
raise RuntimeError from exception

@ -17,108 +17,106 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""Reference implementation for Bech32 and segwit addresses."""
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
def bech32_polymod(values):
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
for i in range(5):
chk ^= generator[i] if ((top >> i) & 1) else 0
chk = ( chk & 0x1FFFFFF ) << 5 ^ value
for i in range( 5 ):
chk ^= generator[ i ] if ( ( top >> i ) & 1 ) else 0
return chk
def bech32_hrp_expand(hrp):
def bech32_hrp_expand( hrp ):
"""Expand the HRP into values for checksum computation."""
return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp]
return [ ord( x ) >> 5 for x in hrp ] + [ 0
] + [ ord( x ) & 31 for x in hrp ]
def bech32_verify_checksum(hrp, data):
def bech32_verify_checksum( hrp, data ):
"""Verify a checksum given HRP and converted data characters."""
return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
return bech32_polymod( bech32_hrp_expand( hrp ) + data ) == 1
def bech32_create_checksum(hrp, data):
def bech32_create_checksum( hrp, data ):
"""Compute the checksum values given HRP and data."""
values = bech32_hrp_expand(hrp) + data
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
values = bech32_hrp_expand( hrp ) + data
polymod = bech32_polymod( values + [ 0, 0, 0, 0, 0, 0 ] ) ^ 1
return [ ( polymod >> 5 * ( 5 - i ) ) & 31 for i in range( 6 ) ]
def bech32_encode(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])
combined = data + bech32_create_checksum( hrp, data )
return hrp + "1" + "".join( [ CHARSET[ d ] for d in combined ] )
def bech32_decode(bech):
def bech32_decode( bech ):
"""Validate a Bech32 string, and determine HRP and data."""
if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or (
bech.lower() != bech and bech.upper() != bech
):
return (None, None)
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")
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 :]):
return (None, None)
hrp = bech[:pos]
data = [CHARSET.find(x) for x in bech[pos + 1 :]]
if not bech32_verify_checksum(hrp, data):
return (None, None)
return (hrp, data[:-6])
def convertbits(data, frombits, tobits, pad=True):
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 : ] ):
return ( None, None )
hrp = bech[ : pos ]
data = [ CHARSET.find( x ) for x in bech[ pos + 1 : ] ]
if not bech32_verify_checksum( hrp, data ):
return ( None, None )
return ( hrp, data[ :-6 ] )
def convertbits( data, frombits, tobits, pad = True ):
"""General power-of-2 base conversion."""
acc = 0
bits = 0
ret = []
maxv = (1 << tobits) - 1
max_acc = (1 << (frombits + tobits - 1)) - 1
maxv = ( 1 << tobits ) - 1
max_acc = ( 1 << ( frombits + tobits - 1 ) ) - 1
for value in data:
if value < 0 or (value >> frombits):
if value < 0 or ( value >> frombits ):
return None
acc = ((acc << frombits) | value) & max_acc
acc = ( ( acc << frombits ) | value ) & max_acc
bits += frombits
while bits >= tobits:
bits -= tobits
ret.append((acc >> bits) & maxv)
ret.append( ( acc >> bits ) & maxv )
if pad:
if bits:
ret.append((acc << (tobits - bits)) & maxv)
elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
ret.append( ( acc << ( tobits - bits ) ) & maxv )
elif bits >= frombits or ( ( acc << ( tobits - bits ) ) & maxv ):
return None
return ret
def decode(hrp, addr):
def decode( hrp, addr ):
"""Decode a segwit address."""
hrpgot, data = bech32_decode(addr)
hrpgot, data = bech32_decode( addr )
if hrpgot != hrp:
return (None, None)
decoded = convertbits(data[1:], 5, 8, False)
if decoded is None or len(decoded) < 2 or len(decoded) > 40:
return (None, None)
if data[0] > 16:
return (None, None)
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
return (None, None)
return (data[0], decoded)
def encode(hrp, witver, witprog):
return ( None, None )
decoded = convertbits( data[ 1 : ], 5, 8, False )
if decoded is None or len( decoded ) < 2 or len( decoded ) > 40:
return ( None, None )
if data[ 0 ] > 16:
return ( None, None )
if data[ 0 ] == 0 and len( decoded ) != 20 and len( decoded ) != 32:
return ( None, None )
return ( data[ 0 ], decoded )
def encode( hrp, witver, witprog ):
"""Encode a segwit address."""
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
if decode(hrp, ret) == (None, None):
ret = bech32_encode( hrp, [ witver ] + convertbits( witprog, 8, 5 ) )
if decode( hrp, ret ) == ( None, None ):
return None
return ret

@ -9,10 +9,14 @@ from .exceptions import InvalidRPCReplyError
from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT
#############################
# Node / network level RPCs #
#############################
def get_bad_blocks(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list:
def get_bad_blocks(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""[WIP] Get list of bad blocks in memory of specific node Known issues
with RPC not returning correctly.
@ -38,12 +42,14 @@ def get_bad_blocks(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list:
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def chain_id(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
def chain_id( endpoint = DEFAULT_ENDPOINT, timeout = DEFAULT_TIMEOUT ) -> dict:
"""Chain id of the chain.
Parameters
@ -68,13 +74,16 @@ def chain_id(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
"""
method = "hmyv2_chainId"
try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)
return data["result"]
data = rpc_request( method, endpoint = endpoint, timeout = timeout )
return data[ "result" ]
except KeyError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_node_metadata(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
def get_node_metadata(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get config for the node.
Parameters
@ -127,13 +136,16 @@ def get_node_metadata(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dic
"""
method = "hmyv2_getNodeMetadata"
try:
metadata = rpc_request(method, endpoint=endpoint, timeout=timeout)
return metadata["result"]
metadata = rpc_request( method, endpoint = endpoint, timeout = timeout )
return metadata[ "result" ]
except KeyError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_peer_info(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
def get_peer_info(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get peer info for the node.
Parameters
@ -165,12 +177,17 @@ def get_peer_info(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def protocol_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def protocol_version(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get the current Harmony protocol version this node supports.
Parameters
@ -196,13 +213,16 @@ def protocol_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
"""
method = "hmyv2_protocolVersion"
try:
value = rpc_request(method, endpoint=endpoint, timeout=timeout)
return value["result"]
value = rpc_request( method, endpoint = endpoint, timeout = timeout )
return value[ "result" ]
except KeyError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_num_peers(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_num_peers(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get number of peers connected to the node.
Parameters
@ -229,13 +249,19 @@ def get_num_peers(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
method = "net_peerCount"
try: # Number of peers represented as a hex string
return int(
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ],
16
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_version(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get version of the EVM network (https://chainid.network/)
Parameters
@ -262,13 +288,16 @@ def get_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
method = "net_version"
try:
return int(
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ],
16
) # this is hexadecimal
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool:
def in_sync( endpoint = DEFAULT_ENDPOINT, timeout = DEFAULT_TIMEOUT ) -> bool:
"""Whether the shard chain is in sync or syncing (not out of sync)
Parameters
@ -293,12 +322,19 @@ def in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool:
"""
method = "hmyv2_inSync"
try:
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
return bool(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def beacon_in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool:
def beacon_in_sync(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool:
"""Whether the beacon chain is in sync or syncing (not out of sync)
Parameters
@ -323,12 +359,19 @@ def beacon_in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool:
"""
method = "hmyv2_beaconInSync"
try:
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
return bool(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_staking_epoch(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get epoch number when blockchain switches to EPoS election.
Parameters
@ -358,13 +401,18 @@ def get_staking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int
"""
method = "hmyv2_getNodeMetadata"
try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
return int(data["chain-config"]["staking-epoch"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
data = rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
return int( data[ "chain-config" ][ "staking-epoch" ] )
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_prestaking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_prestaking_epoch(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get epoch number when blockchain switches to allow staking features
without election.
@ -395,16 +443,18 @@ def get_prestaking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
"""
method = "hmyv2_getNodeMetadata"
try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
return int(data["chain-config"]["prestaking-epoch"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
data = rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
return int( data[ "chain-config" ][ "prestaking-epoch" ] )
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
########################
# Sharding information #
########################
def get_shard(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_shard( endpoint = DEFAULT_ENDPOINT, timeout = DEFAULT_TIMEOUT ) -> int:
"""Get shard ID of the node.
Parameters
@ -430,15 +480,16 @@ def get_shard(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_sharding_structure(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get network sharding structure.
@ -468,15 +519,20 @@ def get_sharding_structure(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
#############################
# Current status of network #
#############################
def get_leader_address(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> str:
def get_leader_address(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str:
"""Get current leader one address.
Parameters
@ -502,13 +558,17 @@ def get_leader_address(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> st
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def is_last_block(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool:
"""If the block at block_num is the last block.
@ -534,22 +594,25 @@ def is_last_block(
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L286
"""
params = [
block_num,
]
params = [ block_num, ]
method = "hmyv2_isLastBlock"
try:
return bool(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def epoch_last_block(
epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Returns the number of the last block in the epoch.
@ -575,21 +638,25 @@ def epoch_last_block(
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L294
"""
params = [
epoch,
]
params = [ epoch, ]
method = "hmyv2_epochLastBlock"
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_circulating_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_circulating_supply(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get current circulation supply of tokens in ONE.
Parameters
@ -615,12 +682,17 @@ def get_circulating_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_total_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_total_supply(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get total number of pre-mined tokens.
Parameters
@ -646,12 +718,17 @@ def get_total_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
"""
method = "hmyv2_getTotalSupply"
try:
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_number(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_block_number(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get current block number.
Parameters
@ -677,12 +754,19 @@ def get_block_number(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
"""
method = "hmyv2_blockNumber"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_current_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_current_epoch(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get current epoch number.
Parameters
@ -708,12 +792,19 @@ def get_current_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int
"""
method = "hmyv2_getEpoch"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_last_cross_links(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list:
def get_last_cross_links(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get last cross shard links.
Parameters
@ -746,12 +837,17 @@ def get_last_cross_links(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_gas_price(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_gas_price(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get network gas price.
Parameters
@ -777,15 +873,22 @@ def get_gas_price(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
"""
method = "hmyv2_gasPrice"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
##############
# Block RPCs #
##############
def get_latest_header(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
def get_latest_header(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get block header of latest block.
Parameters
@ -829,13 +932,17 @@ def get_latest_header(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dic
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_header_by_number(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get block header of block at block_num.
@ -862,17 +969,21 @@ def get_header_by_number(
https://api.hmny.io/#01148e4f-72bb-426d-a123-718a161eaec0
"""
method = "hmyv2_getHeaderByNumber"
params = [block_num]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_latest_chain_headers(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get block header of latest block for beacon chain & shard chain.
@ -919,9 +1030,11 @@ def get_latest_chain_headers(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_by_number( # pylint: disable=too-many-arguments
@ -1005,11 +1118,14 @@ def get_block_by_number( # pylint: disable=too-many-arguments
]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_by_hash( # pylint: disable=too-many-arguments
@ -1062,15 +1178,20 @@ def get_block_by_hash( # pylint: disable=too-many-arguments
]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_transaction_count_by_number(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get transaction count for specific block number.
@ -1099,20 +1220,25 @@ def get_block_transaction_count_by_number(
-------------
https://api.hmny.io/#26c5adfb-d757-4595-9eb7-c6efef63df32
"""
params = [block_num]
params = [ block_num ]
method = "hmyv2_getBlockTransactionCountByNumber"
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_transaction_count_by_hash(
block_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get transaction count for specific block hash.
@ -1141,20 +1267,25 @@ def get_block_transaction_count_by_hash(
-------------
https://api.hmny.io/#66c68844-0208-49bb-a83b-08722bc113eb
"""
params = [block_hash]
params = [ block_hash ]
method = "hmyv2_getBlockTransactionCountByHash"
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_staking_transaction_count_by_number(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get staking transaction count for specific block number.
@ -1183,20 +1314,25 @@ def get_block_staking_transaction_count_by_number(
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L494
"""
params = [block_num]
params = [ block_num ]
method = "hmyv2_getBlockStakingTransactionCountByNumber"
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_staking_transaction_count_by_hash(
block_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get staking transaction count for specific block hash.
@ -1225,16 +1361,19 @@ def get_block_staking_transaction_count_by_hash(
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L523
"""
params = [block_hash]
params = [ block_hash ]
method = "hmyv2_getBlockStakingTransactionCountByHash"
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_blocks( # pylint: disable=too-many-arguments
@ -1293,15 +1432,20 @@ def get_blocks( # pylint: disable=too-many-arguments
]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_signers(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of block signers for specific block number.
@ -1328,18 +1472,23 @@ def get_block_signers(
-------------
https://api.hmny.io/#1e4b5f41-9db6-4dea-92fb-4408db78e622
"""
params = [block_num]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_signers_keys(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of block signer public bls keys for specific block number.
@ -1366,18 +1515,24 @@ def get_block_signers_keys(
-------------
https://api.hmny.io/#9f9c8298-1a4e-4901-beac-f34b59ed02f1
"""
params = [block_num]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def is_block_signer(
block_num, address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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.
@ -1406,18 +1561,23 @@ def is_block_signer(
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L368
"""
params = [block_num, address]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_signed_blocks(
address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool:
"""The number of blocks a particular validator signed for last blocksPeriod
(1 epoch)
@ -1444,19 +1604,26 @@ def get_signed_blocks(
-------------
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L406
"""
params = [address]
params = [ address ]
method = "hmyv2_getSignedBlocks"
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
def get_validators(
epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get list of validators for specific epoch number.
Parameters
@ -1485,18 +1652,23 @@ def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
-------------
https://api.hmny.io/#4dfe91ad-71fa-4c7d-83f3-d1c86a804da5
"""
params = [epoch]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_keys(
epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of validator public bls keys for specific epoch number.
@ -1523,11 +1695,14 @@ def get_validator_keys(
-------------
https://api.hmny.io/#1439b580-fa3c-4d44-a79d-303390997a8c
"""
params = [epoch]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception

@ -53,7 +53,7 @@ import requests
from .util import get_bls_build_variables, get_gopath
if sys.platform.startswith("linux"):
if sys.platform.startswith( "linux" ):
_libs = {
"libbls384_256.so",
"libcrypto.so.10",
@ -77,7 +77,6 @@ ARG_PREFIX = "__PYHMY_ARG_PREFIX__"
environment = os.environ.copy() # The environment for the CLI to execute in.
# completely remove caching...
# we need to improve getting address better internally to REDUCE single calls....
# def _cache_and_lock_accounts_keystore(fn):
@ -102,7 +101,8 @@ environment = os.environ.copy() # The environment for the CLI to execute in.
# return wrap
def account_keystore_path(value=None):
def account_keystore_path( value = None ):
"""
Gets or sets the ACCOUNT_KEYSTORE_PATH
"""
@ -112,7 +112,8 @@ def account_keystore_path(value=None):
account_keystore_path.value = value
return account_keystore_path.value
def binary_path(value=None):
def binary_path( value = None ):
"""
Gets or sets the BINARY_PATH
"""
@ -122,6 +123,7 @@ def binary_path(value=None):
binary_path.value = value
return binary_path.value
def _get_current_accounts_keystore():
"""Internal function that gets the current keystore from the CLI.
@ -129,28 +131,32 @@ def _get_current_accounts_keystore():
values are their 'one1...' addresses.
"""
curr_addresses = {}
response = single_call("hmy keys list")
lines = response.split("\n")
if "NAME" not in lines[0] or "ADDRESS" not in lines[0]:
raise ValueError("Name or Address not found on first line of key list")
if lines[1] != "":
raise ValueError("Unknown format: No blank line between label and data")
for line in lines[2:]:
columns = line.split("\t")
if len(columns) != 2:
response = single_call( "hmy keys list" )
lines = response.split( "\n" )
if "NAME" not in lines[ 0 ] or "ADDRESS" not in lines[ 0 ]:
raise ValueError(
"Name or Address not found on first line of key list"
)
if lines[ 1 ] != "":
raise ValueError(
"Unknown format: No blank line between label and data"
)
for line in lines[ 2 : ]:
columns = line.split( "\t" )
if len( columns ) != 2:
break # Done iterating through all of the addresses.
name, address = columns
curr_addresses[name.strip()] = address
curr_addresses[ name.strip() ] = address
return curr_addresses
def _set_account_keystore_path():
"""Internal function to set the account keystore path according to the
binary."""
response = single_call("hmy keys location").strip()
if not os.path.exists(response):
os.mkdir(response)
account_keystore_path(response)
response = single_call( "hmy keys location" ).strip()
if not os.path.exists( response ):
os.mkdir( response )
account_keystore_path( response )
def _sync_accounts():
@ -159,19 +165,19 @@ def _sync_accounts():
new_keystore = _get_current_accounts_keystore()
for key, value in new_keystore.items():
if key not in _accounts:
_accounts[key] = value
acc_keys_to_remove = [k for k in _accounts if k not in new_keystore]
_accounts[ key ] = value
acc_keys_to_remove = [ k for k in _accounts if k not in new_keystore ]
for key in acc_keys_to_remove:
del _accounts[key]
del _accounts[ key ]
def _make_call_command(command):
def _make_call_command( command ):
"""Internal function that processes a command String or String Arg List for
underlying pexpect or subprocess call.
Note that single quote is not respected for strings.
"""
if isinstance(command, list):
if isinstance( command, list ):
command_toks = command
else:
all_strings = sorted(
@ -179,18 +185,23 @@ def _make_call_command(command):
key=lambda e: len(e), # pylint: disable=unnecessary-lambda
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]
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 element in command_toks_prefix:
if element.startswith(f'"{ARG_PREFIX}_') and element.endswith('"'):
index = int(element.replace(f'"{ARG_PREFIX}_', "").replace('"', ""))
command_toks.append(all_strings[index])
if element.startswith( f'"{ARG_PREFIX}_'
) and element.endswith( '"' ):
index = int(
element.replace( f'"{ARG_PREFIX}_',
"" ).replace( '"',
"" )
)
command_toks.append( all_strings[ index ] )
else:
command_toks.append(element)
if re.match(".*hmy", command_toks[0]):
command_toks = command_toks[1:]
command_toks.append( element )
if re.match( ".*hmy", command_toks[ 0 ] ):
command_toks = command_toks[ 1 : ]
return command_toks
@ -204,41 +215,46 @@ def get_accounts_keystore():
return _accounts
def is_valid_binary(path):
def is_valid_binary( path ):
"""
:param path: Path to the Harmony CLI binary (absolute or relative).
:return: If the file at the path is a CLI binary.
"""
path = os.path.realpath(path)
os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
path = os.path.realpath( path )
os.chmod( path, os.stat( path ).st_mode | stat.S_IEXEC )
try:
with subprocess.Popen(
[path, "version"],
env=environment,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
[ path,
"version" ],
env = environment,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
) as proc:
_, err = proc.communicate()
if not err:
return False
return "harmony" in err.decode().strip().lower()
except (OSError, subprocess.CalledProcessError, subprocess.SubprocessError):
except (
OSError,
subprocess.CalledProcessError,
subprocess.SubprocessError
):
return False
def set_binary(path):
def set_binary( path ):
"""
:param path: The path of the CLI binary to use.
:returns If the binary has been set.
Note that the exposed keystore will be updated accordingly.
"""
path = os.path.realpath(path)
assert os.path.exists(path)
os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC)
if not is_valid_binary(path):
path = os.path.realpath( path )
assert os.path.exists( path )
os.chmod( path, os.stat( path ).st_mode | stat.S_IEXEC )
if not is_valid_binary( path ):
return False
binary_path(path)
binary_path( path )
_set_account_keystore_path()
_sync_accounts()
return True
@ -248,7 +264,7 @@ def get_binary_path():
"""
:return: The absolute path of the CLI binary.
"""
return os.path.abspath(binary_path())
return os.path.abspath( binary_path() )
def get_version():
@ -256,10 +272,11 @@ def get_version():
:return: The version string of the CLI binary.
"""
with subprocess.Popen(
[binary_path(), "version"],
env=environment,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
[ binary_path(),
"version" ],
env = environment,
stdout = subprocess.PIPE,
stderr = subprocess.PIPE,
) as proc:
_, err = proc.communicate()
if not err:
@ -274,10 +291,10 @@ def get_account_keystore_path():
"""
:return: The absolute path to the account keystore of the CLI binary.
"""
return os.path.abspath(account_keystore_path())
return os.path.abspath( account_keystore_path() )
def check_address(address):
def check_address( address ):
"""
:param address: A 'one1...' address.
:return: Boolean of if the address is in the CLI's keystore.
@ -285,15 +302,15 @@ def check_address(address):
return address in get_accounts_keystore().values()
def get_address(name):
def get_address( name ):
"""
:param name: The alias of a key used in the CLI's keystore.
:return: The associated 'one1...' address.
"""
return get_accounts_keystore().get(name, None)
return get_accounts_keystore().get( name, None )
def get_accounts(address):
def get_accounts( address ):
"""
:param address: The 'one1...' address
:return: A list of account names associated with the param
@ -301,38 +318,42 @@ def get_accounts(address):
Note that a list of account names is needed because 1 address can
have multiple names within the CLI's keystore.
"""
return [acc for acc, addr in get_accounts_keystore().items() if address == addr]
return [
acc for acc,
addr in get_accounts_keystore().items() if address == addr
]
def remove_account(name):
def remove_account( name ):
"""Note that this edits the keystore directly since there is currently no
way to remove an address using the CLI.
:param name: The alias of a key used in the CLI's keystore.
:raises RuntimeError: If it failed to remove an account.
"""
if not get_address(name):
if not get_address( name ):
return
keystore_path = f"{get_account_keystore_path()}/{name}"
try:
shutil.rmtree(keystore_path)
except (shutil.Error, FileNotFoundError) as err:
shutil.rmtree( keystore_path )
except ( shutil.Error, FileNotFoundError ) as err:
raise RuntimeError(
f"Failed to delete dir: {keystore_path}\n" f"\tException: {err}"
f"Failed to delete dir: {keystore_path}\n"
f"\tException: {err}"
) from err
_sync_accounts()
def remove_address(address):
def remove_address( address ):
"""
:param address: The 'one1...' address to be removed.
"""
for name in get_accounts(address):
remove_account(name)
for name in get_accounts( address ):
remove_account( name )
_sync_accounts()
def single_call(command, timeout=60, error_ok=False):
def single_call( command, timeout = 60, error_ok = False ):
"""
:param command: String or String Arg List of command to execute on CLI.
:param timeout: Optional timeout in seconds
@ -340,40 +361,47 @@ def single_call(command, timeout=60, error_ok=False):
:returns: Decoded string of response from hmy CLI call
:raises: RuntimeError if bad command
"""
command_toks = [binary_path()] + _make_call_command(command)
command_toks = [ binary_path() ] + _make_call_command( command )
try:
return subprocess.check_output(
command_toks, env=environment, timeout=timeout
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}"
f"Bad CLI args: `{command}`\n "
f"\tException: {err}"
) from err
return err.output.decode()
def expect_call(command, timeout=60):
def expect_call( command, timeout = 60 ):
"""
:param command: String or String Arg List of command to execute on CLI.
:param timeout: Optional timeout in seconds
:returns: A pexpect child program
:raises: RuntimeError if bad command
"""
command_toks = _make_call_command(command)
command_toks = _make_call_command( command )
try:
proc = pexpect.spawn(
f"{binary_path()}", command_toks, env=environment, timeout=timeout
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}"
f"Bad CLI args: `{command}`\n "
f"\tException: {err}"
) from err
return proc
def download(path="./bin/hmy", replace=True, verbose=True):
def download( path = "./bin/hmy", replace = True, verbose = True ):
"""Download the CLI binary to the specified path. Related files will be
saved in the same directory.
@ -382,60 +410,74 @@ def download(path="./bin/hmy", replace=True, verbose=True):
:param verbose: A flag to enable a report message once the binary is downloaded.
:returns the environment to run the saved CLI binary.
"""
path = os.path.realpath(path)
parent_dir = Path(path).parent
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."
if not os.path.exists(path) or replace:
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", encoding='utf8') as script_file:
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", encoding = 'utf8' ) as script_file:
script_file.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)
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"
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.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:
with open(os.devnull, "w", encoding = "UTF-8") as devnull:
with open( os.devnull, "w", encoding = "UTF-8" ) as devnull:
subprocess.call(
[hmy_script_path, "-d"],
stdout=devnull,
stderr=subprocess.STDOUT,
[ hmy_script_path,
"-d" ],
stdout = devnull,
stderr = subprocess.STDOUT,
)
os.rename(os.path.join(parent_dir, "hmy"), path)
os.rename( os.path.join( parent_dir, "hmy" ), path )
if same_name_file:
os.rename(
os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy")
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)
print( f"Saved harmony binary to: `{path}`" )
os.chdir( old_cwd )
env = os.environ.copy()
if sys.platform.startswith("darwin"): # Dynamic linking for darwin
if sys.platform.startswith( "darwin" ): # Dynamic linking for darwin
try:
files_in_parent_dir = set(os.listdir(parent_dir))
if files_in_parent_dir.intersection(_libs) == _libs:
env["DYLD_FALLBACK_LIBRARY_PATH"] = parent_dir
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"):
env.update(get_bls_build_variables())
) 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}`"

@ -15,4 +15,4 @@ IDENTITY_CHAR_LIMIT = 140
WEBSITE_CHAR_LIMIT = 140
SECURITY_CONTACT_CHAR_LIMIT = 140
DETAILS_CHAR_LIMIT = 280
MIN_REQUIRED_DELEGATION = int(10000 * 1e18)
MIN_REQUIRED_DELEGATION = int( 10000 * 1e18 )

@ -76,11 +76,14 @@ def call( # pylint: disable=too-many-arguments
]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def estimate_gas( # pylint: disable=too-many-arguments
@ -141,17 +144,23 @@ def estimate_gas( # pylint: disable=too-many-arguments
method = "hmyv2_estimateGas"
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
],
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ],
16,
)
except KeyError as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_code(
address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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.
@ -182,18 +191,25 @@ def get_code(
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]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_storage_at(
address, key, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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.
@ -226,18 +242,23 @@ def get_storage_at(
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]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_contract_address_from_hash(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str:
"""Get address of the contract which was deployed in the transaction
represented by tx_hash.
@ -266,6 +287,11 @@ def get_contract_address_from_hash(
https://github.com/harmony-one/harmony-test/blob/master/localnet/rpc_tests/test_contract.py#L36
"""
try:
return get_transaction_receipt(tx_hash, endpoint, timeout)["contractAddress"]
return get_transaction_receipt( tx_hash,
endpoint,
timeout )[ "contractAddress" ]
except KeyError as exception:
raise InvalidRPCReplyError("hmyv2_getTransactionReceipt", endpoint) from exception
raise InvalidRPCReplyError(
"hmyv2_getTransactionReceipt",
endpoint
) from exception

@ -2,16 +2,16 @@
Exceptions used by pyhmy
"""
class InvalidRPCReplyError(RuntimeError):
class InvalidRPCReplyError( RuntimeError ):
"""Exception raised when RPC call returns unexpected result Generally
indicates Harmony API has been updated & pyhmy library needs to be updated
as well."""
def __init__(self, method, endpoint):
super().__init__(f"Unexpected reply for {method} from {endpoint}")
def __init__( self, method, endpoint ):
super().__init__( f"Unexpected reply for {method} from {endpoint}" )
class InvalidValidatorError(ValueError):
class InvalidValidatorError( ValueError ):
"""Exception raised Validator does not pass sanity checks."""
errors = {
@ -22,18 +22,17 @@ class InvalidValidatorError(ValueError):
5: "Unable to import validator information from blockchain",
}
def __init__(self, err_code, msg):
def __init__( self, err_code, msg ):
self.code = err_code
self.msg = msg
super().__init__(msg)
super().__init__( msg )
def __str__(self):
def __str__( self ):
return f"[Errno {self.code}] {self.errors[self.code]}: {self.msg}"
class TxConfirmationTimedoutError(AssertionError):
class TxConfirmationTimedoutError( AssertionError ):
"""Exception raised when a transaction is sent to the chain But not
confirmed during the timeout period specified."""
def __init__(self, msg):
super().__init__(f"{msg}")
def __init__( self, msg ):
super().__init__( f"{msg}" )

@ -10,37 +10,41 @@ import logging
import logging.handlers
class _GZipRotator: # pylint: disable=too-few-public-methods
def __call__(self, source, dest):
os.rename(source, dest)
with open(dest, "rb") as f_in:
with gzip.open(f"{dest}.gz", "wb") as f_out:
f_out.writelines(f_in)
os.remove(dest)
class _GZipRotator: # pylint: disable=too-few-public-methods
def __call__( self, source, dest ):
os.rename( source, dest )
with open( dest, "rb" ) as f_in:
with gzip.open( f"{dest}.gz", "wb" ) as f_out:
f_out.writelines( f_in )
os.remove( dest )
class ControlledLogger: # pylint: disable=too-many-instance-attributes
class ControlledLogger: # pylint: disable=too-many-instance-attributes
"""A simple logger that only writes to file when the 'write' method is
called."""
def __init__(self, logger_name, log_dir, backup_count=5):
def __init__( self, logger_name, log_dir, backup_count = 5 ):
"""
: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("/"):
log_dir = log_dir[:-1]
log_dir = os.path.realpath(log_dir)
os.makedirs(log_dir, exist_ok=True)
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
f"{log_dir}/{logger_name}.log",
"midnight",
1,
backupCount = backup_count
)
handler.setFormatter(
logging.Formatter( "%(levelname)s - %(message)s" )
)
handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s"))
handler.rotator = _GZipRotator()
self.filename = handler.baseFilename
self.logger = logging.getLogger(logger_name)
self.logger.addHandler(handler)
self.logger = logging.getLogger( logger_name )
self.logger.addHandler( handler )
self._lock = threading.Lock()
self.filepath = f"{log_dir}/{logger_name}.log"
self.info_buffer = []
@ -48,88 +52,92 @@ class ControlledLogger: # pylint: disable=too-many-instance-attributes
self.warning_buffer = []
self.error_buffer = []
def __repr__(self):
def __repr__( self ):
return f"<ControlledLogger @ {self.filepath} : {self.logger}>"
def _clear(self):
def _clear( self ):
"""Internal method to clear the log buffer."""
self.info_buffer.clear()
self.debug_buffer.clear()
self.warning_buffer.clear()
self.error_buffer.clear()
def info(self, msg):
def info( self, msg ):
"""
:param msg: The info message to log
"""
with self._lock:
self.info_buffer.append(
f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
f"[{threading.get_ident()}] "
f"{datetime.datetime.utcnow()} : {msg}"
)
def debug(self, msg):
def debug( self, msg ):
"""
:param msg: The debug message to log
"""
with self._lock:
self.debug_buffer.append(
f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
f"[{threading.get_ident()}] "
f"{datetime.datetime.utcnow()} : {msg}"
)
def warning(self, msg):
def warning( self, msg ):
"""
:param msg: The warning message to log
"""
with self._lock:
self.warning_buffer.append(
f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
f"[{threading.get_ident()}] "
f"{datetime.datetime.utcnow()} : {msg}"
)
def error(self, msg):
def error( self, msg ):
"""
:param msg: The error message to log
"""
with self._lock:
self.error_buffer.append(
f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}"
f"[{threading.get_ident()}] "
f"{datetime.datetime.utcnow()} : {msg}"
)
def print_info(self):
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):
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):
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):
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):
def write( self ):
"""Flushes ALL of the log buffers to the log file via the logger.
Note that directly after this method call, the respective prints
will print nothing since all log messages are flushed to file.
"""
with self._lock:
self.logger.setLevel(logging.DEBUG)
self.logger.setLevel( logging.DEBUG )
for line in self.debug_buffer:
self.logger.debug(line)
self.logger.setLevel(logging.WARNING)
self.logger.debug( line )
self.logger.setLevel( logging.WARNING )
for line in self.warning_buffer:
self.logger.warning(line)
self.logger.setLevel(logging.ERROR)
self.logger.warning( line )
self.logger.setLevel( logging.ERROR )
for line in self.error_buffer:
self.logger.error(line)
self.logger.setLevel(logging.INFO)
self.logger.error( line )
self.logger.setLevel( logging.INFO )
for line in self.info_buffer:
self.logger.info(line)
self.logger.info( line )
self._clear()

@ -5,11 +5,10 @@ For more granular conversions, see Web3.toWei
from decimal import Decimal
_conversion_unit = Decimal( 1e18 )
_conversion_unit = Decimal(1e18)
def convert_atto_to_one(atto) -> Decimal:
def convert_atto_to_one( atto ) -> Decimal:
"""Convert ATTO to ONE.
Parameters
@ -23,12 +22,12 @@ def convert_atto_to_one(atto) -> Decimal:
decimal
Converted value in ONE
"""
if isinstance(atto, float):
atto = int(atto)
return Decimal(atto) / _conversion_unit
if isinstance( atto, float ):
atto = int( atto )
return Decimal( atto ) / _conversion_unit
def convert_one_to_atto(one) -> Decimal:
def convert_one_to_atto( one ) -> Decimal:
"""Convert ONE to ATTO.
Parameters
@ -41,6 +40,6 @@ def convert_one_to_atto(one) -> Decimal:
decimal
Converted value in ATTO
"""
if isinstance(one, float):
one = str(one)
return Decimal(one) * _conversion_unit
if isinstance( one, float ):
one = str( one )
return Decimal( one ) * _conversion_unit

@ -4,23 +4,23 @@ RPC Specific Exceptions
import requests
class RPCError(RuntimeError):
"""Exception raised when RPC call returns an error."""
def __init__(self, method, endpoint, error):
class RPCError( RuntimeError ):
"""Exception raised when RPC call returns an error."""
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):
class RequestsError( requests.exceptions.RequestException ):
"""Wrapper for requests lib exceptions."""
def __init__( self, endpoint ):
super().__init__( f"Error connecting to {endpoint}" )
def __init__(self, endpoint):
super().__init__(f"Error connecting to {endpoint}")
class RequestsTimeoutError(requests.exceptions.Timeout):
class RequestsTimeoutError( requests.exceptions.Timeout ):
"""Wrapper for requests lib Timeout exceptions."""
def __init__(self, endpoint):
super().__init__(f"Error connecting to {endpoint}")
def __init__( self, endpoint ):
super().__init__( f"Error connecting to {endpoint}" )

@ -11,7 +11,10 @@ from ..constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT
def base_request(
method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
method,
params = None,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str:
"""Basic RPC request.
@ -42,30 +45,40 @@ def base_request(
"""
if params is None:
params = []
elif not isinstance(params, list):
raise TypeError(f"invalid type {params.__class__}")
elif not isinstance( params, list ):
raise TypeError( f"invalid type {params.__class__}" )
try:
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"
}
resp = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
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
raise RequestsTimeoutError( endpoint ) from err
except requests.exceptions.RequestException as err:
raise RequestsError(endpoint) from err
raise RequestsError( endpoint ) from err
def rpc_request(
method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
method,
params = None,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""RPC request.
@ -100,12 +113,12 @@ def rpc_request(
--------
base_request
"""
raw_resp = base_request(method, params, endpoint, timeout)
raw_resp = base_request( method, params, endpoint, timeout )
try:
resp = json.loads(raw_resp)
resp = json.loads( raw_resp )
if "error" in resp:
raise RPCError(method, endpoint, str(resp["error"]))
raise RPCError( method, endpoint, str( resp[ "error" ] ) )
return resp
except json.decoder.JSONDecodeError as err:
raise RPCError(method, endpoint, raw_resp) from err
raise RPCError( method, endpoint, raw_resp ) from err

@ -38,25 +38,34 @@ HARMONY_FORMATTERS = dict(
)
class UnsignedHarmonyTxData(HashableRLP):
class UnsignedHarmonyTxData( HashableRLP ):
"""
Unsigned Harmony transaction data
Includes `shardID` and `toShardID`
as the difference against Eth
"""
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):
class SignedHarmonyTxData( HashableRLP ):
"""
Signed Harmony transaction data
Includes `shardID` and `toShardID`
@ -68,22 +77,35 @@ class SignedHarmonyTxData(HashableRLP):
("s", big_endian_int), # Next 32 bytes
)
# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55
def encode_transaction(
unsigned_transaction, vrs
):
def encode_transaction( unsigned_transaction, vrs ):
"""serialize and encode an unsigned transaction with v,r,s."""
(v, r, s) = vrs # pylint: disable=invalid-name
chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), "v", "r", "s")
if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, SignedHarmonyTxData)):
( v, r, s ) = vrs # pylint: disable=invalid-name
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)
signed_transaction = serializer(
v = v,
r = r,
s = s,
**chain_naive_transaction
)
return rlp.encode( signed_transaction )
def serialize_transaction(filled_transaction):
def serialize_transaction( filled_transaction ):
"""serialize a signed/unsigned transaction."""
if "v" in filled_transaction:
if "shardID" in filled_transaction:
@ -98,35 +120,42 @@ def serialize_transaction(filled_transaction):
for field, _ in serializer._meta.fields:
assert field in filled_transaction, f"Could not find {field} in transaction"
return serializer.from_dict(
{field: filled_transaction[field] for field, _ in serializer._meta.fields}
{
field: filled_transaction[ field ]
for field,
_ in serializer._meta.fields
}
)
# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650
def sanitize_transaction(transaction_dict, private_key):
def sanitize_transaction( transaction_dict, private_key ):
"""remove the originating address from the dict and convert chainId to
int."""
account = Account.from_key( # pylint: disable=no-value-for-parameter
private_key
)
sanitized_transaction = transaction_dict.copy() # do not alter the original dictionary
sanitized_transaction = transaction_dict.copy(
) # do not alter the original dictionary
if "from" in sanitized_transaction:
sanitized_transaction["from"] = convert_one_to_hex(transaction_dict["from"])
if (
sanitized_transaction["from"] == account.address
):
sanitized_transaction = dissoc(sanitized_transaction, "from")
sanitized_transaction[ "from" ] = convert_one_to_hex(
transaction_dict[ "from" ]
)
if sanitized_transaction[ "from" ] == account.address:
sanitized_transaction = dissoc( sanitized_transaction, "from" )
else:
raise TypeError(
"from field must match key's {account.address}, "
"but it was {sanitized_transaction['from']}"
)
if "chainId" in sanitized_transaction:
sanitized_transaction["chainId"] = chain_id_to_int(sanitized_transaction["chainId"])
sanitized_transaction[ "chainId" ] = chain_id_to_int(
sanitized_transaction[ "chainId" ]
)
return account, sanitized_transaction
def sign_transaction(transaction_dict, private_key) -> SignedTransaction:
def sign_transaction( transaction_dict, private_key ) -> SignedTransaction:
"""Sign a (non-staking) transaction dictionary with the specified private
key.
@ -172,33 +201,50 @@ 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" ]
)
# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39
filled_transaction = pipe(
sanitized_transaction,
dict,
partial(merge, TRANSACTION_DEFAULTS),
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)
unsigned_transaction = serialize_transaction( filled_transaction )
transaction_hash = unsigned_transaction.hash()
# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26
if isinstance(
unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData)
unsigned_transaction,
( UnsignedEthereumTxData,
UnsignedHarmonyTxData )
):
chain_id = None
else:
chain_id = unsigned_transaction.v
(v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) # pylint: disable=invalid-name
encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s))
signed_transaction_hash = keccak(encoded_transaction)
( v, # pylint: disable=invalid-name
r, # pylint: disable=invalid-name
s ) = sign_transaction_hash( # pylint: disable=invalid-name
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(
rawTransaction=HexBytes(encoded_transaction),
hash=HexBytes(signed_transaction_hash),
r=r,
s=s,
v=v,
rawTransaction = HexBytes( encoded_transaction ),
hash = HexBytes( signed_transaction_hash ),
r = r,
s = s,
v = v,
)

@ -8,11 +8,13 @@ from .exceptions import InvalidRPCReplyError
from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT
##################
# Validator RPCs #
##################
def get_all_validator_addresses(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of all created validator addresses on chain.
@ -39,13 +41,17 @@ def get_all_validator_addresses(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_information(
validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
validator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get validator information for validator address.
@ -118,17 +124,21 @@ def get_validator_information(
https://api.hmny.io/#659ad999-14ca-4498-8f74-08ed347cab49
"""
method = "hmyv2_getValidatorInformation"
params = [validator_addr]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_elected_validator_addresses(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of elected validator addresses.
@ -156,12 +166,18 @@ def get_elected_validator_addresses(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list:
def get_validators(
epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get validators list for a particular epoch.
Parameters
@ -191,17 +207,22 @@ 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]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_keys(
epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get validator BLS keys in the committee for a particular epoch.
@ -228,17 +249,23 @@ def get_validator_keys(
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152
"""
method = "hmyv2_getValidatorKeys"
params = [epoch]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_information_by_block_number(
validator_addr, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
validator_addr,
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
):
"""Get validator information for validator address at a block.
@ -267,17 +294,22 @@ def get_validator_information_by_block_number(
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L319
"""
method = "hmyv2_getValidatorInformationByBlockNumber"
params = [validator_addr, block_num]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_all_validator_information(
page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
page = 0,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get validator information for all validators on chain.
@ -304,17 +336,22 @@ def get_all_validator_information(
https://api.hmny.io/#df5f1631-7397-48e8-87b4-8dd873235b9c
"""
method = "hmyv2_getAllValidatorInformation"
params = [page]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_self_delegation(
address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get the amount self delegated by validator.
@ -341,19 +378,24 @@ def get_validator_self_delegation(
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L352
"""
method = "hmyv2_getValidatorSelfDelegation"
params = [address]
params = [ address ]
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_total_delegation(
address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get the total amount delegated t ovalidator (including self delegated)
@ -380,19 +422,25 @@ def get_validator_total_delegation(
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L379
"""
method = "hmyv2_getValidatorTotalDelegation"
params = [address]
params = [ address ]
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_all_validator_information_by_block_number(
block_num, page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
page = 0,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get validator information at block number for all validators on chain.
@ -422,20 +470,25 @@ def get_all_validator_information_by_block_number(
https://api.hmny.io/#a229253f-ca76-4b9d-88f5-9fd96e40d583
"""
method = "hmyv2_getAllValidatorInformationByBlockNumber"
params = [page, block_num]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
###################
# Delegation RPCs #
###################
def get_all_delegation_information(
page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
page = 0,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get delegation information for all delegators on chain.
@ -464,19 +517,22 @@ def get_all_delegation_information(
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L413
"""
method = "hmyv2_getAllDelegationInformation"
params = [
page,
]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_delegations_by_delegator(
delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
delegator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of delegations by a delegator.
@ -510,17 +566,23 @@ def get_delegations_by_delegator(
https://api.hmny.io/#454b032c-6072-4ecb-bf24-38b3d6d2af69
"""
method = "hmyv2_getDelegationsByDelegator"
params = [delegator_addr]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_delegations_by_delegator_by_block_number(
delegator_addr, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
delegator_addr,
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of delegations by a delegator at a specific block.
@ -549,20 +611,23 @@ def get_delegations_by_delegator_by_block_number(
https://api.hmny.io/#8ce13bda-e768-47b9-9dbe-193aba410b0a
"""
method = "hmyv2_getDelegationsByDelegatorByBlockNumber"
params = [delegator_addr, block_num]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_delegation_by_delegator_and_validator(
delegator_addr,
validator_address,
endpoint=DEFAULT_ENDPOINT,
timeout=DEFAULT_TIMEOUT,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT,
) -> dict:
"""Get list of delegations by a delegator at a specific block.
@ -592,17 +657,22 @@ def get_delegation_by_delegator_and_validator(
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L605
"""
method = "hmyv2_getDelegationByDelegatorAndValidator"
params = [delegator_addr, validator_address]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_available_redelegation_balance(
delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
delegator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get amount of locked undelegated tokens.
@ -629,19 +699,24 @@ def get_available_redelegation_balance(
https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L653
"""
method = "hmyv2_getAvailableRedelegationBalance"
params = [delegator_addr]
params = [ delegator_addr ]
try:
return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[
"result"
]
rpc_request(
method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
)
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_delegations_by_validator(
validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
validator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of delegations to a validator.
@ -668,20 +743,24 @@ def get_delegations_by_validator(
https://api.hmny.io/#2e02d8db-8fec-41d9-a672-2c9862f63f39
"""
method = "hmyv2_getDelegationsByValidator"
params = [validator_addr]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
########################
# Staking Network RPCs #
########################
def get_current_utility_metrics(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get current utility metrics of network.
@ -711,13 +790,16 @@ def get_current_utility_metrics(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_network_info(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get staking network information.
@ -748,12 +830,17 @@ def get_staking_network_info(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_super_committees(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
def get_super_committees(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get voting committees for current & previous epoch.
Parameters
@ -801,12 +888,17 @@ def get_super_committees(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_total_staking(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
def get_total_staking(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get total staking by validators, only meant to be called on beaconchain.
Parameters
@ -831,13 +923,18 @@ def get_total_staking(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int
"""
method = "hmyv2_getTotalStaking"
try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"])
except (KeyError, TypeError) as exception:
raise InvalidRPCReplyError(method, endpoint) from exception
return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_raw_median_stake_snapshot(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get median stake & additional committee data of the current epoch.
@ -876,6 +973,8 @@ def get_raw_median_stake_snapshot(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception

@ -7,12 +7,7 @@ import math
from decimal import Decimal
from functools import partial
from toolz import (
pipe,
dissoc,
merge,
identity,
)
from toolz import ( pipe, dissoc, merge, identity, )
from hexbytes import HexBytes
@ -32,7 +27,7 @@ from eth_utils.curried import (
to_int,
apply_formatters_to_sequence,
apply_formatter_to_array,
)
)
from .constants import PRECISION, MAX_DECIMAL
@ -45,15 +40,13 @@ from .staking_structures import (
EditValidator,
DelegateOrUndelegate,
CollectRewards,
)
)
from .util import convert_one_to_hex
# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335
def _convert_staking_percentage_to_number(
value,
):
def _convert_staking_percentage_to_number( value, ):
"""Convert from staking percentage to integer For example, 0.1 becomes
1000000000000000000. Since Python floats are problematic with precision,
this function is used as a workaround.
@ -72,47 +65,45 @@ def _convert_staking_percentage_to_number(
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"
if isinstance(value, Decimal):
value = str(value)
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[1:]
if len(value1) == 0:
raise ValueError("StakingDecimal string is empty")
spaced = value1.split(" ")
if len(spaced) > 1:
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")
combined_str += splitted[1]
elif len(splitted) > 2:
raise ValueError("Too many periods to be a StakingDecimal string")
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( " " )
if len( spaced ) > 1:
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" )
combined_str += splitted[ 1 ]
elif len( splitted ) > 2:
raise ValueError( "Too many periods to be a StakingDecimal string" )
if length > PRECISION:
raise ValueError(
"Too much precision, must be less than {PRECISION}"
)
raise ValueError( "Too much precision, must be less than {PRECISION}" )
zeroes_to_add = PRECISION - length
combined_str += (
"0" * zeroes_to_add
) # This will not have any periods, so it is effectively a large integer
val = int(combined_str)
val = int( combined_str )
assert val <= MAX_DECIMAL, "Staking percentage is too large"
return val
def _get_account_and_transaction(transaction_dict, private_key):
def _get_account_and_transaction( transaction_dict, private_key ):
"""Create account from private key and sanitize the transaction
Sanitization involves removal of 'from' key And conversion of chainId key
from str to int (if present)
from str to int ( if present )
Parameters
----------
@ -133,21 +124,25 @@ def _get_account_and_transaction(transaction_dict, private_key):
"""
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
) # 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
# pylint: disable=too-many-locals,protected-access,invalid-name
def _sign_transaction_generic(account, sanitized_transaction, parent_serializer):
def _sign_transaction_generic(
account,
sanitized_transaction,
parent_serializer
):
"""Sign a generic staking transaction, given the serializer base class and
account.
Paramters
---------
account: :obj:`eth_account.Account`, the account to use for signing
sanitized_transaction: :obj:`dict`, The sanitized transaction (chainId checks and no from key)
sanitized_transaction: :obj:`dict`, The sanitized transaction ( chainId checks and no from key )
parent_serializer: :obj: The serializer class from staking_structures
Returns
@ -161,40 +156,52 @@ 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:
if sanitized_transaction.get( "chainId", 0 ) == 0:
unsigned_serializer, signed_serializer = (
parent_serializer.Unsigned(),
parent_serializer.Signed(),
parent_serializer.Unsigned( ),
parent_serializer.Signed( ),
) # unsigned, signed
else:
unsigned_serializer, signed_serializer = (
parent_serializer.SignedChainId(),
parent_serializer.SignedChainId(),
parent_serializer.SignedChainId( ),
parent_serializer.SignedChainId( ),
) # since chain_id_to_v adds v/r/s, unsigned is not used here
# fill the transaction
# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39
filled_transaction = pipe(
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 field, _ in unsigned_serializer._meta.fields:
assert field in filled_transaction, f"Could not find {field} in transaction"
unsigned_transaction = unsigned_serializer.from_dict(
{f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields}
{
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)
transaction_hash = unsigned_transaction.hash( )
( v,
r,
s
) = sign_transaction_hash( account._key_obj,
transaction_hash,
chain_id )
chain_naive_transaction = dissoc(
unsigned_transaction.as_dict(), "v", "r", "s"
unsigned_transaction.as_dict( ),
"v",
"r",
"s"
) # remove extra v/r/s added by chain_id_to_v
# serialize it
# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207
@ -206,30 +213,33 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer)
r=r,
s=s, # in the below statement, remove everything not expected by signed_serializer
**{
f: chain_naive_transaction[f]
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)
encoded_transaction = rlp.encode( signed_transaction )
# hash it
signed_transaction_hash = keccak(encoded_transaction)
signed_transaction_hash = keccak( encoded_transaction )
# return is
return SignedTransaction(
rawTransaction=HexBytes(encoded_transaction),
hash=HexBytes(signed_transaction_hash),
r=r,
s=s,
v=v,
rawTransaction = HexBytes( encoded_transaction ),
hash = HexBytes( signed_transaction_hash ),
r = r,
s = s,
v = v,
)
def _sign_delegate_or_undelegate(transaction_dict, private_key):
def _sign_delegate_or_undelegate( transaction_dict, private_key ):
"""Sign a delegate or undelegate transaction See sign_staking_transaction
for details."""
# preliminary steps
if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]:
if transaction_dict[ "directive" ] not in [
Directive.Delegate,
Directive.Undelegate
]:
raise TypeError(
"Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate"
)
@ -238,43 +248,61 @@ def _sign_delegate_or_undelegate(transaction_dict, private_key):
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)],
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"),
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
account,
sanitized_transaction,
DelegateOrUndelegate
)
def _sign_collect_rewards(transaction_dict, private_key):
def _sign_collect_rewards( transaction_dict, private_key ):
"""Sign a collect rewards transaction 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
)
# 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)
return _sign_transaction_generic(
account,
sanitized_transaction,
CollectRewards
)
def _sign_create_validator(transaction_dict, private_key):
def _sign_create_validator( transaction_dict, private_key ):
"""Sign a create validator transaction See sign_staking_transaction for
details."""
# preliminary steps
if transaction_dict["directive"] != Directive.CreateValidator:
if transaction_dict[ "directive" ] != Directive.CreateValidator:
raise TypeError(
"Only CreateValidator is supported by _sign_create_or_edit_validator"
)
@ -284,70 +312,75 @@ def _sign_create_validator(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
hexstr_if_str( to_int ), # formatter
[
_convert_staking_percentage_to_number(sanitized_transaction.pop("rate")),
_convert_staking_percentage_to_number( sanitized_transaction.pop( "rate" ) ),
_convert_staking_percentage_to_number(
sanitized_transaction.pop("max-rate")
sanitized_transaction.pop( "max-rate" )
),
_convert_staking_percentage_to_number(
sanitized_transaction.pop("max-change-rate")
sanitized_transaction.pop( "max-change-rate" )
),
],
)
commission = [[element] for element in commission]
commission = [ [ element ] for element in commission ]
bls_keys = apply_formatter_to_array(
hexstr_if_str(to_bytes), # formatter
sanitized_transaction.pop("bls-public-keys"),
hexstr_if_str( to_bytes ), # formatter
sanitized_transaction.pop( "bls-public-keys" ),
)
bls_key_sigs = apply_formatter_to_array(
hexstr_if_str(to_bytes), sanitized_transaction.pop("bls-key-sigs") # formatter
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
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
), # min self delegation ( in ONE ), decimals are silently dropped
hexstr_if_str(
to_int
), # max total delegation (in ONE), decimals are silently dropped
), # 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)
), # amount ( the Hexlify in the SDK drops the decimals, which is what we will do too )
],
[
convert_one_to_hex(sanitized_transaction.pop("validatorAddress")),
convert_one_to_hex( sanitized_transaction.pop( "validatorAddress" ) ),
description,
commission,
math.floor(
sanitized_transaction.pop("min-self-delegation")
sanitized_transaction.pop( "min-self-delegation" )
), # Decimal floors it correctly
math.floor(sanitized_transaction.pop("max-total-delegation")),
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)
return _sign_transaction_generic(
account,
sanitized_transaction,
CreateValidator
)
def _sign_edit_validator(transaction_dict, private_key):
def _sign_edit_validator( transaction_dict, private_key ):
"""Sign an edit validator transaction See sign_staking_transaction for
details."""
# preliminary steps
if transaction_dict["directive"] != Directive.EditValidator:
if transaction_dict[ "directive" ] != Directive.EditValidator:
raise TypeError(
"Only EditValidator is supported by _sign_create_or_edit_validator"
)
@ -357,44 +390,48 @@ def _sign_edit_validator(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
hexstr_if_str( to_bytes ), # address
identity, # description
identity, # new rate (it's in a list so can't do hexstr_if_str)
identity, # new rate ( it's in a list so can't do hexstr_if_str )
hexstr_if_str(
to_int
), # min self delegation (in ONE), decimals are silently dropped
), # 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
hexstr_if_str(to_bytes), # key to add sig
), # 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
hexstr_if_str( to_bytes ), # key to add sig
],
[
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"))],
[ _convert_staking_percentage_to_number( sanitized_transaction.pop( "rate" ) ) ],
math.floor(
sanitized_transaction.pop("min-self-delegation")
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"),
sanitized_transaction.pop("bls-key-to-add-sig"),
math.floor( sanitized_transaction.pop( "max-total-delegation" ) ),
sanitized_transaction.pop( "bls-key-to-remove" ),
sanitized_transaction.pop( "bls-key-to-add" ),
sanitized_transaction.pop( "bls-key-to-add-sig" ),
],
)
return _sign_transaction_generic(account, sanitized_transaction, EditValidator)
return _sign_transaction_generic(
account,
sanitized_transaction,
EditValidator
)
def sign_staking_transaction(transaction_dict, private_key):
def sign_staking_transaction( transaction_dict, private_key ):
"""Sign a supplied transaction_dict with the private_key.
Parameters
@ -412,7 +449,7 @@ def sign_staking_transaction(transaction_dict, private_key):
Delegate/Undelegate:
delegatorAddress: :obj:`str`, Address of the delegator
validatorAddress: :obj:`str`, Address of the validator
amount: :obj:`int`, Amount to (un)delegate in ATTO
amount: :obj:`int`, Amount to ( un )delegate in ATTO
CreateValidator:
validatorAddress: :obj:`str`, Address of the validator
name: ;obj:`str`, Name of the validator
@ -468,16 +505,16 @@ def sign_staking_transaction(transaction_dict, private_key):
assert "chainId" in transaction_dict, "chainId missing"
assert "directive" in transaction_dict, "Staking transaction type not specified"
assert isinstance(
transaction_dict["directive"], Directive
transaction_dict[ "directive" ], Directive
), "Unknown staking transaction type"
if transaction_dict["directive"] == Directive.CollectRewards:
return _sign_collect_rewards(transaction_dict, private_key)
if transaction_dict["directive"] == Directive.Delegate:
return _sign_delegate_or_undelegate(transaction_dict, private_key)
if transaction_dict["directive"] == Directive.Undelegate:
return _sign_delegate_or_undelegate(transaction_dict, private_key)
if transaction_dict["directive"] == Directive.CreateValidator:
return _sign_create_validator(transaction_dict, private_key)
if transaction_dict["directive"] == Directive.EditValidator:
return _sign_edit_validator(transaction_dict, private_key)
raise ValueError('Unknown staking transaction type')
if transaction_dict[ "directive" ] == Directive.CollectRewards:
return _sign_collect_rewards( transaction_dict, private_key )
if transaction_dict[ "directive" ] == Directive.Delegate:
return _sign_delegate_or_undelegate( transaction_dict, private_key )
if transaction_dict[ "directive" ] == Directive.Undelegate:
return _sign_delegate_or_undelegate( transaction_dict, private_key )
if transaction_dict[ "directive" ] == Directive.CreateValidator:
return _sign_create_validator( transaction_dict, private_key )
if transaction_dict[ "directive" ] == Directive.EditValidator:
return _sign_edit_validator( transaction_dict, private_key )
raise ValueError( 'Unknown staking transaction type' )

@ -10,16 +10,12 @@ from rlp.sedes import big_endian_int, Binary, CountableList, List, Text
from eth_rlp import HashableRLP
from eth_utils.curried import (
to_int,
hexstr_if_str,
)
from eth_utils.curried import ( to_int, hexstr_if_str, )
# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120
class Directive(
Enum
):
def _generate_next_value_(name, start, count, last_values): # pylint: disable=no-self-argument
class Directive( Enum ):
def _generate_next_value_( name, start, count, last_values ): # pylint: disable=no-self-argument
return count
CreateValidator = auto()
@ -39,24 +35,36 @@ FORMATTERS = {
"chainId": hexstr_if_str(to_int),
}
class CollectRewards:
@staticmethod
def UnsignedChainId():
class UnsignedChainId(HashableRLP):
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):
class SignedChainId( HashableRLP ):
fields = CollectRewards.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
@ -69,14 +77,15 @@ class CollectRewards:
@staticmethod
def Unsigned():
class Unsigned(HashableRLP):
fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId
class Unsigned( HashableRLP ):
fields = CollectRewards.UnsignedChainId(
)._meta.fields[ :-1 ] # drop chainId
return Unsigned
@staticmethod
def Signed():
class Signed(HashableRLP):
class Signed( HashableRLP ):
fields = CollectRewards.Unsigned()._meta.fields[
:-3
] + ( # drop last 3 for raw.pop()
@ -91,31 +100,38 @@ class CollectRewards:
class DelegateOrUndelegate:
@staticmethod
def UnsignedChainId():
class UnsignedChainId(HashableRLP):
class UnsignedChainId( HashableRLP ):
fields = (
("directive", big_endian_int),
( "directive",
big_endian_int ),
(
"stakeMsg",
List(
[
Binary.fixed_length(20, allow_empty=True),
Binary.fixed_length(20, allow_empty=True),
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),
( "nonce",
big_endian_int ),
( "gasPrice",
big_endian_int ),
( "gasLimit",
big_endian_int ),
( "chainId",
big_endian_int ),
)
return UnsignedChainId
@staticmethod
def SignedChainId():
class SignedChainId(HashableRLP):
class SignedChainId( HashableRLP ):
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
@ -128,16 +144,15 @@ class DelegateOrUndelegate:
@staticmethod
def Unsigned():
class Unsigned(HashableRLP):
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[
:-1
] # drop chainId
class Unsigned( HashableRLP ):
fields = DelegateOrUndelegate.UnsignedChainId(
)._meta.fields[ :-1 ] # drop chainId
return Unsigned
@staticmethod
def Signed():
class Signed(HashableRLP):
class Signed( HashableRLP ):
fields = DelegateOrUndelegate.Unsigned()._meta.fields[
:-3
] + ( # drop last 3 for raw.pop()
@ -152,7 +167,7 @@ class DelegateOrUndelegate:
class CreateValidator:
@staticmethod
def UnsignedChainId():
class UnsignedChainId(HashableRLP):
class UnsignedChainId( HashableRLP ):
fields = (
("directive", big_endian_int),
(
@ -196,7 +211,7 @@ class CreateValidator:
@staticmethod
def SignedChainId():
class SignedChainId(HashableRLP):
class SignedChainId( HashableRLP ):
fields = CreateValidator.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
@ -209,14 +224,15 @@ class CreateValidator:
@staticmethod
def Unsigned():
class Unsigned(HashableRLP):
fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId
class Unsigned( HashableRLP ):
fields = CreateValidator.UnsignedChainId(
)._meta.fields[ :-1 ] # drop chainId
return Unsigned
@staticmethod
def Signed():
class Signed(HashableRLP):
class Signed( HashableRLP ):
fields = CreateValidator.Unsigned()._meta.fields[
:-3
] + ( # drop last 3 for raw.pop()
@ -231,7 +247,7 @@ class CreateValidator:
class EditValidator:
@staticmethod
def UnsignedChainId():
class UnsignedChainId(HashableRLP):
class UnsignedChainId( HashableRLP ):
fields = (
("directive", big_endian_int),
(
@ -276,7 +292,7 @@ class EditValidator:
@staticmethod
def SignedChainId():
class SignedChainId(HashableRLP):
class SignedChainId( HashableRLP ):
fields = EditValidator.UnsignedChainId()._meta.fields[
:-1
] + ( # drop chainId
@ -289,14 +305,15 @@ class EditValidator:
@staticmethod
def Unsigned():
class Unsigned(HashableRLP):
fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId
class Unsigned( HashableRLP ):
fields = EditValidator.UnsignedChainId(
)._meta.fields[ :-1 ] # drop chainId
return Unsigned
@staticmethod
def Signed():
class Signed(HashableRLP):
class Signed( HashableRLP ):
fields = EditValidator.Unsigned()._meta.fields[
:-3
] + ( # drop last 3 for raw.pop()

@ -13,7 +13,8 @@ from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError
# Transaction Pool RPCs #
#########################
def get_pending_transactions(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of pending transactions.
@ -39,13 +40,16 @@ def get_pending_transactions(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_error_sink(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get current transactions error sink.
@ -74,13 +78,16 @@ def get_transaction_error_sink(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_pending_staking_transactions(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of pending staking transactions.
@ -106,13 +113,16 @@ def get_pending_staking_transactions(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_transaction_error_sink(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get current staking transactions error sink.
@ -142,12 +152,17 @@ def get_staking_transaction_error_sink(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_pool_stats(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
def get_pool_stats(
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get stats of the pool, that is, number of pending and queued (non-
executable) transactions.
@ -175,16 +190,20 @@ def get_pool_stats(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
####################
# Transaction RPCs #
####################
def get_transaction_by_hash(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get transaction by hash.
@ -231,17 +250,23 @@ def get_transaction_by_hash(
https://api.hmny.io/#117e84f6-a0ec-444e-abe0-455701310389
"""
method = "hmyv2_getTransactionByHash"
params = [tx_hash]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_by_block_hash_and_index(
block_hash, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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.
@ -271,17 +296,23 @@ def get_transaction_by_block_hash_and_index(
https://api.hmny.io/#7c7e8d90-4984-4ebe-bb7e-d7adec167503
"""
method = "hmyv2_getTransactionByBlockHashAndIndex"
params = [block_hash, tx_index]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_by_block_number_and_index(
block_num, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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.
@ -311,17 +342,22 @@ def get_transaction_by_block_number_and_index(
https://api.hmny.io/#bcde8b1c-6ab9-4950-9835-3c7564e49c3e
"""
method = "hmyv2_getTransactionByBlockNumberAndIndex"
params = [block_num, tx_index]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_receipt(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get transaction receipt corresponding to tx_hash.
@ -364,17 +400,22 @@ def get_transaction_receipt(
https://api.hmny.io/#0c2799f8-bcdc-41a4-b362-c3a6a763bb5e
"""
method = "hmyv2_getTransactionReceipt"
params = [tx_hash]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def send_raw_transaction(
signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
signed_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str:
"""Send signed transaction.
@ -403,18 +444,23 @@ def send_raw_transaction(
-------------
https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0
"""
params = [signed_tx]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def send_and_confirm_raw_transaction(
signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
signed_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Send signed transaction and wait for it to be confirmed.
@ -445,24 +491,27 @@ def send_and_confirm_raw_transaction(
-------------
https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0
"""
tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint)
tx_hash = send_raw_transaction( signed_tx, endpoint = endpoint )
start_time = time.time()
while (time.time() - start_time) <= timeout:
tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint)
while ( time.time() - start_time ) <= timeout:
tx_response = get_transaction_by_hash( tx_hash, endpoint = endpoint )
if tx_response is not None:
block_hash = tx_response.get("blockHash", "0x00")
unique_chars = "".join(set(list(block_hash[2:])))
block_hash = tx_response.get( "blockHash", "0x00" )
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) )
if unique_chars != "0":
return tx_response
time.sleep(random.uniform(0.2, 0.5))
raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.")
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
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Get list of pending cross shard transactions.
@ -511,13 +560,17 @@ def get_pending_cx_receipts(
"""
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_cx_receipt_by_hash(
cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
cx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get cross shard receipt by hash on the receiving shard end point.
@ -552,18 +605,23 @@ def get_cx_receipt_by_hash(
-------------
https://api.hmny.io/#3d6ad045-800d-4021-aeb5-30a0fbf724fe
"""
params = [cx_hash]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def resend_cx_receipt(
cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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.
@ -592,20 +650,25 @@ def resend_cx_receipt(
https://api.hmny.io/#c658b56b-d20b-480d-b71a-b0bc505d2164
"""
method = "hmyv2_resendCx"
params = [cx_hash]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
############################
# Staking Transaction RPCs #
############################
def get_staking_transaction_by_hash(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get staking transaction by hash.
@ -646,17 +709,23 @@ def get_staking_transaction_by_hash(
https://api.hmny.io/#296cb4d0-bce2-48e3-bab9-64c3734edd27
"""
method = "hmyv2_getStakingTransactionByHash"
params = [tx_hash]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_transaction_by_block_hash_and_index(
block_hash, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_hash,
tx_index,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get staking transaction by block hash and transaction index.
@ -685,17 +754,23 @@ def get_staking_transaction_by_block_hash_and_index(
https://api.hmny.io/#ba96cf61-61fe-464a-aa06-2803bb4b358f
"""
method = "hmyv2_getStakingTransactionByBlockHashAndIndex"
params = [block_hash, tx_index]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_transaction_by_block_number_and_index(
block_num, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
block_num,
tx_index,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict:
"""Get staking transaction by block number and transaction index.
@ -724,17 +799,22 @@ def get_staking_transaction_by_block_number_and_index(
https://api.hmny.io/#fb41d717-1645-4d3e-8071-6ce8e1b65dd3
"""
method = "hmyv2_getStakingTransactionByBlockNumberAndIndex"
params = [block_num, tx_index]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def send_raw_staking_transaction(
raw_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
raw_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str:
"""Send signed staking transaction.
@ -764,17 +844,22 @@ def send_raw_staking_transaction(
https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401
"""
method = "hmyv2_sendRawStakingTransaction"
params = [raw_tx]
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 exception:
raise InvalidRPCReplyError(method, endpoint) from exception
raise InvalidRPCReplyError( method, endpoint ) from exception
def send_and_confirm_raw_staking_transaction(
signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
signed_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list:
"""Send signed staking transaction and wait for it to be confirmed.
@ -805,14 +890,19 @@ def send_and_confirm_raw_staking_transaction(
-------------
https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401
"""
tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint)
tx_hash = send_raw_staking_transaction( signed_tx, endpoint = endpoint )
start_time = time.time()
while (time.time() - start_time) <= timeout:
tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint)
while ( time.time() - start_time ) <= timeout:
tx_response = get_staking_transaction_by_hash(
tx_hash,
endpoint = endpoint
)
if tx_response is not None:
block_hash = tx_response.get("blockHash", "0x00")
unique_chars = "".join(set(list(block_hash[2:])))
block_hash = tx_response.get( "blockHash", "0x00" )
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) )
if unique_chars != "0":
return tx_response
time.sleep(random.uniform(0.2, 0.5))
raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.")
time.sleep( random.uniform( 0.2, 0.5 ) )
raise TxConfirmationTimedoutError(
"Could not confirm transaction on-chain."
)

@ -13,17 +13,14 @@ from eth_utils import to_checksum_address
from .blockchain import get_latest_header
from .rpc.exceptions import (
RPCError,
RequestsError,
RequestsTimeoutError,
)
from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError, )
from .account import is_valid_address
from .bech32.bech32 import bech32_decode, bech32_encode, convertbits
class Typgpy(str):
class Typgpy( str ):
"""Typography constants for pretty printing.
Note that an ENDC is needed to mark the end of a 'highlighted' text
@ -40,7 +37,7 @@ class Typgpy(str):
UNDERLINE = "\033[4m"
def chain_id_to_int(chain_id):
def chain_id_to_int( chain_id ):
"""
If chain_id is a string, converts it to int.
If chain_id is an int, returns the int.
@ -48,72 +45,73 @@ def chain_id_to_int(chain_id):
Else raises TypeError
"""
chain_ids = dict(
Default=0,
EthMainnet=1,
Morden=2,
Ropsten=3,
Rinkeby=4,
RootstockMainnet=30,
RootstockTestnet=31,
Kovan=42,
EtcMainnet=61,
EtcTestnet=62,
Geth=1337,
Ganache=0,
HmyMainnet=1,
HmyTestnet=2,
HmyLocal=2,
HmyPangaea=3,
Default = 0,
EthMainnet = 1,
Morden = 2,
Ropsten = 3,
Rinkeby = 4,
RootstockMainnet = 30,
RootstockTestnet = 31,
Kovan = 42,
EtcMainnet = 61,
EtcTestnet = 62,
Geth = 1337,
Ganache = 0,
HmyMainnet = 1,
HmyTestnet = 2,
HmyLocal = 2,
HmyPangaea = 3,
)
# do not validate integer chainids, only known strings
if isinstance(chain_id, str):
if isinstance( chain_id, str ):
assert (
chain_id in chain_ids
), f"Chain {chain_id} unknown, specify an integer chainId"
return chain_ids.get(chain_id)
if isinstance(chain_id, int):
return chain_ids.get( chain_id )
if isinstance( chain_id, int ):
return chain_id
raise TypeError("chainId must be str or int")
raise TypeError( "chainId must be str or int" )
def get_gopath():
"""
:returns The go-path, assuming that go is installed.
"""
return subprocess.check_output(["go", "env", "GOPATH"]).decode().strip()
return subprocess.check_output( [ "go", "env", "GOPATH" ] ).decode().strip()
def get_goversion():
"""
:returns The go-version, assuming that go is installed.
"""
return subprocess.check_output(["go", "version"]).decode().strip()
return subprocess.check_output( [ "go", "version" ] ).decode().strip()
def convert_one_to_hex(addr):
def convert_one_to_hex( addr ):
"""Given a one address, convert it to hex checksum address."""
if not is_valid_address(addr):
return to_checksum_address(addr)
_, data = bech32_decode(addr)
buf = convertbits(data, 5, 8, False)
address = "0x" + "".join(f"{x:02x}" for x in buf)
return str(to_checksum_address(address))
if not is_valid_address( addr ):
return to_checksum_address( addr )
_, data = bech32_decode( addr )
buf = convertbits( data, 5, 8, False )
address = "0x" + "".join( f"{x:02x}" for x in buf )
return str( to_checksum_address( address ) )
def convert_hex_to_one(addr):
def convert_hex_to_one( addr ):
"""Given a hex address, convert it to a one address."""
if is_valid_address(addr):
if is_valid_address( addr ):
return addr
checksum_addr = str(to_checksum_address(addr))
checksum_addr = str( to_checksum_address( addr ) )
data = bytearray.fromhex(
checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr
checksum_addr[ 2 : ] if checksum_addr
.startswith( "0x" ) else checksum_addr
)
buf = convertbits(data, 8, 5)
return str(bech32_encode("one", buf))
buf = convertbits( data, 8, 5 )
return str( bech32_encode( "one", buf ) )
def is_active_shard(endpoint, delay_tolerance=60):
def is_active_shard( endpoint, delay_tolerance = 60 ):
"""
:param endpoint: The endpoint of the SHARD to check
:param delay_tolerance: The time (in seconds) that the shard timestamp can be behind
@ -121,14 +119,15 @@ 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
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,
"%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):
return abs( time_delta.seconds ) < delay_tolerance
except ( RPCError, RequestsError, RequestsTimeoutError ):
return False
@ -144,33 +143,36 @@ def get_bls_build_variables():
variables = {}
try:
openssl_dir = (
subprocess.check_output(["which", "openssl"])
.decode()
.strip()
.split("\n", maxsplit=1)[0]
subprocess.check_output(
[ "which",
"openssl" ]
).decode().strip().split( "\n",
maxsplit = 1 )[ 0 ]
)
except (IndexError, subprocess.CalledProcessError) as exception:
raise RuntimeError("`openssl` not found") from exception
except ( IndexError, subprocess.CalledProcessError ) as exception:
raise RuntimeError( "`openssl` not found" ) from exception
hmy_path = f"{get_gopath()}/src/github.com/harmony-one"
bls_dir = f"{hmy_path}/bls"
mcl_dir = f"{hmy_path}/mcl"
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"):
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_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"]
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"
]
else:
variables["CGO_CFLAGS"] = f"-I{bls_dir}/include -I{mcl_dir}/include"
variables["CGO_LDFLAGS"] = f"-L{bls_dir}/lib"
variables["LD_LIBRARY_PATH"] = f"{bls_dir}/lib:{mcl_dir}/lib"
variables[ "CGO_CFLAGS" ] = f"-I{bls_dir}/include -I{mcl_dir}/include"
variables[ "CGO_LDFLAGS" ] = f"-L{bls_dir}/lib"
variables[ "LD_LIBRARY_PATH" ] = f"{bls_dir}/lib:{mcl_dir}/lib"
return variables
def json_load(string, **kwargs):
def json_load( string, **kwargs ):
"""
:param string: The JSON string to load
:returns A dictionary loaded from a JSON string to a dictionary.
@ -179,7 +181,7 @@ def json_load(string, **kwargs):
Note that this prints the failed input should an error arise.
"""
try:
return json.loads(string, **kwargs)
return json.loads( string, **kwargs )
except Exception as exception:
print(f"{Typgpy.FAIL}Could not parse input: '{string}'{Typgpy.ENDC}")
print( f"{Typgpy.FAIL}Could not parse input: '{string}'{Typgpy.ENDC}" )
raise exception

@ -22,11 +22,7 @@ from .constants import (
from .exceptions import InvalidValidatorError
from .rpc.exceptions import (
RPCError,
RequestsError,
RequestsTimeoutError,
)
from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError, )
from .staking import get_all_validator_addresses, get_validator_information
@ -34,16 +30,22 @@ from .staking_structures import Directive
from .staking_signing import sign_staking_transaction
class Validator: # pylint: disable=too-many-instance-attributes, too-many-public-methods
class Validator: # pylint: disable=too-many-instance-attributes, too-many-public-methods
"""
Harmony validator
"""
def __init__(self, address):
if not isinstance(address, str):
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")
def __init__( self, address ):
if not isinstance( address, str ):
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"
)
self._address = address
self._bls_keys = []
self._bls_key_sigs = []
@ -62,7 +64,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
self._max_change_rate = None
self._max_rate = None
def _sanitize_input(self, data, check_str=False) -> str:
def _sanitize_input( self, data, check_str = False ) -> str:
"""If data is None, return '' else return data.
Raises
@ -70,26 +72,26 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError if check_str is True and str is not passed
"""
if check_str:
if not isinstance(data, str):
if not isinstance( data, str ):
raise InvalidValidatorError(
3,
"Expected data to be string "
f"to avoid floating point precision issues but got {data}",
)
return "" if not data else str(data)
return "" if not data else str( data )
def __str__(self) -> str:
def __str__( self ) -> str:
"""Returns JSON string representation of Validator fields."""
info = self.export()
for key, value in info.items():
if isinstance(value, Decimal):
info[key] = str(value)
return json.dumps(info)
if isinstance( value, Decimal ):
info[ key ] = str( value )
return json.dumps( info )
def __repr__(self) -> str:
def __repr__( self ) -> str:
return f"<Validator: {hex(id(self))}>"
def get_address(self) -> str:
def get_address( self ) -> str:
"""Get validator address.
Returns
@ -99,7 +101,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._address
def add_bls_key(self, key) -> bool:
def add_bls_key( self, key ) -> bool:
"""Add BLS public key to validator BLS keys if not already in list.
Returns
@ -107,13 +109,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
bool
If adding BLS key succeeded
"""
key = self._sanitize_input(key)
key = self._sanitize_input( key )
if key not in self._bls_keys:
self._bls_keys.append(key)
self._bls_keys.append( key )
return True
return False
def remove_bls_key(self, key) -> bool:
def remove_bls_key( self, key ) -> bool:
"""Remove BLS public key from validator BLS keys if exists.
Returns
@ -121,13 +123,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
bool
If removing BLS key succeeded
"""
key = self._sanitize_input(key)
key = self._sanitize_input( key )
if key in self._bls_keys:
self._bls_keys.remove(key)
self._bls_keys.remove( key )
return True
return False
def get_bls_keys(self) -> list:
def get_bls_keys( self ) -> list:
"""Get list of validator BLS keys.
Returns
@ -137,7 +139,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._bls_keys
def add_bls_key_sig(self, key) -> bool:
def add_bls_key_sig( self, key ) -> bool:
"""Add BLS public key to validator BLS keys if not already in list.
Returns
@ -145,13 +147,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
bool
If adding BLS key succeeded
"""
key = self._sanitize_input(key)
key = self._sanitize_input( key )
if key not in self._bls_key_sigs:
self._bls_key_sigs.append(key)
self._bls_key_sigs.append( key )
return True
return False
def remove_bls_key_sig(self, key) -> bool:
def remove_bls_key_sig( self, key ) -> bool:
"""Remove BLS public key from validator BLS keys if exists.
Returns
@ -159,13 +161,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
bool
If removing BLS key succeeded
"""
key = self._sanitize_input(key)
key = self._sanitize_input( key )
if key in self._bls_key_sigs:
self._bls_key_sigs.remove(key)
self._bls_key_sigs.remove( key )
return True
return False
def get_bls_key_sigs(self) -> list:
def get_bls_key_sigs( self ) -> list:
"""Get list of validator BLS keys.
Returns
@ -175,7 +177,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._bls_key_sigs
def set_name(self, name):
def set_name( self, name ):
"""Set validator name.
Parameters
@ -188,14 +190,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
name = self._sanitize_input(name)
if len(name) > NAME_CHAR_LIMIT:
name = self._sanitize_input( name )
if len( name ) > NAME_CHAR_LIMIT:
raise InvalidValidatorError(
3, f"Name must be less than {NAME_CHAR_LIMIT} characters"
3,
f"Name must be less than {NAME_CHAR_LIMIT} characters"
)
self._name = name
def get_name(self) -> str:
def get_name( self ) -> str:
"""Get validator name.
Returns
@ -205,7 +208,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._name
def set_identity(self, identity):
def set_identity( self, identity ):
"""Set validator identity.
Parameters
@ -218,14 +221,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
identity = self._sanitize_input(identity)
if len(identity) > IDENTITY_CHAR_LIMIT:
identity = self._sanitize_input( identity )
if len( identity ) > IDENTITY_CHAR_LIMIT:
raise InvalidValidatorError(
3, f"Identity must be less than {IDENTITY_CHAR_LIMIT} characters"
3,
f"Identity must be less than {IDENTITY_CHAR_LIMIT} characters"
)
self._identity = identity
def get_identity(self) -> str:
def get_identity( self ) -> str:
"""Get validator identity.
Returns
@ -235,7 +239,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._identity
def set_website(self, website):
def set_website( self, website ):
"""Set validator website.
Parameters
@ -248,14 +252,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
website = self._sanitize_input(website)
if len(website) > WEBSITE_CHAR_LIMIT:
website = self._sanitize_input( website )
if len( website ) > WEBSITE_CHAR_LIMIT:
raise InvalidValidatorError(
3, f"Website must be less than {WEBSITE_CHAR_LIMIT} characters"
3,
f"Website must be less than {WEBSITE_CHAR_LIMIT} characters"
)
self._website = website
def get_website(self) -> str:
def get_website( self ) -> str:
"""Get validator website.
Returns
@ -265,7 +270,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._website
def set_security_contact(self, contact):
def set_security_contact( self, contact ):
"""Set validator security contact.
Parameters
@ -278,15 +283,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
contact = self._sanitize_input(contact)
if len(contact) > SECURITY_CONTACT_CHAR_LIMIT:
contact = self._sanitize_input( contact )
if len( contact ) > SECURITY_CONTACT_CHAR_LIMIT:
raise InvalidValidatorError(
3,
f"Security contact must be less than {SECURITY_CONTACT_CHAR_LIMIT} characters",
)
self._security_contact = contact
def get_security_contact(self) -> str:
def get_security_contact( self ) -> str:
"""Get validator security contact.
Returns
@ -296,7 +301,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._security_contact
def set_details(self, details):
def set_details( self, details ):
"""Set validator details.
Parameters
@ -309,14 +314,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
details = self._sanitize_input(details)
if len(details) > DETAILS_CHAR_LIMIT:
details = self._sanitize_input( details )
if len( details ) > DETAILS_CHAR_LIMIT:
raise InvalidValidatorError(
3, f"Details must be less than {DETAILS_CHAR_LIMIT} characters"
3,
f"Details must be less than {DETAILS_CHAR_LIMIT} characters"
)
self._details = details
def get_details(self) -> str:
def get_details( self ) -> str:
"""Get validator details.
Returns
@ -326,7 +332,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._details
def set_min_self_delegation(self, delegation):
def set_min_self_delegation( self, delegation ):
"""Set validator min self delegation.
Parameters
@ -339,12 +345,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
delegation = self._sanitize_input(delegation)
delegation = self._sanitize_input( delegation )
try:
delegation = Decimal(delegation)
except (TypeError, InvalidOperation) as exception:
delegation = Decimal( delegation )
except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(
3, "Min self delegation must be a number"
3,
"Min self delegation must be a number"
) from exception
if delegation < MIN_REQUIRED_DELEGATION:
raise InvalidValidatorError(
@ -353,7 +360,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
)
self._min_self_delegation = delegation
def get_min_self_delegation(self) -> Decimal:
def get_min_self_delegation( self ) -> Decimal:
"""Get validator min self delegation.
Returns
@ -363,7 +370,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._min_self_delegation
def set_max_total_delegation(self, max_delegation):
def set_max_total_delegation( self, max_delegation ):
"""Set validator max total delegation.
Parameters
@ -376,12 +383,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
max_delegation = self._sanitize_input(max_delegation)
max_delegation = self._sanitize_input( max_delegation )
try:
max_delegation = Decimal(max_delegation)
except (TypeError, InvalidOperation) as exception:
max_delegation = Decimal( max_delegation )
except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(
3, "Max total delegation must be a number"
3,
"Max total delegation must be a number"
) from exception
if self._min_self_delegation:
if max_delegation < self._min_self_delegation:
@ -392,11 +400,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
)
else:
raise InvalidValidatorError(
4, "Min self delegation must be set before max total delegation"
4,
"Min self delegation must be set before max total delegation"
)
self._max_total_delegation = max_delegation
def get_max_total_delegation(self) -> Decimal:
def get_max_total_delegation( self ) -> Decimal:
"""Get validator max total delegation.
Returns
@ -406,7 +415,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._max_total_delegation
def set_amount(self, amount):
def set_amount( self, amount ):
"""Set validator initial delegation amount.
Parameters
@ -419,11 +428,14 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
amount = self._sanitize_input(amount)
amount = self._sanitize_input( amount )
try:
amount = Decimal(amount)
except (TypeError, InvalidOperation) as exception:
raise InvalidValidatorError(3, "Amount must be a number") from exception
amount = Decimal( amount )
except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(
3,
"Amount must be a number"
) from exception
if self._min_self_delegation:
if amount < self._min_self_delegation:
raise InvalidValidatorError(
@ -433,7 +445,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
)
else:
raise InvalidValidatorError(
4, "Min self delegation must be set before amount"
4,
"Min self delegation must be set before amount"
)
if self._max_total_delegation:
if amount > self._max_total_delegation:
@ -444,11 +457,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
)
else:
raise InvalidValidatorError(
4, "Max total delegation must be set before amount"
4,
"Max total delegation must be set before amount"
)
self._inital_delegation = amount
def get_amount(self) -> Decimal:
def get_amount( self ) -> Decimal:
"""Get validator initial delegation amount.
Returns
@ -458,7 +472,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._inital_delegation
def set_max_rate(self, rate):
def set_max_rate( self, rate ):
"""Set validator max commission rate.
Parameters
@ -471,16 +485,19 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
rate = self._sanitize_input(rate, True)
rate = self._sanitize_input( rate, True )
try:
rate = Decimal(rate)
except (TypeError, InvalidOperation) as exception:
raise InvalidValidatorError(3, "Max rate must be a number") from exception
rate = Decimal( rate )
except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(
3,
"Max rate must be a number"
) from exception
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:
def get_max_rate( self ) -> Decimal:
"""Get validator max commission rate.
Returns
@ -490,7 +507,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._max_rate
def set_max_change_rate(self, rate):
def set_max_change_rate( self, rate ):
"""Set validator max commission change rate.
Parameters
@ -503,14 +520,18 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
rate = self._sanitize_input(rate, True)
rate = self._sanitize_input( rate, True )
try:
rate = Decimal(rate)
except (TypeError, InvalidOperation) as exception:
raise InvalidValidatorError(3, "Max change rate must be a number") from exception
rate = Decimal( rate )
except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(
3,
"Max change rate must be a number"
) from exception
if rate < 0:
raise InvalidValidatorError(
3, "Max change rate must be greater than or equal to 0"
3,
"Max change rate must be greater than or equal to 0"
)
if self._max_rate:
if rate > self._max_rate:
@ -520,11 +541,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
)
else:
raise InvalidValidatorError(
4, "Max rate must be set before max change rate"
4,
"Max rate must be set before max change rate"
)
self._max_change_rate = rate
def get_max_change_rate(self) -> Decimal:
def get_max_change_rate( self ) -> Decimal:
"""Get validator max commission change rate.
Returns
@ -534,7 +556,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
"""
return self._max_change_rate
def set_rate(self, rate):
def set_rate( self, rate ):
"""Set validator commission rate.
Parameters
@ -547,23 +569,30 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
InvalidValidatorError
If input is invalid
"""
rate = self._sanitize_input(rate, True)
rate = self._sanitize_input( rate, True )
try:
rate = Decimal(rate)
except (TypeError, InvalidOperation) as exception:
raise InvalidValidatorError(3, "Rate must be a number") from exception
rate = Decimal( rate )
except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(
3,
"Rate must be a number"
) from exception
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}"
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:
def get_rate( self ) -> Decimal:
"""Get validator commission rate.
Returns
@ -574,7 +603,9 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
return self._rate
def does_validator_exist(
self, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
self,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool:
"""Check if validator exists on blockchain.
@ -595,12 +626,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
RPCError, RequestsError, RequestsTimeoutError
If unable to get list of validators on chain
"""
all_validators = get_all_validator_addresses(endpoint, timeout)
all_validators = get_all_validator_addresses( endpoint, timeout )
if self._address in all_validators:
return True
return False
def load(self, info):
def load( self, info ):
"""Import validator information.
Parameters
@ -631,32 +662,37 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
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"]:
self.add_bls_key(key)
for key in info[ "bls-public-keys" ]:
self.add_bls_key( key )
self._bls_key_sigs = []
for key in info["bls-key-sigs"]:
self.add_bls_key_sig(key)
for key in info[ "bls-key-sigs" ]:
self.add_bls_key_sig( key )
except KeyError as exception:
raise InvalidValidatorError(3, "Info has missing key") from exception
raise InvalidValidatorError(
3,
"Info has missing key"
) from exception
def load_from_blockchain(
self, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT
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
@ -675,46 +711,54 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
If any error occur getting & importing validator information from the blockchain
"""
try:
if not self.does_validator_exist(endpoint, timeout):
if not self.does_validator_exist( endpoint, timeout ):
raise InvalidValidatorError(
5, f"Validator does not exist on chain according to {endpoint}"
5,
f"Validator does not exist on chain according to {endpoint}"
)
except (RPCError, RequestsError, RequestsTimeoutError) as exception:
except ( RPCError, RequestsError, RequestsTimeoutError ) as exception:
raise InvalidValidatorError(
5, "Error requesting validator information"
5,
"Error requesting validator information"
) from exception
try:
validator_info = get_validator_information(self._address, endpoint, timeout)
except (RPCError, RequestsError, RequestsTimeoutError) as exception:
validator_info = get_validator_information(
self._address,
endpoint,
timeout
)
except ( RPCError, RequestsError, RequestsTimeoutError ) as exception:
raise InvalidValidatorError(
5, "Error requesting validator information"
5,
"Error requesting validator information"
) from exception
# 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"]
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"]
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 exception:
raise InvalidValidatorError(
5, "Error importing validator information from RPC result"
5,
"Error importing validator information from RPC result"
) from exception
def export(self) -> dict:
def export( self ) -> dict:
"""Export validator information as dict.
Returns
@ -760,14 +804,16 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
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
return sign_staking_transaction(info, private_key)
info[ "chainId" ] = chain_id
return sign_staking_transaction( info, private_key )
def sign_edit_validator_transaction( # pylint: disable=too-many-arguments
self,
@ -797,22 +843,24 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
-------------
https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L460
"""
self.set_rate(rate)
self.add_bls_key(bls_key_to_add)
self.remove_bls_key(bls_key_to_remove)
self.set_rate( rate )
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
return sign_staking_transaction(info, private_key)
info[ "chainId" ] = chain_id
return sign_staking_transaction( info, private_key )

@ -2,8 +2,8 @@ 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")
bech32.decode( "one", "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" )

@ -10,57 +10,58 @@ TEMP_DIR = "/tmp/pyhmy-testing/test-cli"
BINARY_FILE_PATH = f"{TEMP_DIR}/bin/cli_test_binary"
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture( scope = "session", autouse = True )
def setup():
shutil.rmtree(TEMP_DIR, ignore_errors=True)
os.makedirs(TEMP_DIR, exist_ok=True)
shutil.rmtree( TEMP_DIR, ignore_errors = True )
os.makedirs( TEMP_DIR, exist_ok = True )
def test_download_cli():
env = cli.download(BINARY_FILE_PATH, replace=False, verbose=False)
cli.environment.update(env)
assert os.path.exists(BINARY_FILE_PATH)
env = cli.download( BINARY_FILE_PATH, replace = False, verbose = False )
cli.environment.update( env )
assert os.path.exists( BINARY_FILE_PATH )
def test_is_valid():
bad_file_path = os.path.realpath(f"{TEMP_DIR}/test_is_valid/bad_hmy")
shutil.rmtree(Path(bad_file_path).parent, ignore_errors=True)
os.makedirs(Path(bad_file_path).parent, exist_ok=True)
Path(bad_file_path).touch()
assert os.path.exists(BINARY_FILE_PATH), "harmony cli did not download"
assert os.path.exists(bad_file_path), "did not create bad binary"
assert cli.is_valid_binary(BINARY_FILE_PATH)
assert not cli.is_valid_binary(bad_file_path)
bad_file_path = os.path.realpath( f"{TEMP_DIR}/test_is_valid/bad_hmy" )
shutil.rmtree( Path( bad_file_path ).parent, ignore_errors = True )
os.makedirs( Path( bad_file_path ).parent, exist_ok = True )
Path( bad_file_path ).touch()
assert os.path.exists( BINARY_FILE_PATH ), "harmony cli did not download"
assert os.path.exists( bad_file_path ), "did not create bad binary"
assert cli.is_valid_binary( BINARY_FILE_PATH )
assert not cli.is_valid_binary( bad_file_path )
def test_bad_bin_set():
bad_file_path = os.path.realpath(f"{TEMP_DIR}/test_bad_bin_set/hmy")
shutil.rmtree(Path(bad_file_path).parent, ignore_errors=True)
os.makedirs(Path(bad_file_path).parent, exist_ok=True)
Path(bad_file_path).touch()
is_set = cli.set_binary(bad_file_path)
bad_file_path = os.path.realpath( f"{TEMP_DIR}/test_bad_bin_set/hmy" )
shutil.rmtree( Path( bad_file_path ).parent, ignore_errors = True )
os.makedirs( Path( bad_file_path ).parent, exist_ok = True )
Path( bad_file_path ).touch()
is_set = cli.set_binary( bad_file_path )
assert not is_set
assert cli.get_binary_path() != bad_file_path
def test_bin_set():
cli.set_binary(BINARY_FILE_PATH)
cli.set_binary( BINARY_FILE_PATH )
cli_binary_path = cli.get_binary_path()
assert os.path.realpath(cli_binary_path) == os.path.realpath(BINARY_FILE_PATH)
assert os.path.realpath( cli_binary_path
) == os.path.realpath( BINARY_FILE_PATH )
def test_update_keystore():
cli.single_call("hmy keys add test1")
cli.single_call( "hmy keys add test1" )
addrs = cli.get_accounts_keystore()
assert "test1" in addrs.keys()
check_addr = addrs["test1"]
accounts_list = cli.get_accounts(check_addr)
check_acc = accounts_list[0]
check_addr = addrs[ "test1" ]
accounts_list = cli.get_accounts( check_addr )
check_acc = accounts_list[ 0 ]
assert check_acc == "test1"
raw_cli_keys_list_print = cli.single_call("hmy keys list", timeout=2)
raw_cli_keys_list_print = cli.single_call( "hmy keys list", timeout = 2 )
assert check_addr in raw_cli_keys_list_print
assert check_acc in raw_cli_keys_list_print
assert addrs[check_acc] == check_addr
cli.remove_address(check_addr)
assert addrs[ check_acc ] == check_addr
cli.remove_address( check_addr )
assert check_addr not in addrs.values()
assert "test1" not in addrs.keys()

@ -6,19 +6,19 @@ from pyhmy import logging
def test_basic_logger():
if os.path.exists(f"{os.getcwd()}/logs/pytest.log"):
os.remove(f"{os.getcwd()}/logs/pytest.log")
logger = logging.ControlledLogger("pytest", "logs/")
assert os.path.exists(f"{os.getcwd()}/logs/pytest.log")
logger.info("test info")
logger.debug("test debug")
logger.error("test error")
logger.warning("test warning")
with open(f"{os.getcwd()}/logs/pytest.log", "r") as f:
if os.path.exists( f"{os.getcwd()}/logs/pytest.log" ):
os.remove( f"{os.getcwd()}/logs/pytest.log" )
logger = logging.ControlledLogger( "pytest", "logs/" )
assert os.path.exists( f"{os.getcwd()}/logs/pytest.log" )
logger.info( "test info" )
logger.debug( "test debug" )
logger.error( "test error" )
logger.warning( "test warning" )
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:

@ -4,28 +4,28 @@ from pyhmy import numbers
def test_convert_atto_to_one():
a = numbers.convert_atto_to_one(1e18)
assert Decimal(1) == a
a = numbers.convert_atto_to_one( 1e18 )
assert Decimal( 1 ) == a
b = numbers.convert_atto_to_one(1e18 + 0.6)
assert Decimal(1) == b
b = numbers.convert_atto_to_one( 1e18 + 0.6 )
assert Decimal( 1 ) == b
c = numbers.convert_atto_to_one("1" + ("0" * 18))
assert Decimal(1) == c
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
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
a = numbers.convert_one_to_atto( 1e-18 )
assert Decimal( 1 ) == a
b = numbers.convert_one_to_atto(1.5)
assert Decimal(1.5e18) == b
b = numbers.convert_one_to_atto( 1.5 )
assert Decimal( 1.5e18 ) == b
c = numbers.convert_one_to_atto("1")
assert Decimal(1e18) == c
c = numbers.convert_one_to_atto( "1" )
assert Decimal( 1e18 ) == c
d = numbers.convert_one_to_atto(Decimal(1))
assert Decimal(1e18) == d
d = numbers.convert_one_to_atto( Decimal( 1 ) )
assert Decimal( 1e18 ) == d

@ -7,56 +7,70 @@ import requests
from pyhmy.rpc import exceptions, request
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture( scope = "session", autouse = True )
def setup():
endpoint = "http://localhost:9500"
timeout = 30
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,
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)
pytest.skip(
"can not connect to local blockchain",
allow_module_level = True
)
def test_request_connection_error():
# Find available port
s = socket.socket()
s.bind(("localhost", 0))
port = s.getsockname()[1]
s.bind( ( "localhost", 0 ) )
port = s.getsockname()[ 1 ]
s.close()
if port == 0:
pytest.skip("could not find available port")
pytest.skip( "could not find available port" )
bad_endpoint = f"http://localhost:{port}"
bad_request = None
try:
bad_request = request.rpc_request(
"hmyv2_getNodeMetadata", endpoint=bad_endpoint
"hmyv2_getNodeMetadata",
endpoint = bad_endpoint
)
except Exception as e:
assert isinstance(e, exceptions.RequestsError)
assert isinstance( e, exceptions.RequestsError )
assert bad_request is None
def test_request_rpc_error():
error_request = None
try:
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)
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:
assert isinstance(e, exceptions.RPCError)
assert isinstance( e, exceptions.RPCError )
assert error_request is None
@ -65,33 +79,40 @@ def test_rpc_request():
timeout = 30
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,
headers = headers,
data = json.dumps( payload ),
timeout = timeout,
allow_redirects = True,
)
except:
pytest.skip("can not connect to local blockchain")
pytest.skip( "can not connect to local blockchain" )
assert response is not None
resp = None
try:
resp = json.loads(response.content)
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)
rpc_response = request.rpc_request( method, params, endpoint, timeout )
except exceptions.RPCError as e:
assert "error" in resp

@ -11,7 +11,9 @@ import requests
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",
@ -35,35 +37,37 @@ stx_hashes = [
"0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968",
"0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f",
]
assert len(txs) == len(tx_hashes), "Mismatch in tx and tx_hash count"
assert len(stxs) == len(stx_hashes), "Mismatch in stx and stx_hash count"
assert len( txs ) == len( tx_hashes ), "Mismatch in tx and tx_hash count"
assert len( stxs ) == len( stx_hashes ), "Mismatch in stx and stx_hash count"
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture( scope = "session", autouse = True )
def setup_blockchain():
# return
metadata = _check_connection()
_check_staking_epoch(metadata)
_check_staking_epoch( metadata )
for i in range(len(txs)):
tx = txs[i]
tx_hash = tx_hashes[i]
_send_transaction(tx, endpoint)
if not _wait_for_transaction_confirmed(tx_hash, endpoint):
for i in range( len( txs ) ):
tx = txs[ i ]
tx_hash = tx_hashes[ i ]
_send_transaction( tx, endpoint )
if not _wait_for_transaction_confirmed( tx_hash, endpoint ):
pytest.skip(
"Could not confirm initial transaction #{} on chain".format(i),
allow_module_level=True,
"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):
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,
"Could not confirm initial staking transaction #{} on chain"
.format( i ),
allow_module_level = True,
)
@ -78,31 +82,31 @@ def _check_connection():
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
headers = headers,
data = json.dumps( payload ),
timeout = timeout,
allow_redirects = True,
)
metadata = json.loads(response.content)
metadata = json.loads( response.content )
if "error" in metadata:
pytest.skip(
f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}",
allow_module_level=True,
allow_module_level = True,
)
if "chain-config" not in metadata["result"]:
if "chain-config" not in metadata[ "result" ]:
pytest.skip(
"Chain config not found in hmyv2_getNodeMetadata reply",
allow_module_level=True,
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,
allow_module_level = True,
)
def _check_staking_epoch(metadata):
def _check_staking_epoch( metadata ):
latest_header = None
try:
payload = {
@ -114,158 +118,164 @@ def _check_staking_epoch(metadata):
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
headers = headers,
data = json.dumps( payload ),
timeout = timeout,
allow_redirects = True,
)
latest_header = json.loads(response.content)
latest_header = json.loads( response.content )
if "error" in latest_header:
pytest.skip(
f"Error in hmyv2_latestHeader reply: {latest_header['error']}",
allow_module_level=True,
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,
allow_module_level = True,
)
def _send_transaction(raw_tx, endpoint):
def _send_transaction( raw_tx, endpoint ):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": "hmyv2_sendRawTransaction",
"params": [raw_tx],
"params": [ raw_tx ],
}
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
headers = headers,
data = json.dumps( payload ),
timeout = timeout,
allow_redirects = True,
)
tx = json.loads(response.content)
tx = json.loads( response.content )
if "error" in tx:
pytest.skip(
f"Error in hmyv2_sendRawTransaction reply: {tx['error']}",
allow_module_level=True,
allow_module_level = True,
)
except Exception as e:
pytest.skip(
"Failed to get hmyv2_sendRawTransaction reply", allow_module_level=True
"Failed to get hmyv2_sendRawTransaction reply",
allow_module_level = True
)
def _check_transaction(tx_hash, endpoint):
def _check_transaction( tx_hash, endpoint ):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": "hmyv2_getTransactionByHash",
"params": [tx_hash],
"params": [ tx_hash ],
}
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
headers = headers,
data = json.dumps( payload ),
timeout = timeout,
allow_redirects = True,
)
tx_data = json.loads(response.content)
tx_data = json.loads( response.content )
return tx_data
except Exception as e:
pytest.skip(
"Failed to get hmyv2_getTransactionByHash reply", allow_module_level=True
"Failed to get hmyv2_getTransactionByHash reply",
allow_module_level = True
)
def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout=30):
def _wait_for_transaction_confirmed( tx_hash, endpoint, timeout = 30 ):
start_time = time.time()
while (time.time() - start_time) <= timeout:
tx_data = _check_transaction(tx_hash, endpoint)
while ( time.time() - start_time ) <= timeout:
tx_data = _check_transaction( tx_hash, endpoint )
if tx_data is not None:
block_hash = tx_data["result"].get("blockHash", "0x00")
unique_chars = "".join(set(list(block_hash[2:])))
block_hash = tx_data[ "result" ].get( "blockHash", "0x00" )
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) )
if unique_chars != "0":
return True
time.sleep(random.uniform(0.2, 0.5))
time.sleep( random.uniform( 0.2, 0.5 ) )
return False
def _send_staking_transaction(raw_tx, endpoint=endpoint):
def _send_staking_transaction( raw_tx, endpoint = endpoint ):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": "hmyv2_sendRawStakingTransaction",
"params": [raw_tx],
"params": [ raw_tx ],
}
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
headers = headers,
data = json.dumps( payload ),
timeout = timeout,
allow_redirects = True,
)
staking_tx = json.loads(response.content)
staking_tx = json.loads( response.content )
if "error" in staking_tx:
pytest.skip(
f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}",
allow_module_level=True,
allow_module_level = True,
)
except Exception as e:
pytest.skip(
"Failed to get hmyv2_sendRawStakingTransaction reply",
allow_module_level=True,
allow_module_level = True,
)
def _check_staking_transaction(stx_hash, endpoint=endpoint):
def _check_staking_transaction( stx_hash, endpoint = endpoint ):
try:
payload = {
"id": "1",
"jsonrpc": "2.0",
"method": "hmyv2_getStakingTransactionByHash",
"params": [stx_hash],
"params": [ stx_hash ],
}
response = requests.request(
"POST",
endpoint,
headers=headers,
data=json.dumps(payload),
timeout=timeout,
allow_redirects=True,
headers = headers,
data = json.dumps( payload ),
timeout = timeout,
allow_redirects = True,
)
stx_data = json.loads(response.content)
stx_data = json.loads( response.content )
return stx_data
except Exception as e:
pytest.skip(
"Failed to get hmyv2_getStakingTransactionByHash reply",
allow_module_level=True,
allow_module_level = True,
)
def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout=30):
def _wait_for_staking_transaction_confirmed( tx_hash, endpoint, timeout = 30 ):
answer = False
start_time = time.time()
while (time.time() - start_time) <= timeout:
tx_data = _check_staking_transaction(tx_hash, endpoint)
while ( time.time() - start_time ) <= timeout:
tx_data = _check_staking_transaction( tx_hash, endpoint )
if tx_data is not None:
block_hash = tx_data["result"].get("blockHash", "0x00")
unique_chars = "".join(set(list(block_hash[2:])))
block_hash = tx_data[ "result" ].get( "blockHash", "0x00" )
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) )
if unique_chars != "0":
answer = True
time.sleep(random.uniform(0.2, 0.5))
time.sleep( random.uniform( 0.2, 0.5 ) )
return answer

@ -5,7 +5,6 @@ from pyhmy import account
from pyhmy.rpc import exceptions
explorer_endpoint = "http://localhost:9700"
endpoint_shard_one = "http://localhost:9502"
local_test_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
@ -15,122 +14,142 @@ test_block_number = 1
fake_shard = "http://example.com"
def _test_account_rpc(fn, *args, **kwargs):
if not callable(fn):
pytest.fail(f"Invalid function: {fn}")
def _test_account_rpc( fn, *args, **kwargs ):
if not callable( fn ):
pytest.fail( f"Invalid function: {fn}" )
try:
response = fn(*args, **kwargs)
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_balance(setup_blockchain):
balance = _test_account_rpc(account.get_balance, local_test_address)
assert isinstance(balance, int)
def test_get_balance( setup_blockchain ):
balance = _test_account_rpc( account.get_balance, local_test_address )
assert isinstance( balance, int )
assert balance > 0
def test_get_balance_by_block(setup_blockchain):
def test_get_balance_by_block( setup_blockchain ):
balance = _test_account_rpc(
account.get_balance_by_block, local_test_address, genesis_block_number
account.get_balance_by_block,
local_test_address,
genesis_block_number
)
assert isinstance(balance, int)
assert isinstance( balance, int )
assert balance > 0
def test_get_account_nonce(setup_blockchain):
def test_get_account_nonce( setup_blockchain ):
true_nonce = _test_account_rpc(
account.get_account_nonce,
local_test_address,
test_block_number,
endpoint=endpoint_shard_one,
endpoint = endpoint_shard_one,
)
assert isinstance(true_nonce, int)
assert isinstance( true_nonce, int )
def test_get_transaction_history(setup_blockchain):
def test_get_transaction_history( setup_blockchain ):
tx_history = _test_account_rpc(
account.get_transaction_history, local_test_address, endpoint=explorer_endpoint
account.get_transaction_history,
local_test_address,
endpoint = explorer_endpoint
)
assert isinstance(tx_history, list)
assert len(tx_history) >= 0
assert isinstance( tx_history, list )
assert len( tx_history ) >= 0
def test_get_staking_transaction_history(setup_blockchain):
def test_get_staking_transaction_history( setup_blockchain ):
staking_tx_history = _test_account_rpc(
account.get_staking_transaction_history,
test_validator_address,
endpoint=explorer_endpoint,
endpoint = explorer_endpoint,
)
assert isinstance(staking_tx_history, list)
assert len(staking_tx_history) > 0
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_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)
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):
def test_get_transaction_count( setup_blockchain ):
tx_count = _test_account_rpc(
account.get_transaction_count, local_test_address, "latest", explorer_endpoint
account.get_transaction_count,
local_test_address,
"latest",
explorer_endpoint
)
assert isinstance(tx_count, int)
assert isinstance( tx_count, int )
assert tx_count > 0
def test_get_transactions_count(setup_blockchain):
def test_get_transactions_count( setup_blockchain ):
tx_count = _test_account_rpc(
account.get_transactions_count, local_test_address, "ALL", explorer_endpoint
account.get_transactions_count,
local_test_address,
"ALL",
explorer_endpoint
)
def test_get_staking_transactions_count(setup_blockchain):
def test_get_staking_transactions_count( setup_blockchain ):
tx_count = _test_account_rpc(
account.get_staking_transactions_count,
local_test_address,
"ALL",
explorer_endpoint,
)
assert isinstance(tx_count, int)
assert isinstance( tx_count, int )
def test_errors():
with pytest.raises(exceptions.RPCError):
account.get_balance("", fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_balance_by_block("", 1, fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_account_nonce("", 1, fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_transaction_count("", 1, fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_transactions_count("", 1, fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_transactions_count("", "ALL", fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_transaction_history("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_staking_transaction_history("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_balance_on_all_shards("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
account.get_total_balance("", endpoint=fake_shard)
with pytest.raises( exceptions.RPCError ):
account.get_balance( "", fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_balance_by_block( "", 1, fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_account_nonce( "", 1, fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_transaction_count( "", 1, fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_transactions_count( "", 1, fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_transactions_count( "", "ALL", fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_transaction_history( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_staking_transaction_history( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_balance_on_all_shards( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
account.get_total_balance( "", endpoint = fake_shard )

@ -5,7 +5,6 @@ from pyhmy import blockchain
from pyhmy.rpc import exceptions
test_epoch_number = 0
genesis_block_number = 0
test_block_number = 1
@ -14,323 +13,357 @@ fake_shard = "http://example.com"
address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
def _test_blockchain_rpc(fn, *args, **kwargs):
if not callable(fn):
pytest.fail(f"Invalid function: {fn}")
def _test_blockchain_rpc( fn, *args, **kwargs ):
if not callable( fn ):
pytest.fail( f"Invalid function: {fn}" )
try:
response = fn(*args, **kwargs)
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_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_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)
def test_get_leader_address( setup_blockchain ):
leader = _test_blockchain_rpc( blockchain.get_leader_address )
assert isinstance( leader, str )
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_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 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 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_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_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_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):
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)
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"]
test_block_hash = block[ "hash" ]
def test_get_block_by_hash(setup_blockchain):
def test_get_block_by_hash( setup_blockchain ):
if not test_block_hash:
pytest.skip("Failed to get reference block hash")
block = _test_blockchain_rpc(blockchain.get_block_by_hash, test_block_hash)
assert isinstance(block, dict)
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):
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
blockchain.get_block_transaction_count_by_number,
test_block_number
)
assert isinstance(tx_count, int)
assert isinstance( tx_count, int )
def test_get_block_transaction_count_by_hash(setup_blockchain):
def test_get_block_transaction_count_by_hash( setup_blockchain ):
if not test_block_hash:
pytest.skip("Failed to get reference block hash")
pytest.skip( "Failed to get reference block hash" )
tx_count = _test_blockchain_rpc(
blockchain.get_block_transaction_count_by_hash, test_block_hash
blockchain.get_block_transaction_count_by_hash,
test_block_hash
)
assert isinstance(tx_count, int)
assert isinstance( tx_count, int )
def test_get_blocks(setup_blockchain):
def test_get_blocks( setup_blockchain ):
blocks = _test_blockchain_rpc(
blockchain.get_blocks, genesis_block_number, test_block_number
blockchain.get_blocks,
genesis_block_number,
test_block_number
)
assert isinstance(blocks, list)
assert len(blocks) == (test_block_number - genesis_block_number + 1)
assert isinstance( blocks, list )
assert len( blocks ) == ( test_block_number - genesis_block_number + 1 )
def test_get_block_signers(setup_blockchain):
def test_get_block_signers( setup_blockchain ):
block_signers = _test_blockchain_rpc(
blockchain.get_block_signers, test_block_number
blockchain.get_block_signers,
test_block_number
)
assert isinstance(block_signers, list)
assert len(block_signers) > 0
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)
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 len( validators[ "validators" ] ) > 0
def test_get_shard(setup_blockchain):
shard = _test_blockchain_rpc(blockchain.get_shard)
assert isinstance(shard, int)
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_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_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):
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)
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_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_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_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_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_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)
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_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_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_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_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_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_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_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):
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
blockchain.get_block_staking_transaction_count_by_number,
test_block_number
)
assert isinstance(tx_count, int)
assert isinstance( tx_count, int )
def test_get_block_staking_transaction_count_by_hash(setup_blockchain):
def test_get_block_staking_transaction_count_by_hash( setup_blockchain ):
if not test_block_hash:
pytest.skip("Failed to get reference block hash")
pytest.skip( "Failed to get reference block hash" )
tx_count = _test_blockchain_rpc(
blockchain.get_block_staking_transaction_count_by_hash, test_block_hash
blockchain.get_block_staking_transaction_count_by_hash,
test_block_hash
)
assert isinstance(tx_count, int)
assert isinstance( tx_count, int )
def test_is_block_signer(setup_blockchain):
def test_is_block_signer( setup_blockchain ):
is_signer = _test_blockchain_rpc(
blockchain.is_block_signer, test_block_number, address
blockchain.is_block_signer,
test_block_number,
address
)
assert isinstance(is_signer, bool)
assert isinstance( is_signer, bool )
def test_get_signed_blocks(setup_blockchain):
signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address)
assert isinstance(signed_blocks, int)
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_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_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)
with pytest.raises(exceptions.RPCError):
blockchain.get_node_metadata(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_peer_info(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.protocol_version(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_shard(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_staking_epoch(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_prestaking_epoch(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_sharding_structure(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_leader_address(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.is_last_block(0, fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.epoch_last_block(0, fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_circulating_supply(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_total_supply(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_block_number(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_current_epoch(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_last_cross_links(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_gas_price(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_num_peers(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_version(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_latest_header(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_header_by_number(0, fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_latest_chain_headers(fake_shard)
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)
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)
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)
with pytest.raises(exceptions.RPCError):
blockchain.get_blocks(0, 1, endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_block_signers(0, fake_shard)
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)
with pytest.raises(exceptions.RPCError):
blockchain.get_signed_blocks("", fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_validators(1, fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.get_validator_keys(0, fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.in_sync(fake_shard)
with pytest.raises(exceptions.RPCError):
blockchain.beacon_in_sync(fake_shard)
with pytest.raises( exceptions.RPCError ):
blockchain.chain_id( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_node_metadata( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_peer_info( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.protocol_version( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_shard( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_staking_epoch( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_prestaking_epoch( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_sharding_structure( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_leader_address( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.is_last_block( 0, fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.epoch_last_block( 0, fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_circulating_supply( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_total_supply( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_block_number( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_current_epoch( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_last_cross_links( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_gas_price( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_num_peers( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_version( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_latest_header( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_header_by_number( 0, fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_latest_chain_headers( fake_shard )
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 )
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 )
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 )
with pytest.raises( exceptions.RPCError ):
blockchain.get_blocks( 0, 1, endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_block_signers( 0, fake_shard )
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 )
with pytest.raises( exceptions.RPCError ):
blockchain.get_signed_blocks( "", fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_validators( 1, fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.get_validator_keys( 0, fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.in_sync( fake_shard )
with pytest.raises( exceptions.RPCError ):
blockchain.beacon_in_sync( fake_shard )

@ -12,69 +12,75 @@ contract_address = None
fake_shard = "http://example.com"
def _test_contract_rpc(fn, *args, **kwargs):
if not callable(fn):
pytest.fail(f"Invalid function: {fn}")
def _test_contract_rpc( fn, *args, **kwargs ):
if not callable( fn ):
pytest.fail( f"Invalid function: {fn}" )
try:
response = fn(*args, **kwargs)
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):
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.get_contract_address_from_hash,
contract_tx_hash
)
assert isinstance(contract_address, str)
assert isinstance( contract_address, str )
def test_call(setup_blockchain):
def test_call( setup_blockchain ):
if not contract_address:
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):
def test_estimate_gas( setup_blockchain ):
if not contract_address:
pytest.skip("Contract address not loaded yet")
gas = _test_contract_rpc(contract.estimate_gas, contract_address)
assert isinstance(gas, int)
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):
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):
def test_get_storage_at( setup_blockchain ):
if not contract_address:
pytest.skip("Contract address not loaded yet")
pytest.skip( "Contract address not loaded yet" )
storage = _test_contract_rpc(
contract.get_storage_at, contract_address, "0x0", "latest"
contract.get_storage_at,
contract_address,
"0x0",
"latest"
)
assert isinstance(storage, str) and storage.startswith("0x")
assert isinstance( storage, str ) and storage.startswith( "0x" )
def test_errors():
with pytest.raises(exceptions.RPCError):
contract.get_contract_address_from_hash("", fake_shard)
with pytest.raises(exceptions.RPCError):
contract.call("", "", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
contract.estimate_gas("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
contract.get_code("", "latest", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
contract.get_storage_at("", 1, "latest", endpoint=fake_shard)
with pytest.raises( exceptions.RPCError ):
contract.get_contract_address_from_hash( "", fake_shard )
with pytest.raises( exceptions.RPCError ):
contract.call( "", "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
contract.estimate_gas( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
contract.get_code( "", "latest", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
contract.get_storage_at( "", 1, "latest", endpoint = fake_shard )

@ -1,5 +1,4 @@
from pyhmy import signing
"""
Test signature source (node.js)
import { Transaction, RLPSign, TxStatus } from '@harmony-js/transaction';
@ -43,8 +42,8 @@ def test_eth_transaction():
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1"
signed_tx.rawTransaction.hex() ==
"0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1"
)
@ -96,6 +95,6 @@ def test_hmy_transaction():
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d"
signed_tx.rawTransaction.hex() ==
"0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d"
)

@ -5,222 +5,244 @@ from pyhmy import staking
from pyhmy.rpc import exceptions
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}")
def _test_staking_rpc( fn, *args, **kwargs ):
if not callable( fn ):
pytest.fail( f"Invalid function: {fn}" )
try:
response = fn(*args, **kwargs)
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
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_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_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):
def test_get_delegations_by_delegator( setup_blockchain ):
delegations = _test_staking_rpc(
staking.get_delegations_by_delegator, test_validator_address
staking.get_delegations_by_delegator,
test_validator_address
)
assert isinstance(delegations, list)
assert len(delegations) > 0
assert isinstance( delegations, list )
assert len( delegations ) > 0
def test_get_delegations_by_validator(setup_blockchain):
def test_get_delegations_by_validator( setup_blockchain ):
delegations = _test_staking_rpc(
staking.get_delegations_by_validator, test_validator_address
staking.get_delegations_by_validator,
test_validator_address
)
assert isinstance(delegations, list)
assert len(delegations) > 0
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_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_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_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_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):
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,
endpoint = explorer_endpoint,
)
assert isinstance(info, dict)
assert isinstance( info, dict )
def test_get_validator_information_by_block(setup_blockchain):
def test_get_validator_information_by_block( setup_blockchain ):
# Apparently validator information not created until block after create-validator transaction is accepted, so +1 block
info = _test_staking_rpc(
staking.get_all_validator_information_by_block_number,
"latest",
endpoint=explorer_endpoint,
endpoint = explorer_endpoint,
)
assert isinstance(info, list)
assert isinstance( info, list )
def test_get_delegations_by_delegator_by_block(setup_blockchain):
def test_get_delegations_by_delegator_by_block( setup_blockchain ):
delegations = _test_staking_rpc(
staking.get_delegations_by_delegator_by_block_number,
test_validator_address,
"latest",
endpoint=explorer_endpoint,
endpoint = explorer_endpoint,
)
assert isinstance(delegations, list)
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_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
def test_get_validators( setup_blockchain ):
validators = _test_staking_rpc( staking.get_validators, 2 )
assert isinstance( validators, dict )
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_keys( setup_blockchain ):
validators = _test_staking_rpc( staking.get_validator_keys, 2 )
assert isinstance( validators, list )
def test_get_validator_self_delegation(setup_blockchain):
def test_get_validator_self_delegation( setup_blockchain ):
self_delegation = _test_staking_rpc(
staking.get_validator_self_delegation, test_validator_address
staking.get_validator_self_delegation,
test_validator_address
)
assert isinstance(self_delegation, int)
assert isinstance( self_delegation, int )
assert self_delegation > 0
def test_get_validator_total_delegation(setup_blockchain):
def test_get_validator_total_delegation( setup_blockchain ):
total_delegation = _test_staking_rpc(
staking.get_validator_total_delegation, test_validator_address
staking.get_validator_total_delegation,
test_validator_address
)
assert isinstance(total_delegation, int)
assert isinstance( total_delegation, int )
assert total_delegation > 0
def test_get_all_delegation_information(setup_blockchain):
def test_get_all_delegation_information( setup_blockchain ):
delegation_information = _test_staking_rpc(
staking.get_all_delegation_information, 0
staking.get_all_delegation_information,
0
)
assert isinstance(delegation_information, list)
assert len(delegation_information) > 0
assert isinstance( delegation_information, list )
assert len( delegation_information ) > 0
def test_get_delegation_by_delegator_and_validator(setup_blockchain):
def test_get_delegation_by_delegator_and_validator( setup_blockchain ):
delegation_information = _test_staking_rpc(
staking.get_delegation_by_delegator_and_validator,
test_validator_address,
test_validator_address,
)
assert isinstance(delegation_information, dict)
assert isinstance( delegation_information, dict )
def test_get_available_redelegation_balance(setup_blockchain):
def test_get_available_redelegation_balance( setup_blockchain ):
redelgation_balance = _test_staking_rpc(
staking.get_available_redelegation_balance, test_validator_address
staking.get_available_redelegation_balance,
test_validator_address
)
assert isinstance(redelgation_balance, int)
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)
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"
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)
with pytest.raises(exceptions.RPCError):
staking.get_elected_validator_addresses(fake_shard)
with pytest.raises(exceptions.RPCError):
staking.get_validators(1, fake_shard)
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)
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)
with pytest.raises(exceptions.RPCError):
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)
with pytest.raises(exceptions.RPCError):
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)
with pytest.raises(exceptions.RPCError):
staking.get_available_redelegation_balance("", fake_shard)
with pytest.raises(exceptions.RPCError):
staking.get_delegations_by_validator("", fake_shard)
with pytest.raises(exceptions.RPCError):
staking.get_current_utility_metrics(fake_shard)
with pytest.raises(exceptions.RPCError):
staking.get_staking_network_info(fake_shard)
with pytest.raises(exceptions.RPCError):
staking.get_super_committees(fake_shard)
with pytest.raises(exceptions.RPCError):
staking.get_total_staking(fake_shard)
with pytest.raises(exceptions.RPCError):
staking.get_raw_median_stake_snapshot(fake_shard)
with pytest.raises( exceptions.RPCError ):
staking.get_all_validator_addresses( fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_validator_information( "", fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_elected_validator_addresses( fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_validators( 1, fake_shard )
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 )
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 )
with pytest.raises( exceptions.RPCError ):
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 )
with pytest.raises( exceptions.RPCError ):
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 )
with pytest.raises( exceptions.RPCError ):
staking.get_available_redelegation_balance( "", fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_delegations_by_validator( "", fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_current_utility_metrics( fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_staking_network_info( fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_super_committees( fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_total_staking( fake_shard )
with pytest.raises( exceptions.RPCError ):
staking.get_raw_median_stake_snapshot( fake_shard )

@ -5,7 +5,6 @@ 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
# staking transactions without a chain id have been omitted as well, since the node does not accept them anyway
"""
let stakingTx
let stakeMsg3: CollectRewards = new CollectRewards(
@ -33,7 +32,6 @@ console.log(signed)
# }
# signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48')
# assert signed_tx.rawTransaction.hex() == '0xf85a04d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5823a0490e4ceb747563ba40da3e0db8a65133cf6f6ae4c48a24866cd6aa1f0d6c2414a06dbd51a67b35b5685e7b7420cba26e63b0e7d3c696fc6cb69d48e54fcad280e9'
"""
let stakingTx
let stakeMsg3: CollectRewards = new CollectRewards(
@ -67,8 +65,8 @@ def test_collect_rewards_chain_id():
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099"
signed_tx.rawTransaction.hex() ==
"0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099"
)
@ -100,7 +98,7 @@ def test_delegate():
"validatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9",
"amount": 5,
"nonce": 2,
"gasPrice": int(convert_one_to_atto(1)),
"gasPrice": int( convert_one_to_atto( 1 ) ),
"gasLimit": 100,
"chainId": 2,
}
@ -109,6 +107,6 @@ def test_delegate():
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
)
assert (
signed_tx.rawTransaction.hex()
== "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6"
signed_tx.rawTransaction.hex() ==
"0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6"
)

@ -4,7 +4,6 @@ from pyhmy import transaction
from pyhmy.rpc import exceptions
endpoint = "http://localhost:9500"
endpoint_shard_one = "http://localhost:9502"
fake_shard = "http://example.com"
@ -30,212 +29,238 @@ raw_stx = "0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f446
raw_stx_hash = "0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629"
def _test_transaction_rpc(fn, *args, **kwargs):
if not callable(fn):
pytest.fail(f"Invalid function: {fn}")
def _test_transaction_rpc( fn, *args, **kwargs ):
if not callable( fn ):
pytest.fail( f"Invalid function: {fn}" )
try:
response = fn(*args, **kwargs)
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_pending_transactions( setup_blockchain ):
pool = _test_transaction_rpc( transaction.get_pending_transactions )
assert isinstance( pool, list )
def test_get_transaction_by_hash(setup_blockchain):
def test_get_transaction_by_hash( setup_blockchain ):
tx = _test_transaction_rpc(
transaction.get_transaction_by_hash, tx_hash, endpoint=endpoint
transaction.get_transaction_by_hash,
tx_hash,
endpoint = endpoint
)
assert tx
assert isinstance(tx, dict)
assert isinstance( tx, dict )
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):
def test_get_transaction_by_block_hash_and_index( setup_blockchain ):
if not tx_block_hash:
pytest.skip("Failed to get reference block hash")
pytest.skip( "Failed to get reference block hash" )
tx = _test_transaction_rpc(
transaction.get_transaction_by_block_hash_and_index,
tx_block_hash,
tx_index,
endpoint=endpoint,
endpoint = endpoint,
)
assert tx
assert isinstance(tx, dict)
assert isinstance( tx, dict )
def test_get_transaction_by_block_number_and_index(setup_blockchain):
def test_get_transaction_by_block_number_and_index( setup_blockchain ):
if not tx_block_num:
pytest.skip("Failed to get reference block num")
pytest.skip( "Failed to get reference block num" )
tx = _test_transaction_rpc(
transaction.get_transaction_by_block_number_and_index,
tx_block_num,
tx_index,
endpoint=endpoint,
endpoint = endpoint,
)
assert tx
assert isinstance(tx, dict)
assert isinstance( tx, dict )
def test_get_transaction_receipt(setup_blockchain):
def test_get_transaction_receipt( setup_blockchain ):
tx_receipt = _test_transaction_rpc(
transaction.get_transaction_receipt, tx_hash, endpoint=endpoint
transaction.get_transaction_receipt,
tx_hash,
endpoint = endpoint
)
assert tx_receipt
assert isinstance(tx_receipt, dict)
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_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):
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
transaction.send_and_confirm_raw_transaction,
raw_tx
)
assert isinstance(test_tx, dict)
assert test_tx["hash"] == raw_tx_hash
assert isinstance( test_tx, dict )
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_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):
def test_get_cx_receipt_by_hash( setup_blockchain ):
cx = _test_transaction_rpc(
transaction.get_cx_receipt_by_hash, cx_hash, endpoint_shard_one
transaction.get_cx_receipt_by_hash,
cx_hash,
endpoint_shard_one
)
assert cx
assert isinstance(cx, dict)
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)
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):
def test_get_staking_transaction_by_hash( setup_blockchain ):
staking_tx = _test_transaction_rpc(
transaction.get_staking_transaction_by_hash, stx_hash
transaction.get_staking_transaction_by_hash,
stx_hash
)
assert staking_tx
assert isinstance(staking_tx, dict)
assert isinstance( staking_tx, dict )
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):
def test_get_transaction_by_block_hash_and_index( setup_blockchain ):
if not stx_block_hash:
pytest.skip("Failed to get reference block hash")
pytest.skip( "Failed to get reference block hash" )
stx = _test_transaction_rpc(
transaction.get_staking_transaction_by_block_hash_and_index,
stx_block_hash,
stx_index,
)
assert stx
assert isinstance(stx, dict)
assert isinstance( stx, dict )
def test_get_transaction_by_block_number_and_index(setup_blockchain):
def test_get_transaction_by_block_number_and_index( setup_blockchain ):
if not stx_block_num:
pytest.skip("Failed to get reference block num")
pytest.skip( "Failed to get reference block num" )
stx = _test_transaction_rpc(
transaction.get_staking_transaction_by_block_number_and_index,
stx_block_num,
stx_index,
)
assert stx
assert isinstance(stx, dict)
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_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):
def test_send_raw_staking_transaction( setup_blockchain ):
test_stx = _test_transaction_rpc(
transaction.send_and_confirm_raw_staking_transaction, raw_stx, endpoint=endpoint
transaction.send_and_confirm_raw_staking_transaction,
raw_stx,
endpoint = endpoint
)
assert isinstance(test_stx, dict)
assert test_stx["hash"] == raw_stx_hash
assert isinstance( test_stx, dict )
assert test_stx[ "hash" ] == raw_stx_hash
def test_get_pool_stats(setup_blockchain):
def test_get_pool_stats( setup_blockchain ):
test_pool_stats = _test_transaction_rpc(
transaction.get_pool_stats, endpoint=endpoint
transaction.get_pool_stats,
endpoint = endpoint
)
assert isinstance(test_pool_stats, dict)
assert isinstance( test_pool_stats, dict )
def test_get_pending_staking_transactions(setup_blockchain):
def test_get_pending_staking_transactions( setup_blockchain ):
pending_staking_transactions = _test_transaction_rpc(
transaction.get_pending_staking_transactions, endpoint=endpoint
transaction.get_pending_staking_transactions,
endpoint = endpoint
)
assert isinstance(pending_staking_transactions, list)
assert isinstance( pending_staking_transactions, list )
def test_errors():
with pytest.raises(exceptions.RPCError):
transaction.get_pending_transactions(fake_shard)
with pytest.raises(exceptions.RPCError):
transaction.get_transaction_error_sink(fake_shard)
with pytest.raises(exceptions.RPCError):
transaction.get_pool_stats(fake_shard)
with pytest.raises(exceptions.RPCError):
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)
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)
with pytest.raises(exceptions.RPCError):
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)
with pytest.raises(exceptions.RPCError):
transaction.resend_cx_receipt("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
transaction.get_staking_transaction_by_hash("", endpoint=fake_shard)
with pytest.raises(exceptions.RPCError):
with pytest.raises( exceptions.RPCError ):
transaction.get_pending_transactions( fake_shard )
with pytest.raises( exceptions.RPCError ):
transaction.get_transaction_error_sink( fake_shard )
with pytest.raises( exceptions.RPCError ):
transaction.get_pool_stats( fake_shard )
with pytest.raises( exceptions.RPCError ):
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
)
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 )
with pytest.raises( exceptions.RPCError ):
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 )
with pytest.raises( exceptions.RPCError ):
transaction.resend_cx_receipt( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ):
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
"",
1,
endpoint = fake_shard
)
with pytest.raises(exceptions.RPCError):
with pytest.raises( exceptions.RPCError ):
transaction.get_staking_transaction_by_block_number_and_index(
1, 1, endpoint=fake_shard
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)
with pytest.raises(exceptions.RPCError):
transaction.get_pending_staking_transactions(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 )
with pytest.raises( exceptions.RPCError ):
transaction.get_pending_staking_transactions( endpoint = fake_shard )

@ -14,25 +14,25 @@ test_validator_object = None
test_validator_loaded = False
def test_instantiate_validator(setup_blockchain):
def test_instantiate_validator( setup_blockchain ):
global test_validator_object
test_validator_object = validator.Validator(
"one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
)
assert isinstance(test_validator_object, validator.Validator)
assert isinstance( test_validator_object, validator.Validator )
def test_load_validator(setup_blockchain):
def test_load_validator( setup_blockchain ):
if not test_validator_object:
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),
"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",
@ -42,9 +42,9 @@ def test_load_validator(setup_blockchain):
"bls-key-sigs": [
"0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414"
],
"max-total-delegation": convert_one_to_atto(40000),
"max-total-delegation": convert_one_to_atto( 40000 ),
}
test_validator_object.load(info)
test_validator_object.load( info )
global test_validator_loaded
test_validator_loaded = True
@ -78,19 +78,19 @@ 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")
def test_create_validator_sign( setup_blockchain ):
if not ( test_validator_object or test_validator_loaded ):
pytest.skip( "Validator not instantiated yet" )
signed_hash = test_validator_object.sign_create_validator_transaction(
2,
int(convert_one_to_atto(1)),
int( convert_one_to_atto( 1 ) ),
100,
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
2,
).rawTransaction.hex()
assert (
signed_hash
== "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd"
signed_hash ==
"0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd"
)
@ -137,9 +137,9 @@ 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")
def test_edit_validator_sign( setup_blockchain ):
if not ( test_validator_object or test_validator_loaded ):
pytest.skip( "Validator not instantiated yet" )
signed_hash = test_validator_object.sign_edit_validator_transaction(
2,
int(convert_one_to_atto(1)),
@ -152,67 +152,73 @@ def test_edit_validator_sign(setup_blockchain):
2,
).rawTransaction.hex()
assert (
signed_hash
== "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995"
signed_hash ==
"0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995"
)
def test_invalid_validator(setup_blockchain):
if not (test_validator_object or test_validator_loaded):
pytest.skip("Validator not instantiated yet")
with pytest.raises(InvalidValidatorError):
def test_invalid_validator( setup_blockchain ):
if not ( test_validator_object or test_validator_loaded ):
pytest.skip( "Validator not instantiated yet" )
with pytest.raises( InvalidValidatorError ):
info = {
"name": "Alice",
}
test_validator_object.load(info)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_name("a" * 141)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_identity("a" * 141)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_website("a" * 141)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_security_contact("a" * 141)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_details("a" * 281)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_min_self_delegation(1)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_max_total_delegation(1)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_amount(1)
with pytest.raises(InvalidValidatorError):
test_validator_object.set_max_rate("2.0")
with pytest.raises(InvalidValidatorError):
test_validator_object.set_max_change_rate("-2.0")
with pytest.raises(InvalidValidatorError):
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")
test_validator_object.load( info )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_name( "a" * 141 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_identity( "a" * 141 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_website( "a" * 141 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_security_contact( "a" * 141 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_details( "a" * 281 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_min_self_delegation( 1 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_max_total_delegation( 1 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_amount( 1 )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_max_rate( "2.0" )
with pytest.raises( InvalidValidatorError ):
test_validator_object.set_max_change_rate( "-2.0" )
with pytest.raises( InvalidValidatorError ):
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"
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.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)
assert isinstance(test_validator_object.get_amount(), Decimal)
assert isinstance(test_validator_object.get_max_rate(), Decimal)
assert isinstance(test_validator_object.get_max_change_rate(), Decimal)
assert isinstance(test_validator_object.get_rate(), Decimal)
assert len(test_validator_object.get_bls_keys()) > 0
assert isinstance(
test_validator_object.get_min_self_delegation(),
Decimal
)
assert isinstance(
test_validator_object.get_max_total_delegation(),
Decimal
)
assert isinstance( test_validator_object.get_amount(), Decimal )
assert isinstance( test_validator_object.get_max_rate(), Decimal )
assert isinstance( test_validator_object.get_max_change_rate(), Decimal )
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):
def test_validator_load_from_blockchain( setup_blockchain ):
test_validator_object2 = validator.Validator(
"one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
)

@ -12,59 +12,64 @@ from pyhmy import util
TEMP_DIR = "/tmp/pyhmy-testing/test-util"
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture( scope = "session", autouse = True )
def setup():
shutil.rmtree(TEMP_DIR, ignore_errors=True)
os.makedirs(TEMP_DIR, exist_ok=True)
shutil.rmtree( TEMP_DIR, ignore_errors = True )
os.makedirs( TEMP_DIR, exist_ok = True )
def test_json_load():
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]}
loaded_dict = util.json_load(json.dumps(ref_dict))
assert str(ref_dict) == str(loaded_dict)
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 ]
}
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( 2 ) == 2
assert util.chain_id_to_int( "HmyMainnet" ) == 1
def test_get_gopath():
assert isinstance(util.get_gopath(), str)
assert isinstance( util.get_gopath(), str )
def test_get_goversion():
assert isinstance(util.get_goversion(), str)
assert isinstance( util.get_goversion(), str )
def test_convert_one_to_hex():
assert (
util.convert_one_to_hex("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58")
util.convert_one_to_hex( "0xebcd16e8c1d8f493ba04e99a56474122d81a9c58" )
== "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58"
)
assert (
util.convert_one_to_hex("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9")
util.convert_one_to_hex( "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" )
== "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58"
)
def test_convert_hex_to_one():
assert (
util.convert_hex_to_one("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58")
util.convert_hex_to_one( "0xebcd16e8c1d8f493ba04e99a56474122d81a9c58" )
== "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
)
assert (
util.convert_hex_to_one("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9")
util.convert_hex_to_one( "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" )
== "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
)
def test_get_bls_build_variables():
assert isinstance(util.get_bls_build_variables(), dict)
assert isinstance( util.get_bls_build_variables(), dict )
def test_is_active_shard():
assert isinstance(util.is_active_shard(""), bool)
assert isinstance( util.is_active_shard( "" ), bool )

Loading…
Cancel
Save