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. 8
      pyhmy/__init__.py
  3. 115
      pyhmy/account.py
  4. 10
      pyhmy/bech32/bech32.py
  5. 389
      pyhmy/blockchain.py
  6. 82
      pyhmy/cli.py
  7. 60
      pyhmy/contract.py
  8. 3
      pyhmy/exceptions.py
  9. 22
      pyhmy/logging.py
  10. 1
      pyhmy/numbers.py
  11. 8
      pyhmy/rpc/exceptions.py
  12. 21
      pyhmy/rpc/request.py
  13. 100
      pyhmy/signing.py
  14. 243
      pyhmy/staking.py
  15. 95
      pyhmy/staking_signing.py
  16. 69
      pyhmy/staking_structures.py
  17. 212
      pyhmy/transaction.py
  18. 28
      pyhmy/util.py
  19. 118
      pyhmy/validator.py
  20. 3
      tests/cli-pyhmy/test_cli.py
  21. 35
      tests/request-pyhmy/test_request.py
  22. 24
      tests/sdk-pyhmy/conftest.py
  23. 41
      tests/sdk-pyhmy/test_account.py
  24. 71
      tests/sdk-pyhmy/test_blockchain.py
  25. 16
      tests/sdk-pyhmy/test_contract.py
  26. 9
      tests/sdk-pyhmy/test_signing.py
  27. 60
      tests/sdk-pyhmy/test_staking.py
  28. 10
      tests/sdk-pyhmy/test_staking_signing.py
  29. 57
      tests/sdk-pyhmy/test_transaction.py
  30. 22
      tests/sdk-pyhmy/test_validator.py
  31. 7
      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

@ -9,11 +9,15 @@ from ._version import __version__
if sys.version_info.major < 3: if sys.version_info.major < 3:
warnings.simplefilter( "always", DeprecationWarning ) warnings.simplefilter( "always", DeprecationWarning )
warnings.warn( warnings.warn(
DeprecationWarning("`pyhmy` does not support Python 2. Please use Python 3.") DeprecationWarning(
"`pyhmy` does not support Python 2. Please use Python 3."
)
) )
warnings.resetwarnings() warnings.resetwarnings()
if sys.platform.startswith( "win32" ) or sys.platform.startswith( "cygwin" ): if sys.platform.startswith( "win32" ) or sys.platform.startswith( "cygwin" ):
warnings.simplefilter( "always", ImportWarning ) warnings.simplefilter( "always", ImportWarning )
warnings.warn(ImportWarning("`pyhmy` does not work on Windows or Cygwin.")) warnings.warn(
ImportWarning( "`pyhmy` does not work on Windows or Cygwin." )
)
warnings.resetwarnings() warnings.resetwarnings()

@ -13,6 +13,7 @@ from .bech32.bech32 import bech32_decode
from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT 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 Check if given string is valid one address
@ -36,7 +37,11 @@ def is_valid_address(address) -> bool:
return True return True
def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: def get_balance(
address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int:
"""Get current account balance. """Get current account balance.
Parameters Parameters
@ -66,7 +71,10 @@ def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
params = [ address ] params = [ address ]
try: try:
balance = rpc_request( balance = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ] )[ "result" ]
return int( balance ) # v2 returns the result as it is return int( balance ) # v2 returns the result as it is
except TypeError as exception: # check will work if rpc returns None except TypeError as exception: # check will work if rpc returns None
@ -74,7 +82,10 @@ def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
def get_balance_by_block( def get_balance_by_block(
address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get account balance for address at a given block number. """Get account balance for address at a given block number.
@ -108,7 +119,10 @@ def get_balance_by_block(
params = [ address, block_num ] params = [ address, block_num ]
try: try:
balance = rpc_request( balance = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ] )[ "result" ]
return int( balance ) return int( balance )
except TypeError as exception: except TypeError as exception:
@ -116,7 +130,10 @@ def get_balance_by_block(
def get_account_nonce( 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: ) -> int:
"""Get the account nonce. """Get the account nonce.
@ -148,23 +165,32 @@ def get_account_nonce(
method = "hmyv2_getAccountNonce" method = "hmyv2_getAccountNonce"
params = [ address, block_num ] params = [ address, block_num ]
try: try:
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ nonce = rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( nonce ) return int( nonce )
except TypeError as exception: except TypeError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_nonce( def get_nonce(
address, block_num="latest", endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
block_num = "latest",
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""See get_account_nonce.""" """See get_account_nonce."""
return get_account_nonce( address, block_num, endpoint, timeout ) return get_account_nonce( address, block_num, endpoint, timeout )
def get_transaction_count( def get_transaction_count(
address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get the number of transactions the given address has sent for the given """Get the number of transactions the given address has sent for the given
block number Legacy for apiv1. For apiv2, please use block number Legacy for apiv1. For apiv2, please use
@ -199,16 +225,22 @@ def get_transaction_count(
method = "hmyv2_getTransactionCount" method = "hmyv2_getTransactionCount"
params = [ address, block_num ] params = [ address, block_num ]
try: try:
nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ nonce = rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
return int( nonce ) return int( nonce )
except TypeError as exception: except TypeError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transactions_count( def get_transactions_count(
address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
tx_type,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get the number of regular transactions from genesis of input type. """Get the number of regular transactions from genesis of input type.
@ -243,7 +275,10 @@ def get_transactions_count(
params = [ address, tx_type ] params = [ address, tx_type ]
try: try:
tx_count = rpc_request( tx_count = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ] )[ "result" ]
return int( tx_count ) return int( tx_count )
except TypeError as exception: except TypeError as exception:
@ -251,7 +286,10 @@ def get_transactions_count(
def get_staking_transactions_count( def get_staking_transactions_count(
address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
tx_type,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get the number of staking transactions from genesis of input type """Get the number of staking transactions from genesis of input type
("SENT", "RECEIVED", "ALL") ("SENT", "RECEIVED", "ALL")
@ -287,7 +325,10 @@ def get_staking_transactions_count(
params = [ address, tx_type ] params = [ address, tx_type ]
try: try:
tx_count = rpc_request( tx_count = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ] )[ "result" ]
return int( tx_count ) return int( tx_count )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
@ -359,7 +400,10 @@ def get_transaction_history( # pylint: disable=too-many-arguments
method = "hmyv2_getTransactionsHistory" method = "hmyv2_getTransactionsHistory"
try: try:
tx_history = rpc_request( 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: except KeyError as exception:
@ -445,7 +489,10 @@ def get_staking_transaction_history( # pylint: disable=too-many-arguments
method = "hmyv2_getStakingTransactionsHistory" method = "hmyv2_getStakingTransactionsHistory"
try: try:
stx_history = rpc_request( stx_history = rpc_request(
method, params=params, endpoint=endpoint, timeout=timeout method,
params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ] )[ "result" ]
return stx_history[ "staking_transactions" ] return stx_history[ "staking_transactions" ]
except KeyError as exception: except KeyError as exception:
@ -453,7 +500,10 @@ def get_staking_transaction_history( # pylint: disable=too-many-arguments
def get_balance_on_all_shards( 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: ) -> list:
"""Get current account balance in all shards & optionally report errors """Get current account balance in all shards & optionally report errors
getting account balance for a shard. getting account balance for a shard.
@ -483,25 +533,37 @@ def get_balance_on_all_shards(
] ]
""" """
balances = [] balances = []
sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout) sharding_structure = get_sharding_structure(
endpoint = endpoint,
timeout = timeout
)
for shard in sharding_structure: for shard in sharding_structure:
try: try:
balances.append( balances.append(
{ {
"shard": shard[ "shardID" ], "shard": shard[ "shardID" ],
"balance": get_balance( "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: if not skip_error:
balances.append({"shard": shard["shardID"], "balance": None}) balances.append(
{
"shard": shard[ "shardID" ],
"balance": None
}
)
return balances return balances
def get_total_balance( def get_total_balance(
address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get total account balance on all shards. """Get total account balance on all shards.
@ -530,7 +592,10 @@ def get_total_balance(
""" """
try: try:
balances = get_balance_on_all_shards( 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: except TypeError as exception:

@ -17,10 +17,8 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # 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 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE. # THE SOFTWARE.
"""Reference implementation for Bech32 and segwit addresses.""" """Reference implementation for Bech32 and segwit addresses."""
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
@ -38,7 +36,8 @@ def bech32_polymod(values):
def bech32_hrp_expand( hrp ): def bech32_hrp_expand( hrp ):
"""Expand the HRP into values for checksum computation.""" """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 ):
@ -61,9 +60,8 @@ def bech32_encode(hrp, data):
def bech32_decode( bech ): def bech32_decode( bech ):
"""Validate a Bech32 string, and determine HRP and data.""" """Validate a Bech32 string, and determine HRP and data."""
if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or ( if ( any( ord( x ) < 33 or ord( x ) > 126 for x in bech
bech.lower() != bech and bech.upper() != bech ) ) or ( bech.lower() != bech and bech.upper() != bech ):
):
return ( None, None ) return ( None, None )
bech = bech.lower() bech = bech.lower()
pos = bech.rfind( "1" ) pos = bech.rfind( "1" )

@ -9,10 +9,14 @@ from .exceptions import InvalidRPCReplyError
from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT
############################# #############################
# Node / network level RPCs # # 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 """[WIP] Get list of bad blocks in memory of specific node Known issues
with RPC not returning correctly. with RPC not returning correctly.
@ -38,7 +42,9 @@ def get_bad_blocks(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list:
""" """
method = "hmyv2_getCurrentBadBlocks" method = "hmyv2_getCurrentBadBlocks"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -74,7 +80,10 @@ def chain_id(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
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. """Get config for the node.
Parameters Parameters
@ -133,7 +142,10 @@ def get_node_metadata(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dic
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. """Get peer info for the node.
Parameters Parameters
@ -165,12 +177,17 @@ def get_peer_info(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
""" """
method = "hmyv2_getPeerInfo" method = "hmyv2_getPeerInfo"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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. """Get the current Harmony protocol version this node supports.
Parameters Parameters
@ -202,7 +219,10 @@ def protocol_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
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. """Get number of peers connected to the node.
Parameters Parameters
@ -229,13 +249,19 @@ def get_num_peers(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
method = "net_peerCount" method = "net_peerCount"
try: # Number of peers represented as a hex string try: # Number of peers represented as a hex string
return int( 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: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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/) """Get version of the EVM network (https://chainid.network/)
Parameters Parameters
@ -262,7 +288,10 @@ def get_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
method = "net_version" method = "net_version"
try: try:
return int( return int(
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ],
16
) # this is hexadecimal ) # this is hexadecimal
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -293,12 +322,19 @@ def in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool:
""" """
method = "hmyv2_inSync" method = "hmyv2_inSync"
try: try:
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) return bool(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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) """Whether the beacon chain is in sync or syncing (not out of sync)
Parameters Parameters
@ -323,12 +359,19 @@ def beacon_in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool:
""" """
method = "hmyv2_beaconInSync" method = "hmyv2_beaconInSync"
try: try:
return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) return bool(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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. """Get epoch number when blockchain switches to EPoS election.
Parameters Parameters
@ -358,13 +401,18 @@ def get_staking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int
""" """
method = "hmyv2_getNodeMetadata" method = "hmyv2_getNodeMetadata"
try: try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] data = rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
return int( data[ "chain-config" ][ "staking-epoch" ] ) return int( data[ "chain-config" ][ "staking-epoch" ] )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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 """Get epoch number when blockchain switches to allow staking features
without election. without election.
@ -395,7 +443,9 @@ def get_prestaking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
""" """
method = "hmyv2_getNodeMetadata" method = "hmyv2_getNodeMetadata"
try: try:
data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] data = rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
return int( data[ "chain-config" ][ "prestaking-epoch" ] ) return int( data[ "chain-config" ][ "prestaking-epoch" ] )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -430,15 +480,16 @@ def get_shard(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
""" """
method = "hmyv2_getNodeMetadata" method = "hmyv2_getNodeMetadata"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"][ return rpc_request( method,
"shard-id" endpoint = endpoint,
] timeout = timeout )[ "result" ][ "shard-id" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_sharding_structure( def get_sharding_structure(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get network sharding structure. """Get network sharding structure.
@ -468,7 +519,9 @@ def get_sharding_structure(
""" """
method = "hmyv2_getShardingStructure" method = "hmyv2_getShardingStructure"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -476,7 +529,10 @@ def get_sharding_structure(
############################# #############################
# Current status of network # # 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. """Get current leader one address.
Parameters Parameters
@ -502,13 +558,17 @@ def get_leader_address(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> st
""" """
method = "hmyv2_getLeader" method = "hmyv2_getLeader"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def is_last_block( def is_last_block(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool: ) -> bool:
"""If the block at block_num is the last block. """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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L286
""" """
params = [ params = [ block_num, ]
block_num,
]
method = "hmyv2_isLastBlock" method = "hmyv2_isLastBlock"
try: try:
return bool( return bool(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def epoch_last_block( def epoch_last_block(
epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Returns the number of the last block in the epoch. """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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L294
""" """
params = [ params = [ epoch, ]
epoch,
]
method = "hmyv2_epochLastBlock" method = "hmyv2_epochLastBlock"
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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. """Get current circulation supply of tokens in ONE.
Parameters Parameters
@ -615,12 +682,17 @@ def get_circulating_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -
""" """
method = "hmyv2_getCirculatingSupply" method = "hmyv2_getCirculatingSupply"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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. """Get total number of pre-mined tokens.
Parameters Parameters
@ -646,12 +718,17 @@ def get_total_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
""" """
method = "hmyv2_getTotalSupply" method = "hmyv2_getTotalSupply"
try: try:
rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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. """Get current block number.
Parameters Parameters
@ -677,12 +754,19 @@ def get_block_number(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
""" """
method = "hmyv2_blockNumber" method = "hmyv2_blockNumber"
try: try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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. """Get current epoch number.
Parameters Parameters
@ -708,12 +792,19 @@ def get_current_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int
""" """
method = "hmyv2_getEpoch" method = "hmyv2_getEpoch"
try: try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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. """Get last cross shard links.
Parameters Parameters
@ -746,12 +837,17 @@ def get_last_cross_links(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
""" """
method = "hmyv2_getLastCrossLinks" method = "hmyv2_getLastCrossLinks"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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. """Get network gas price.
Parameters Parameters
@ -777,7 +873,11 @@ def get_gas_price(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
""" """
method = "hmyv2_gasPrice" method = "hmyv2_gasPrice"
try: try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -785,7 +885,10 @@ def get_gas_price(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int:
############## ##############
# Block RPCs # # 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. """Get block header of latest block.
Parameters Parameters
@ -829,13 +932,17 @@ def get_latest_header(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dic
""" """
method = "hmyv2_latestHeader" method = "hmyv2_latestHeader"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_header_by_number( def get_header_by_number(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get block header of block at block_num. """Get block header of block at block_num.
@ -864,15 +971,19 @@ def get_header_by_number(
method = "hmyv2_getHeaderByNumber" method = "hmyv2_getHeaderByNumber"
params = [ block_num ] params = [ block_num ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_latest_chain_headers( def get_latest_chain_headers(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get block header of latest block for beacon chain & shard chain. """Get block header of latest block for beacon chain & shard chain.
@ -919,7 +1030,9 @@ def get_latest_chain_headers(
""" """
method = "hmyv2_getLatestChainHeaders" method = "hmyv2_getLatestChainHeaders"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -1005,9 +1118,12 @@ def get_block_by_number( # pylint: disable=too-many-arguments
] ]
method = "hmyv2_getBlockByNumber" method = "hmyv2_getBlockByNumber"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -1062,15 +1178,20 @@ def get_block_by_hash( # pylint: disable=too-many-arguments
] ]
method = "hmyv2_getBlockByHash" method = "hmyv2_getBlockByHash"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_transaction_count_by_number( def get_block_transaction_count_by_number(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get transaction count for specific block number. """Get transaction count for specific block number.
@ -1103,16 +1224,21 @@ def get_block_transaction_count_by_number(
method = "hmyv2_getBlockTransactionCountByNumber" method = "hmyv2_getBlockTransactionCountByNumber"
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_transaction_count_by_hash( def get_block_transaction_count_by_hash(
block_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT block_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get transaction count for specific block hash. """Get transaction count for specific block hash.
@ -1145,16 +1271,21 @@ def get_block_transaction_count_by_hash(
method = "hmyv2_getBlockTransactionCountByHash" method = "hmyv2_getBlockTransactionCountByHash"
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_staking_transaction_count_by_number( 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: ) -> int:
"""Get staking transaction count for specific block number. """Get staking transaction count for specific block number.
@ -1187,16 +1318,21 @@ def get_block_staking_transaction_count_by_number(
method = "hmyv2_getBlockStakingTransactionCountByNumber" method = "hmyv2_getBlockStakingTransactionCountByNumber"
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_staking_transaction_count_by_hash( 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: ) -> int:
"""Get staking transaction count for specific block hash. """Get staking transaction count for specific block hash.
@ -1229,9 +1365,12 @@ def get_block_staking_transaction_count_by_hash(
method = "hmyv2_getBlockStakingTransactionCountByHash" method = "hmyv2_getBlockStakingTransactionCountByHash"
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -1293,15 +1432,20 @@ def get_blocks( # pylint: disable=too-many-arguments
] ]
method = "hmyv2_getBlocks" method = "hmyv2_getBlocks"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_signers( def get_block_signers(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of block signers for specific block number. """Get list of block signers for specific block number.
@ -1331,15 +1475,20 @@ def get_block_signers(
params = [ block_num ] params = [ block_num ]
method = "hmyv2_getBlockSigners" method = "hmyv2_getBlockSigners"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_block_signers_keys( def get_block_signers_keys(
block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of block signer public bls keys for specific block number. """Get list of block signer public bls keys for specific block number.
@ -1369,15 +1518,21 @@ def get_block_signers_keys(
params = [ block_num ] params = [ block_num ]
method = "hmyv2_getBlockSignerKeys" method = "hmyv2_getBlockSignerKeys"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def is_block_signer( def is_block_signer(
block_num, address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT block_num,
address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool: ) -> bool:
"""Determine if the account at address is a signer for the block at """Determine if the account at address is a signer for the block at
block_num. block_num.
@ -1409,15 +1564,20 @@ def is_block_signer(
params = [ block_num, address ] params = [ block_num, address ]
method = "hmyv2_isBlockSigner" method = "hmyv2_isBlockSigner"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_signed_blocks( def get_signed_blocks(
address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool: ) -> bool:
"""The number of blocks a particular validator signed for last blocksPeriod """The number of blocks a particular validator signed for last blocksPeriod
(1 epoch) (1 epoch)
@ -1448,15 +1608,22 @@ def get_signed_blocks(
method = "hmyv2_getSignedBlocks" method = "hmyv2_getSignedBlocks"
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from 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. """Get list of validators for specific epoch number.
Parameters Parameters
@ -1488,15 +1655,20 @@ def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
params = [ epoch ] params = [ epoch ]
method = "hmyv2_getValidators" method = "hmyv2_getValidators"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_keys( def get_validator_keys(
epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of validator public bls keys for specific epoch number. """Get list of validator public bls keys for specific epoch number.
@ -1526,8 +1698,11 @@ def get_validator_keys(
params = [ epoch ] params = [ epoch ]
method = "hmyv2_getValidatorKeys" method = "hmyv2_getValidatorKeys"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception

@ -77,7 +77,6 @@ ARG_PREFIX = "__PYHMY_ARG_PREFIX__"
environment = os.environ.copy() # The environment for the CLI to execute in. environment = os.environ.copy() # The environment for the CLI to execute in.
# completely remove caching... # completely remove caching...
# we need to improve getting address better internally to REDUCE single calls.... # we need to improve getting address better internally to REDUCE single calls....
# def _cache_and_lock_accounts_keystore(fn): # def _cache_and_lock_accounts_keystore(fn):
@ -102,6 +101,7 @@ environment = os.environ.copy() # The environment for the CLI to execute in.
# return wrap # return wrap
def account_keystore_path( value = None ): def account_keystore_path( value = None ):
""" """
Gets or sets the ACCOUNT_KEYSTORE_PATH Gets or sets the ACCOUNT_KEYSTORE_PATH
@ -112,6 +112,7 @@ def account_keystore_path(value=None):
account_keystore_path.value = value account_keystore_path.value = value
return account_keystore_path.value return account_keystore_path.value
def binary_path( value = None ): def binary_path( value = None ):
""" """
Gets or sets the BINARY_PATH Gets or sets the BINARY_PATH
@ -122,6 +123,7 @@ def binary_path(value=None):
binary_path.value = value binary_path.value = value
return binary_path.value return binary_path.value
def _get_current_accounts_keystore(): def _get_current_accounts_keystore():
"""Internal function that gets the current keystore from the CLI. """Internal function that gets the current keystore from the CLI.
@ -132,9 +134,13 @@ def _get_current_accounts_keystore():
response = single_call( "hmy keys list" ) response = single_call( "hmy keys list" )
lines = response.split( "\n" ) lines = response.split( "\n" )
if "NAME" not in lines[ 0 ] or "ADDRESS" not in lines[ 0 ]: 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") raise ValueError(
"Name or Address not found on first line of key list"
)
if lines[ 1 ] != "": if lines[ 1 ] != "":
raise ValueError("Unknown format: No blank line between label and data") raise ValueError(
"Unknown format: No blank line between label and data"
)
for line in lines[ 2 : ]: for line in lines[ 2 : ]:
columns = line.split( "\t" ) columns = line.split( "\t" )
if len( columns ) != 2: if len( columns ) != 2:
@ -184,8 +190,13 @@ def _make_call_command(command):
command_toks_prefix = [ el for el in command.split( " " ) if el ] command_toks_prefix = [ el for el in command.split( " " ) if el ]
command_toks = [] command_toks = []
for element in command_toks_prefix: for element in command_toks_prefix:
if element.startswith(f'"{ARG_PREFIX}_') and element.endswith('"'): if element.startswith( f'"{ARG_PREFIX}_'
index = int(element.replace(f'"{ARG_PREFIX}_', "").replace('"', "")) ) and element.endswith( '"' ):
index = int(
element.replace( f'"{ARG_PREFIX}_',
"" ).replace( '"',
"" )
)
command_toks.append( all_strings[ index ] ) command_toks.append( all_strings[ index ] )
else: else:
command_toks.append( element ) command_toks.append( element )
@ -213,7 +224,8 @@ def is_valid_binary(path):
os.chmod( path, os.stat( path ).st_mode | stat.S_IEXEC ) os.chmod( path, os.stat( path ).st_mode | stat.S_IEXEC )
try: try:
with subprocess.Popen( with subprocess.Popen(
[path, "version"], [ path,
"version" ],
env = environment, env = environment,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
@ -222,7 +234,11 @@ def is_valid_binary(path):
if not err: if not err:
return False return False
return "harmony" in err.decode().strip().lower() return "harmony" in err.decode().strip().lower()
except (OSError, subprocess.CalledProcessError, subprocess.SubprocessError): except (
OSError,
subprocess.CalledProcessError,
subprocess.SubprocessError
):
return False return False
@ -256,7 +272,8 @@ def get_version():
:return: The version string of the CLI binary. :return: The version string of the CLI binary.
""" """
with subprocess.Popen( with subprocess.Popen(
[binary_path(), "version"], [ binary_path(),
"version" ],
env = environment, env = environment,
stdout = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.PIPE, stderr = subprocess.PIPE,
@ -301,7 +318,10 @@ def get_accounts(address):
Note that a list of account names is needed because 1 address can Note that a list of account names is needed because 1 address can
have multiple names within the CLI's keystore. 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 ):
@ -318,7 +338,8 @@ def remove_account(name):
shutil.rmtree( keystore_path ) shutil.rmtree( keystore_path )
except ( shutil.Error, FileNotFoundError ) as err: except ( shutil.Error, FileNotFoundError ) as err:
raise RuntimeError( 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 ) from err
_sync_accounts() _sync_accounts()
@ -343,12 +364,15 @@ def single_call(command, timeout=60, error_ok=False):
command_toks = [ binary_path() ] + _make_call_command( command ) command_toks = [ binary_path() ] + _make_call_command( command )
try: try:
return subprocess.check_output( return subprocess.check_output(
command_toks, env=environment, timeout=timeout command_toks,
env = environment,
timeout = timeout
).decode() ).decode()
except subprocess.CalledProcessError as err: except subprocess.CalledProcessError as err:
if not error_ok: if not error_ok:
raise RuntimeError( raise RuntimeError(
f"Bad CLI args: `{command}`\n " f"\tException: {err}" f"Bad CLI args: `{command}`\n "
f"\tException: {err}"
) from err ) from err
return err.output.decode() return err.output.decode()
@ -363,12 +387,16 @@ def expect_call(command, timeout=60):
command_toks = _make_call_command( command ) command_toks = _make_call_command( command )
try: try:
proc = pexpect.spawn( proc = pexpect.spawn(
f"{binary_path()}", command_toks, env=environment, timeout=timeout f"{binary_path()}",
command_toks,
env = environment,
timeout = timeout
) )
proc.delaybeforesend = None proc.delaybeforesend = None
except pexpect.ExceptionPexpect as err: except pexpect.ExceptionPexpect as err:
raise RuntimeError( raise RuntimeError(
f"Bad CLI args: `{command}`\n " f"\tException: {err}" f"Bad CLI args: `{command}`\n "
f"\tException: {err}"
) from err ) from err
return proc return proc
@ -399,28 +427,40 @@ def download(path="./bin/hmy", replace=True, verbose=True):
"https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh" "https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh"
).content.decode() ).content.decode()
) )
os.chmod(hmy_script_path, os.stat(hmy_script_path).st_mode | stat.S_IEXEC) os.chmod(
hmy_script_path,
os.stat( hmy_script_path ).st_mode | stat.S_IEXEC
)
same_name_file = False same_name_file = False
if ( 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. ): # Save same name file.
same_name_file = True same_name_file = True
os.rename( 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: if verbose:
subprocess.call( [ hmy_script_path, "-d" ] ) subprocess.call( [ hmy_script_path, "-d" ] )
else: else:
with open( os.devnull, "w", encoding = "UTF-8" ) as devnull: with open( os.devnull, "w", encoding = "UTF-8" ) as devnull:
subprocess.call( subprocess.call(
[hmy_script_path, "-d"], [ hmy_script_path,
"-d" ],
stdout = devnull, stdout = devnull,
stderr = subprocess.STDOUT, stderr = subprocess.STDOUT,
) )
os.rename( os.path.join( parent_dir, "hmy" ), path ) os.rename( os.path.join( parent_dir, "hmy" ), path )
if same_name_file: if same_name_file:
os.rename( os.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: if verbose:
print( f"Saved harmony binary to: `{path}`" ) print( f"Saved harmony binary to: `{path}`" )
@ -434,7 +474,9 @@ def download(path="./bin/hmy", replace=True, verbose=True):
env[ "DYLD_FALLBACK_LIBRARY_PATH" ] = parent_dir env[ "DYLD_FALLBACK_LIBRARY_PATH" ] = parent_dir
elif os.path.exists( elif os.path.exists(
f"{get_gopath()}/src/github.com/harmony-one/bls" f"{get_gopath()}/src/github.com/harmony-one/bls"
) and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"): ) and os.path.exists(
f"{get_gopath()}/src/github.com/harmony-one/mcl"
):
env.update( get_bls_build_variables() ) env.update( get_bls_build_variables() )
else: else:
raise RuntimeWarning( raise RuntimeWarning(

@ -76,9 +76,12 @@ def call( # pylint: disable=too-many-arguments
] ]
method = "hmyv2_call" method = "hmyv2_call"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -141,9 +144,12 @@ def estimate_gas( # pylint: disable=too-many-arguments
method = "hmyv2_estimateGas" method = "hmyv2_estimateGas"
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
], params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ],
16, 16,
) )
except KeyError as exception: except KeyError as exception:
@ -151,7 +157,10 @@ def estimate_gas( # pylint: disable=too-many-arguments
def get_code( def get_code(
address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
block_num,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str: ) -> str:
"""Get the code stored at the given address in the state for the given """Get the code stored at the given address in the state for the given
block number. block number.
@ -185,15 +194,22 @@ def get_code(
params = [ address, block_num ] params = [ address, block_num ]
method = "hmyv2_getCode" method = "hmyv2_getCode"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_storage_at( 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: ) -> str:
"""Get the storage from the state at the given address, the key and the """Get the storage from the state at the given address, the key and the
block number. block number.
@ -229,15 +245,20 @@ def get_storage_at(
params = [ address, key, block_num ] params = [ address, key, block_num ]
method = "hmyv2_getStorageAt" method = "hmyv2_getStorageAt"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_contract_address_from_hash( def get_contract_address_from_hash(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str: ) -> str:
"""Get address of the contract which was deployed in the transaction """Get address of the contract which was deployed in the transaction
represented by tx_hash. represented by tx_hash.
@ -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 https://github.com/harmony-one/harmony-test/blob/master/localnet/rpc_tests/test_contract.py#L36
""" """
try: try:
return get_transaction_receipt(tx_hash, endpoint, timeout)["contractAddress"] return get_transaction_receipt( tx_hash,
endpoint,
timeout )[ "contractAddress" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError("hmyv2_getTransactionReceipt", endpoint) from exception raise InvalidRPCReplyError(
"hmyv2_getTransactionReceipt",
endpoint
) from exception

@ -2,11 +2,11 @@
Exceptions used by pyhmy Exceptions used by pyhmy
""" """
class InvalidRPCReplyError( RuntimeError ): class InvalidRPCReplyError( RuntimeError ):
"""Exception raised when RPC call returns unexpected result Generally """Exception raised when RPC call returns unexpected result Generally
indicates Harmony API has been updated & pyhmy library needs to be updated indicates Harmony API has been updated & pyhmy library needs to be updated
as well.""" as well."""
def __init__( self, method, endpoint ): def __init__( self, method, endpoint ):
super().__init__( f"Unexpected reply for {method} from {endpoint}" ) super().__init__( f"Unexpected reply for {method} from {endpoint}" )
@ -34,6 +34,5 @@ class InvalidValidatorError(ValueError):
class TxConfirmationTimedoutError( AssertionError ): class TxConfirmationTimedoutError( AssertionError ):
"""Exception raised when a transaction is sent to the chain But not """Exception raised when a transaction is sent to the chain But not
confirmed during the timeout period specified.""" confirmed during the timeout period specified."""
def __init__( self, msg ): def __init__( self, msg ):
super().__init__( f"{msg}" ) super().__init__( f"{msg}" )

@ -22,7 +22,6 @@ class _GZipRotator: # pylint: disable=too-few-public-methods
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 """A simple logger that only writes to file when the 'write' method is
called.""" 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 logger_name: The name of the logger and logfile
@ -33,9 +32,14 @@ class ControlledLogger: # pylint: disable=too-many-instance-attributes
log_dir = os.path.realpath( log_dir ) log_dir = os.path.realpath( log_dir )
os.makedirs( log_dir, exist_ok = True ) os.makedirs( log_dir, exist_ok = True )
handler = logging.handlers.TimedRotatingFileHandler( 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() handler.rotator = _GZipRotator()
self.filename = handler.baseFilename self.filename = handler.baseFilename
@ -64,7 +68,8 @@ class ControlledLogger: # pylint: disable=too-many-instance-attributes
""" """
with self._lock: with self._lock:
self.info_buffer.append( 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 ):
@ -73,7 +78,8 @@ class ControlledLogger: # pylint: disable=too-many-instance-attributes
""" """
with self._lock: with self._lock:
self.debug_buffer.append( 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 ):
@ -82,7 +88,8 @@ class ControlledLogger: # pylint: disable=too-many-instance-attributes
""" """
with self._lock: with self._lock:
self.warning_buffer.append( 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 ):
@ -91,7 +98,8 @@ class ControlledLogger: # pylint: disable=too-many-instance-attributes
""" """
with self._lock: with self._lock:
self.error_buffer.append( 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 ):

@ -5,7 +5,6 @@ For more granular conversions, see Web3.toWei
from decimal import Decimal from decimal import Decimal
_conversion_unit = Decimal( 1e18 ) _conversion_unit = Decimal( 1e18 )

@ -4,23 +4,23 @@ RPC Specific Exceptions
import requests import requests
class RPCError( RuntimeError ): class RPCError( RuntimeError ):
"""Exception raised when RPC call returns an error.""" """Exception raised when RPC call returns an error."""
def __init__( self, method, endpoint, error ): def __init__( self, method, endpoint, error ):
self.error = error self.error = error
super().__init__(f"Error in reply from {endpoint}: {method} returned {error}") super().__init__(
f"Error in reply from {endpoint}: {method} returned {error}"
)
class RequestsError( requests.exceptions.RequestException ): class RequestsError( requests.exceptions.RequestException ):
"""Wrapper for requests lib exceptions.""" """Wrapper for requests lib exceptions."""
def __init__( self, endpoint ): def __init__( self, endpoint ):
super().__init__( f"Error connecting to {endpoint}" ) super().__init__( f"Error connecting to {endpoint}" )
class RequestsTimeoutError( requests.exceptions.Timeout ): class RequestsTimeoutError( requests.exceptions.Timeout ):
"""Wrapper for requests lib Timeout exceptions.""" """Wrapper for requests lib Timeout exceptions."""
def __init__( self, endpoint ): def __init__( self, endpoint ):
super().__init__( f"Error connecting to {endpoint}" ) super().__init__( f"Error connecting to {endpoint}" )

@ -11,7 +11,10 @@ from ..constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT
def base_request( def base_request(
method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT method,
params = None,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str: ) -> str:
"""Basic RPC request. """Basic RPC request.
@ -46,8 +49,15 @@ def base_request(
raise TypeError( f"invalid type {params.__class__}" ) raise TypeError( f"invalid type {params.__class__}" )
try: try:
payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} payload = {
headers = {"Content-Type": "application/json"} "id": "1",
"jsonrpc": "2.0",
"method": method,
"params": params
}
headers = {
"Content-Type": "application/json"
}
resp = requests.request( resp = requests.request(
"POST", "POST",
@ -65,7 +75,10 @@ def base_request(
def rpc_request( def rpc_request(
method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT method,
params = None,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""RPC request. """RPC request.

@ -45,14 +45,23 @@ class UnsignedHarmonyTxData(HashableRLP):
as the difference against Eth as the difference against Eth
""" """
fields = ( fields = (
("nonce", big_endian_int), ( "nonce",
("gasPrice", big_endian_int), big_endian_int ),
("gas", big_endian_int), ( "gasPrice",
("shardID", big_endian_int), big_endian_int ),
("toShardID", big_endian_int), ( "gas",
("to", Binary.fixed_length(20, allow_empty=True)), big_endian_int ),
("value", big_endian_int), ( "shardID",
("data", binary), big_endian_int ),
( "toShardID",
big_endian_int ),
( "to",
Binary.fixed_length( 20,
allow_empty = True ) ),
( "value",
big_endian_int ),
( "data",
binary ),
) )
@ -68,18 +77,31 @@ class SignedHarmonyTxData(HashableRLP):
("s", big_endian_int), # Next 32 bytes ("s", big_endian_int), # Next 32 bytes
) )
# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55
def encode_transaction( def encode_transaction( unsigned_transaction, vrs ):
unsigned_transaction, vrs
):
"""serialize and encode an unsigned transaction with v,r,s.""" """serialize and encode an unsigned transaction with v,r,s."""
( v, r, s ) = vrs # pylint: disable=invalid-name ( v, r, s ) = vrs # pylint: disable=invalid-name
chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), "v", "r", "s") chain_naive_transaction = dissoc(
if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, SignedHarmonyTxData)): unsigned_transaction.as_dict(),
"v",
"r",
"s"
)
if isinstance(
unsigned_transaction,
( UnsignedHarmonyTxData,
SignedHarmonyTxData )
):
serializer = SignedHarmonyTxData serializer = SignedHarmonyTxData
else: else:
serializer = SignedEthereumTxData serializer = SignedEthereumTxData
signed_transaction = serializer(v=v, r=r, s=s, **chain_naive_transaction) signed_transaction = serializer(
v = v,
r = r,
s = s,
**chain_naive_transaction
)
return rlp.encode( signed_transaction ) return rlp.encode( signed_transaction )
@ -98,7 +120,11 @@ def serialize_transaction(filled_transaction):
for field, _ in serializer._meta.fields: for field, _ in serializer._meta.fields:
assert field in filled_transaction, f"Could not find {field} in transaction" assert field in filled_transaction, f"Could not find {field} in transaction"
return serializer.from_dict( return serializer.from_dict(
{field: filled_transaction[field] for field, _ in serializer._meta.fields} {
field: filled_transaction[ field ]
for field,
_ in serializer._meta.fields
}
) )
@ -109,12 +135,13 @@ def sanitize_transaction(transaction_dict, private_key):
account = Account.from_key( # pylint: disable=no-value-for-parameter account = Account.from_key( # pylint: disable=no-value-for-parameter
private_key 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: if "from" in sanitized_transaction:
sanitized_transaction["from"] = convert_one_to_hex(transaction_dict["from"]) sanitized_transaction[ "from" ] = convert_one_to_hex(
if ( transaction_dict[ "from" ]
sanitized_transaction["from"] == account.address )
): if sanitized_transaction[ "from" ] == account.address:
sanitized_transaction = dissoc( sanitized_transaction, "from" ) sanitized_transaction = dissoc( sanitized_transaction, "from" )
else: else:
raise TypeError( raise TypeError(
@ -122,7 +149,9 @@ def sanitize_transaction(transaction_dict, private_key):
"but it was {sanitized_transaction['from']}" "but it was {sanitized_transaction['from']}"
) )
if "chainId" in sanitized_transaction: 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 return account, sanitized_transaction
@ -172,13 +201,17 @@ def sign_transaction(transaction_dict, private_key) -> SignedTransaction:
https://readthedocs.org/projects/eth-account/downloads/pdf/stable/ https://readthedocs.org/projects/eth-account/downloads/pdf/stable/
""" """
account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key)
if "to" in sanitized_transaction and sanitized_transaction["to"] is not None: if "to" in sanitized_transaction and sanitized_transaction[ "to"
sanitized_transaction["to"] = convert_one_to_hex(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 # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39
filled_transaction = pipe( filled_transaction = pipe(
sanitized_transaction, sanitized_transaction,
dict, dict,
partial(merge, TRANSACTION_DEFAULTS), partial( merge,
TRANSACTION_DEFAULTS ),
chain_id_to_v, chain_id_to_v,
apply_formatters_to_dict( HARMONY_FORMATTERS ), apply_formatters_to_dict( HARMONY_FORMATTERS ),
) )
@ -187,13 +220,26 @@ def sign_transaction(transaction_dict, private_key) -> SignedTransaction:
# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26
if isinstance( if isinstance(
unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData) unsigned_transaction,
( UnsignedEthereumTxData,
UnsignedHarmonyTxData )
): ):
chain_id = None chain_id = None
else: else:
chain_id = unsigned_transaction.v chain_id = unsigned_transaction.v
(v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) # pylint: disable=invalid-name ( v, # pylint: disable=invalid-name
encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s)) 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 ) signed_transaction_hash = keccak( encoded_transaction )
return SignedTransaction( return SignedTransaction(
rawTransaction = HexBytes( encoded_transaction ), rawTransaction = HexBytes( encoded_transaction ),

@ -8,11 +8,13 @@ from .exceptions import InvalidRPCReplyError
from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT
################## ##################
# Validator RPCs # # Validator RPCs #
################## ##################
def get_all_validator_addresses( def get_all_validator_addresses(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of all created validator addresses on chain. """Get list of all created validator addresses on chain.
@ -39,13 +41,17 @@ def get_all_validator_addresses(
""" """
method = "hmyv2_getAllValidatorAddresses" method = "hmyv2_getAllValidatorAddresses"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_information( def get_validator_information(
validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT validator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get validator information for validator address. """Get validator information for validator address.
@ -120,15 +126,19 @@ def get_validator_information(
method = "hmyv2_getValidatorInformation" method = "hmyv2_getValidatorInformation"
params = [ validator_addr ] params = [ validator_addr ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_elected_validator_addresses( def get_elected_validator_addresses(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of elected validator addresses. """Get list of elected validator addresses.
@ -156,12 +166,18 @@ def get_elected_validator_addresses(
""" """
method = "hmyv2_getElectedValidatorAddresses" method = "hmyv2_getElectedValidatorAddresses"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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. """Get validators list for a particular epoch.
Parameters Parameters
@ -193,15 +209,20 @@ def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
method = "hmyv2_getValidators" method = "hmyv2_getValidators"
params = [ epoch ] params = [ epoch ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_keys( def get_validator_keys(
epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT epoch,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get validator BLS keys in the committee for a particular epoch. """Get validator BLS keys in the committee for a particular epoch.
@ -230,15 +251,21 @@ def get_validator_keys(
method = "hmyv2_getValidatorKeys" method = "hmyv2_getValidatorKeys"
params = [ epoch ] params = [ epoch ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_information_by_block_number( 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. """Get validator information for validator address at a block.
@ -269,15 +296,20 @@ def get_validator_information_by_block_number(
method = "hmyv2_getValidatorInformationByBlockNumber" method = "hmyv2_getValidatorInformationByBlockNumber"
params = [ validator_addr, block_num ] params = [ validator_addr, block_num ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_all_validator_information( def get_all_validator_information(
page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT page = 0,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get validator information for all validators on chain. """Get validator information for all validators on chain.
@ -306,15 +338,20 @@ def get_all_validator_information(
method = "hmyv2_getAllValidatorInformation" method = "hmyv2_getAllValidatorInformation"
params = [ page ] params = [ page ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_self_delegation( def get_validator_self_delegation(
address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get the amount self delegated by validator. """Get the amount self delegated by validator.
@ -344,16 +381,21 @@ def get_validator_self_delegation(
params = [ address ] params = [ address ]
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_validator_total_delegation( def get_validator_total_delegation(
address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT address,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get the total amount delegated t ovalidator (including self delegated) """Get the total amount delegated t ovalidator (including self delegated)
@ -383,16 +425,22 @@ def get_validator_total_delegation(
params = [ address ] params = [ address ]
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_all_validator_information_by_block_number( 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: ) -> list:
"""Get validator information at block number for all validators on chain. """Get validator information at block number for all validators on chain.
@ -424,9 +472,12 @@ def get_all_validator_information_by_block_number(
method = "hmyv2_getAllValidatorInformationByBlockNumber" method = "hmyv2_getAllValidatorInformationByBlockNumber"
params = [ page, block_num ] params = [ page, block_num ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -435,7 +486,9 @@ def get_all_validator_information_by_block_number(
# Delegation RPCs # # Delegation RPCs #
################### ###################
def get_all_delegation_information( def get_all_delegation_information(
page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT page = 0,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get delegation information for all delegators on chain. """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 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L413
""" """
method = "hmyv2_getAllDelegationInformation" method = "hmyv2_getAllDelegationInformation"
params = [ params = [ page, ]
page,
]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_delegations_by_delegator( def get_delegations_by_delegator(
delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT delegator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of delegations by a delegator. """Get list of delegations by a delegator.
@ -512,15 +568,21 @@ def get_delegations_by_delegator(
method = "hmyv2_getDelegationsByDelegator" method = "hmyv2_getDelegationsByDelegator"
params = [ delegator_addr ] params = [ delegator_addr ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_delegations_by_delegator_by_block_number( 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: ) -> list:
"""Get list of delegations by a delegator at a specific block. """Get list of delegations by a delegator at a specific block.
@ -551,9 +613,12 @@ def get_delegations_by_delegator_by_block_number(
method = "hmyv2_getDelegationsByDelegatorByBlockNumber" method = "hmyv2_getDelegationsByDelegatorByBlockNumber"
params = [ delegator_addr, block_num ] params = [ delegator_addr, block_num ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -594,15 +659,20 @@ def get_delegation_by_delegator_and_validator(
method = "hmyv2_getDelegationByDelegatorAndValidator" method = "hmyv2_getDelegationByDelegatorAndValidator"
params = [ delegator_addr, validator_address ] params = [ delegator_addr, validator_address ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_available_redelegation_balance( def get_available_redelegation_balance(
delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT delegator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> int: ) -> int:
"""Get amount of locked undelegated tokens. """Get amount of locked undelegated tokens.
@ -632,16 +702,21 @@ def get_available_redelegation_balance(
params = [ delegator_addr ] params = [ delegator_addr ]
try: try:
return int( return int(
rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
) )
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_delegations_by_validator( def get_delegations_by_validator(
validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT validator_addr,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of delegations to a validator. """Get list of delegations to a validator.
@ -670,9 +745,12 @@ def get_delegations_by_validator(
method = "hmyv2_getDelegationsByValidator" method = "hmyv2_getDelegationsByValidator"
params = [ validator_addr ] params = [ validator_addr ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -681,7 +759,8 @@ def get_delegations_by_validator(
# Staking Network RPCs # # Staking Network RPCs #
######################## ########################
def get_current_utility_metrics( def get_current_utility_metrics(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get current utility metrics of network. """Get current utility metrics of network.
@ -711,13 +790,16 @@ def get_current_utility_metrics(
""" """
method = "hmyv2_getCurrentUtilityMetrics" method = "hmyv2_getCurrentUtilityMetrics"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_network_info( def get_staking_network_info(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get staking network information. """Get staking network information.
@ -748,12 +830,17 @@ def get_staking_network_info(
""" """
method = "hmyv2_getStakingNetworkInfo" method = "hmyv2_getStakingNetworkInfo"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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. """Get voting committees for current & previous epoch.
Parameters Parameters
@ -801,12 +888,17 @@ def get_super_committees(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) ->
""" """
method = "hmyv2_getSuperCommittees" method = "hmyv2_getSuperCommittees"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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. """Get total staking by validators, only meant to be called on beaconchain.
Parameters Parameters
@ -831,13 +923,18 @@ def get_total_staking(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int
""" """
method = "hmyv2_getTotalStaking" method = "hmyv2_getTotalStaking"
try: try:
return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) return int(
rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
)
except ( KeyError, TypeError ) as exception: except ( KeyError, TypeError ) as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_raw_median_stake_snapshot( def get_raw_median_stake_snapshot(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get median stake & additional committee data of the current epoch. """Get median stake & additional committee data of the current epoch.
@ -876,6 +973,8 @@ def get_raw_median_stake_snapshot(
""" """
method = "hmyv2_getMedianRawStakeSnapshot" method = "hmyv2_getMedianRawStakeSnapshot"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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 decimal import Decimal
from functools import partial from functools import partial
from toolz import ( from toolz import ( pipe, dissoc, merge, identity, )
pipe,
dissoc,
merge,
identity,
)
from hexbytes import HexBytes from hexbytes import HexBytes
@ -51,9 +46,7 @@ from .util import convert_one_to_hex
# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335
def _convert_staking_percentage_to_number( def _convert_staking_percentage_to_number( value, ):
value,
):
"""Convert from staking percentage to integer For example, 0.1 becomes """Convert from staking percentage to integer For example, 0.1 becomes
1000000000000000000. Since Python floats are problematic with precision, 1000000000000000000. Since Python floats are problematic with precision,
this function is used as a workaround. this function is used as a workaround.
@ -97,9 +90,7 @@ def _convert_staking_percentage_to_number(
elif len( splitted ) > 2: elif len( splitted ) > 2:
raise ValueError( "Too many periods to be a StakingDecimal string" ) raise ValueError( "Too many periods to be a StakingDecimal string" )
if length > PRECISION: if length > PRECISION:
raise ValueError( raise ValueError( "Too much precision, must be less than {PRECISION}" )
"Too much precision, must be less than {PRECISION}"
)
zeroes_to_add = PRECISION - length zeroes_to_add = PRECISION - length
combined_str += ( combined_str += (
"0" * zeroes_to_add "0" * zeroes_to_add
@ -135,12 +126,16 @@ def _get_account_and_transaction(transaction_dict, private_key):
transaction_dict, private_key transaction_dict, private_key
) # remove from, convert chain id ( if present ) to integer ) # remove from, convert chain id ( if present ) to integer
sanitized_transaction[ "directive" ] = sanitized_transaction[ sanitized_transaction[ "directive" ] = sanitized_transaction[
"directive" "directive" ].value # convert to value, like in TypeScript
].value # convert to value, like in TypeScript
return account, sanitized_transaction return account, sanitized_transaction
# pylint: disable=too-many-locals,protected-access,invalid-name # 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 """Sign a generic staking transaction, given the serializer base class and
account. account.
@ -184,7 +179,11 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer)
for field, _ in unsigned_serializer._meta.fields: for field, _ in unsigned_serializer._meta.fields:
assert field in filled_transaction, f"Could not find {field} in transaction" assert field in filled_transaction, f"Could not find {field} in transaction"
unsigned_transaction = unsigned_serializer.from_dict( unsigned_transaction = unsigned_serializer.from_dict(
{f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields} {
f: filled_transaction[ f ]
for f,
_ in unsigned_serializer._meta.fields
}
) # drop extras silently ) # drop extras silently
# sign the unsigned transaction # sign the unsigned transaction
if "v" in unsigned_transaction.as_dict( ): if "v" in unsigned_transaction.as_dict( ):
@ -192,9 +191,17 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer)
else: else:
chain_id = None chain_id = None
transaction_hash = unsigned_transaction.hash( ) transaction_hash = unsigned_transaction.hash( )
(v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) ( v,
r,
s
) = sign_transaction_hash( account._key_obj,
transaction_hash,
chain_id )
chain_naive_transaction = dissoc( 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 ) # remove extra v/r/s added by chain_id_to_v
# serialize it # serialize it
# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207
@ -229,7 +236,10 @@ def _sign_delegate_or_undelegate(transaction_dict, private_key):
"""Sign a delegate or undelegate transaction See sign_staking_transaction """Sign a delegate or undelegate transaction See sign_staking_transaction
for details.""" for details."""
# preliminary steps # preliminary steps
if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]: if transaction_dict[ "directive" ] not in [
Directive.Delegate,
Directive.Undelegate
]:
raise TypeError( raise TypeError(
"Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate" "Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate"
) )
@ -239,15 +249,25 @@ def _sign_delegate_or_undelegate(transaction_dict, private_key):
) )
# encode the stakeMsg # encode the stakeMsg
sanitized_transaction[ "stakeMsg" ] = apply_formatters_to_sequence( 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")), hexstr_if_str( to_bytes ),
convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), hexstr_if_str( to_bytes ),
hexstr_if_str( to_int )
],
[
convert_one_to_hex(
sanitized_transaction.pop( "delegatorAddress" )
),
convert_one_to_hex(
sanitized_transaction.pop( "validatorAddress" )
),
sanitized_transaction.pop( "amount" ), sanitized_transaction.pop( "amount" ),
], ],
) )
return _sign_transaction_generic( return _sign_transaction_generic(
account, sanitized_transaction, DelegateOrUndelegate account,
sanitized_transaction,
DelegateOrUndelegate
) )
@ -256,7 +276,9 @@ def _sign_collect_rewards(transaction_dict, private_key):
details.""" details."""
# preliminary steps # preliminary steps
if transaction_dict[ "directive" ] != Directive.CollectRewards: if transaction_dict[ "directive" ] != Directive.CollectRewards:
raise TypeError("Only CollectRewards is supported by _sign_collect_rewards") raise TypeError(
"Only CollectRewards is supported by _sign_collect_rewards"
)
# first common step # first common step
account, sanitized_transaction = _get_account_and_transaction( account, sanitized_transaction = _get_account_and_transaction(
transaction_dict, private_key transaction_dict, private_key
@ -264,10 +286,16 @@ def _sign_collect_rewards(transaction_dict, private_key):
# encode the stakeMsg # encode the stakeMsg
sanitized_transaction[ "stakeMsg" ] = [ sanitized_transaction[ "stakeMsg" ] = [
hexstr_if_str( to_bytes )( hexstr_if_str( to_bytes )(
convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")) 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 ):
@ -308,7 +336,8 @@ def _sign_create_validator(transaction_dict, private_key):
sanitized_transaction.pop( "bls-public-keys" ), sanitized_transaction.pop( "bls-public-keys" ),
) )
bls_key_sigs = apply_formatter_to_array( 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(
[ [
@ -340,7 +369,11 @@ def _sign_create_validator(transaction_dict, private_key):
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 ):
@ -391,7 +424,11 @@ def _sign_edit_validator(transaction_dict, private_key):
sanitized_transaction.pop( "bls-key-to-add-sig" ), 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 ):

@ -10,15 +10,11 @@ from rlp.sedes import big_endian_int, Binary, CountableList, List, Text
from eth_rlp import HashableRLP from eth_rlp import HashableRLP
from eth_utils.curried import ( from eth_utils.curried import ( to_int, hexstr_if_str, )
to_int,
hexstr_if_str,
)
# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120
class Directive( class Directive( Enum ):
Enum
):
def _generate_next_value_( name, start, count, last_values ): # pylint: disable=no-self-argument def _generate_next_value_( name, start, count, last_values ): # pylint: disable=no-self-argument
return count return count
@ -39,17 +35,29 @@ FORMATTERS = {
"chainId": hexstr_if_str(to_int), "chainId": hexstr_if_str(to_int),
} }
class CollectRewards: class CollectRewards:
@staticmethod @staticmethod
def UnsignedChainId(): def UnsignedChainId():
class UnsignedChainId( HashableRLP ): class UnsignedChainId( HashableRLP ):
fields = ( fields = (
("directive", big_endian_int), ( "directive",
("stakeMsg", CountableList(Binary.fixed_length(20, allow_empty=True))), big_endian_int ),
("nonce", big_endian_int), (
("gasPrice", big_endian_int), "stakeMsg",
("gasLimit", big_endian_int), CountableList(
("chainId", big_endian_int), 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 return UnsignedChainId
@ -70,7 +78,8 @@ class CollectRewards:
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned( HashableRLP ): class Unsigned( HashableRLP ):
fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId fields = CollectRewards.UnsignedChainId(
)._meta.fields[ :-1 ] # drop chainId
return Unsigned return Unsigned
@ -93,22 +102,29 @@ class DelegateOrUndelegate:
def UnsignedChainId(): def UnsignedChainId():
class UnsignedChainId( HashableRLP ): class UnsignedChainId( HashableRLP ):
fields = ( fields = (
("directive", big_endian_int), ( "directive",
big_endian_int ),
( (
"stakeMsg", "stakeMsg",
List( List(
[ [
Binary.fixed_length(20, allow_empty=True), Binary.fixed_length( 20,
Binary.fixed_length(20, allow_empty=True), allow_empty = True ),
Binary.fixed_length( 20,
allow_empty = True ),
big_endian_int, big_endian_int,
], ],
True, True,
), ),
), ),
("nonce", big_endian_int), ( "nonce",
("gasPrice", big_endian_int), big_endian_int ),
("gasLimit", big_endian_int), ( "gasPrice",
("chainId", big_endian_int), big_endian_int ),
( "gasLimit",
big_endian_int ),
( "chainId",
big_endian_int ),
) )
return UnsignedChainId return UnsignedChainId
@ -129,9 +145,8 @@ class DelegateOrUndelegate:
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned( HashableRLP ): class Unsigned( HashableRLP ):
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ fields = DelegateOrUndelegate.UnsignedChainId(
:-1 )._meta.fields[ :-1 ] # drop chainId
] # drop chainId
return Unsigned return Unsigned
@ -210,7 +225,8 @@ class CreateValidator:
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned( HashableRLP ): class Unsigned( HashableRLP ):
fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId fields = CreateValidator.UnsignedChainId(
)._meta.fields[ :-1 ] # drop chainId
return Unsigned return Unsigned
@ -290,7 +306,8 @@ class EditValidator:
@staticmethod @staticmethod
def Unsigned(): def Unsigned():
class Unsigned( HashableRLP ): class Unsigned( HashableRLP ):
fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId fields = EditValidator.UnsignedChainId(
)._meta.fields[ :-1 ] # drop chainId
return Unsigned return Unsigned

@ -13,7 +13,8 @@ from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError
# Transaction Pool RPCs # # Transaction Pool RPCs #
######################### #########################
def get_pending_transactions( def get_pending_transactions(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of pending transactions. """Get list of pending transactions.
@ -39,13 +40,16 @@ def get_pending_transactions(
""" """
method = "hmyv2_pendingTransactions" method = "hmyv2_pendingTransactions"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_error_sink( def get_transaction_error_sink(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get current transactions error sink. """Get current transactions error sink.
@ -74,13 +78,16 @@ def get_transaction_error_sink(
""" """
method = "hmyv2_getCurrentTransactionErrorSink" method = "hmyv2_getCurrentTransactionErrorSink"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_pending_staking_transactions( def get_pending_staking_transactions(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of pending staking transactions. """Get list of pending staking transactions.
@ -106,13 +113,16 @@ def get_pending_staking_transactions(
""" """
method = "hmyv2_pendingStakingTransactions" method = "hmyv2_pendingStakingTransactions"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_staking_transaction_error_sink( def get_staking_transaction_error_sink(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get current staking transactions error sink. """Get current staking transactions error sink.
@ -142,12 +152,17 @@ def get_staking_transaction_error_sink(
""" """
method = "hmyv2_getCurrentStakingErrorSink" method = "hmyv2_getCurrentStakingErrorSink"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: 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- """Get stats of the pool, that is, number of pending and queued (non-
executable) transactions. executable) transactions.
@ -175,7 +190,9 @@ def get_pool_stats(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
""" """
method = "hmyv2_getPoolStats" method = "hmyv2_getPoolStats"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -184,7 +201,9 @@ def get_pool_stats(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict:
# Transaction RPCs # # Transaction RPCs #
#################### ####################
def get_transaction_by_hash( def get_transaction_by_hash(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get transaction by hash. """Get transaction by hash.
@ -233,15 +252,21 @@ def get_transaction_by_hash(
method = "hmyv2_getTransactionByHash" method = "hmyv2_getTransactionByHash"
params = [ tx_hash ] params = [ tx_hash ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_by_block_hash_and_index( 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: ) -> dict:
"""Get transaction based on index in list of transactions in a block by """Get transaction based on index in list of transactions in a block by
block hash. block hash.
@ -273,15 +298,21 @@ def get_transaction_by_block_hash_and_index(
method = "hmyv2_getTransactionByBlockHashAndIndex" method = "hmyv2_getTransactionByBlockHashAndIndex"
params = [ block_hash, tx_index ] params = [ block_hash, tx_index ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_by_block_number_and_index( 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: ) -> dict:
"""Get transaction based on index in list of transactions in a block by """Get transaction based on index in list of transactions in a block by
block number. block number.
@ -313,15 +344,20 @@ def get_transaction_by_block_number_and_index(
method = "hmyv2_getTransactionByBlockNumberAndIndex" method = "hmyv2_getTransactionByBlockNumberAndIndex"
params = [ block_num, tx_index ] params = [ block_num, tx_index ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_transaction_receipt( def get_transaction_receipt(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get transaction receipt corresponding to tx_hash. """Get transaction receipt corresponding to tx_hash.
@ -366,15 +402,20 @@ def get_transaction_receipt(
method = "hmyv2_getTransactionReceipt" method = "hmyv2_getTransactionReceipt"
params = [ tx_hash ] params = [ tx_hash ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def send_raw_transaction( def send_raw_transaction(
signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT signed_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str: ) -> str:
"""Send signed transaction. """Send signed transaction.
@ -406,15 +447,20 @@ def send_raw_transaction(
params = [ signed_tx ] params = [ signed_tx ]
method = "hmyv2_sendRawTransaction" method = "hmyv2_sendRawTransaction"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def send_and_confirm_raw_transaction( def send_and_confirm_raw_transaction(
signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT signed_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Send signed transaction and wait for it to be confirmed. """Send signed transaction and wait for it to be confirmed.
@ -455,14 +501,17 @@ def send_and_confirm_raw_transaction(
if unique_chars != "0": if unique_chars != "0":
return tx_response return tx_response
time.sleep( random.uniform( 0.2, 0.5 ) ) time.sleep( random.uniform( 0.2, 0.5 ) )
raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") raise TxConfirmationTimedoutError(
"Could not confirm transaction on-chain."
)
############################### ###############################
# CrossShard Transaction RPCs # # CrossShard Transaction RPCs #
############################### ###############################
def get_pending_cx_receipts( def get_pending_cx_receipts(
endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Get list of pending cross shard transactions. """Get list of pending cross shard transactions.
@ -511,13 +560,17 @@ def get_pending_cx_receipts(
""" """
method = "hmyv2_getPendingCXReceipts" method = "hmyv2_getPendingCXReceipts"
try: try:
return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return rpc_request( method,
endpoint = endpoint,
timeout = timeout )[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def get_cx_receipt_by_hash( def get_cx_receipt_by_hash(
cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT cx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get cross shard receipt by hash on the receiving shard end point. """Get cross shard receipt by hash on the receiving shard end point.
@ -555,15 +608,20 @@ def get_cx_receipt_by_hash(
params = [ cx_hash ] params = [ cx_hash ]
method = "hmyv2_getCXReceiptByHash" method = "hmyv2_getCXReceiptByHash"
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def resend_cx_receipt( def resend_cx_receipt(
cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT cx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool: ) -> bool:
"""Resend the cross shard receipt to the receiving shard to re-process if """Resend the cross shard receipt to the receiving shard to re-process if
the transaction did not pay out. the transaction did not pay out.
@ -594,9 +652,12 @@ def resend_cx_receipt(
method = "hmyv2_resendCx" method = "hmyv2_resendCx"
params = [ cx_hash ] params = [ cx_hash ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
@ -605,7 +666,9 @@ def resend_cx_receipt(
# Staking Transaction RPCs # # Staking Transaction RPCs #
############################ ############################
def get_staking_transaction_by_hash( def get_staking_transaction_by_hash(
tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT tx_hash,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> dict: ) -> dict:
"""Get staking transaction by hash. """Get staking transaction by hash.
@ -648,15 +711,21 @@ def get_staking_transaction_by_hash(
method = "hmyv2_getStakingTransactionByHash" method = "hmyv2_getStakingTransactionByHash"
params = [ tx_hash ] params = [ tx_hash ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: 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( 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: ) -> dict:
"""Get staking transaction by block hash and transaction index. """Get staking transaction by block hash and transaction index.
@ -687,15 +756,21 @@ def get_staking_transaction_by_block_hash_and_index(
method = "hmyv2_getStakingTransactionByBlockHashAndIndex" method = "hmyv2_getStakingTransactionByBlockHashAndIndex"
params = [ block_hash, tx_index ] params = [ block_hash, tx_index ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: 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( 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: ) -> dict:
"""Get staking transaction by block number and transaction index. """Get staking transaction by block number and transaction index.
@ -726,15 +801,20 @@ def get_staking_transaction_by_block_number_and_index(
method = "hmyv2_getStakingTransactionByBlockNumberAndIndex" method = "hmyv2_getStakingTransactionByBlockNumberAndIndex"
params = [ block_num, tx_index ] params = [ block_num, tx_index ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def send_raw_staking_transaction( def send_raw_staking_transaction(
raw_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT raw_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> str: ) -> str:
"""Send signed staking transaction. """Send signed staking transaction.
@ -766,15 +846,20 @@ def send_raw_staking_transaction(
method = "hmyv2_sendRawStakingTransaction" method = "hmyv2_sendRawStakingTransaction"
params = [ raw_tx ] params = [ raw_tx ]
try: try:
return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ return rpc_request(
"result" method,
] params = params,
endpoint = endpoint,
timeout = timeout
)[ "result" ]
except KeyError as exception: except KeyError as exception:
raise InvalidRPCReplyError( method, endpoint ) from exception raise InvalidRPCReplyError( method, endpoint ) from exception
def send_and_confirm_raw_staking_transaction( def send_and_confirm_raw_staking_transaction(
signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT signed_tx,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> list: ) -> list:
"""Send signed staking transaction and wait for it to be confirmed. """Send signed staking transaction and wait for it to be confirmed.
@ -808,11 +893,16 @@ def send_and_confirm_raw_staking_transaction(
tx_hash = send_raw_staking_transaction( signed_tx, endpoint = endpoint ) tx_hash = send_raw_staking_transaction( signed_tx, endpoint = endpoint )
start_time = time.time() start_time = time.time()
while ( time.time() - start_time ) <= timeout: while ( time.time() - start_time ) <= timeout:
tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint) tx_response = get_staking_transaction_by_hash(
tx_hash,
endpoint = endpoint
)
if tx_response is not None: if tx_response is not None:
block_hash = tx_response.get( "blockHash", "0x00" ) block_hash = tx_response.get( "blockHash", "0x00" )
unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) )
if unique_chars != "0": if unique_chars != "0":
return tx_response return tx_response
time.sleep( random.uniform( 0.2, 0.5 ) ) time.sleep( random.uniform( 0.2, 0.5 ) )
raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") raise TxConfirmationTimedoutError(
"Could not confirm transaction on-chain."
)

@ -13,16 +13,13 @@ from eth_utils import to_checksum_address
from .blockchain import get_latest_header from .blockchain import get_latest_header
from .rpc.exceptions import ( from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError, )
RPCError,
RequestsError,
RequestsTimeoutError,
)
from .account import is_valid_address from .account import is_valid_address
from .bech32.bech32 import bech32_decode, bech32_encode, convertbits from .bech32.bech32 import bech32_decode, bech32_encode, convertbits
class Typgpy( str ): class Typgpy( str ):
"""Typography constants for pretty printing. """Typography constants for pretty printing.
@ -107,7 +104,8 @@ def convert_hex_to_one(addr):
return addr return addr
checksum_addr = str( to_checksum_address( addr ) ) checksum_addr = str( to_checksum_address( addr ) )
data = bytearray.fromhex( 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 ) buf = convertbits( data, 8, 5 )
return str( bech32_encode( "one", buf ) ) return str( bech32_encode( "one", buf ) )
@ -124,7 +122,8 @@ def is_active_shard(endpoint, delay_tolerance=60):
latest_header = get_latest_header( endpoint = endpoint ) latest_header = get_latest_header( endpoint = endpoint )
time_str = latest_header[ "timestamp" ][ : 19 ] + ".0" # Fit time format time_str = latest_header[ "timestamp" ][ : 19 ] + ".0" # Fit time format
timestamp = datetime.datetime.strptime( timestamp = datetime.datetime.strptime(
time_str, "%Y-%m-%d %H:%M:%S.%f" time_str,
"%Y-%m-%d %H:%M:%S.%f"
).replace( tzinfo = None ) ).replace( tzinfo = None )
time_delta = curr_time - timestamp time_delta = curr_time - timestamp
return abs( time_delta.seconds ) < delay_tolerance return abs( time_delta.seconds ) < delay_tolerance
@ -144,10 +143,11 @@ def get_bls_build_variables():
variables = {} variables = {}
try: try:
openssl_dir = ( openssl_dir = (
subprocess.check_output(["which", "openssl"]) subprocess.check_output(
.decode() [ "which",
.strip() "openssl" ]
.split("\n", maxsplit=1)[0] ).decode().strip().split( "\n",
maxsplit = 1 )[ 0 ]
) )
except ( IndexError, subprocess.CalledProcessError ) as exception: except ( IndexError, subprocess.CalledProcessError ) as exception:
raise RuntimeError( "`openssl` not found" ) from exception raise RuntimeError( "`openssl` not found" ) from exception
@ -161,8 +161,10 @@ def get_bls_build_variables():
"CGO_CFLAGS" "CGO_CFLAGS"
] = f"-I{bls_dir}/include -I{mcl_dir}/include -I{openssl_dir}/include" ] = f"-I{bls_dir}/include -I{mcl_dir}/include -I{openssl_dir}/include"
variables[ "CGO_LDFLAGS" ] = f"-L{bls_dir}/lib -L{openssl_dir}/lib" variables[ "CGO_LDFLAGS" ] = f"-L{bls_dir}/lib -L{openssl_dir}/lib"
variables["LD_LIBRARY_PATH"] = f"{bls_dir}/lib:{mcl_dir}/lib:{openssl_dir}/lib" variables[ "LD_LIBRARY_PATH"
variables["DYLD_FALLBACK_LIBRARY_PATH"] = variables["LD_LIBRARY_PATH"] ] = f"{bls_dir}/lib:{mcl_dir}/lib:{openssl_dir}/lib"
variables[ "DYLD_FALLBACK_LIBRARY_PATH" ] = variables[ "LD_LIBRARY_PATH"
]
else: else:
variables[ "CGO_CFLAGS" ] = f"-I{bls_dir}/include -I{mcl_dir}/include" variables[ "CGO_CFLAGS" ] = f"-I{bls_dir}/include -I{mcl_dir}/include"
variables[ "CGO_LDFLAGS" ] = f"-L{bls_dir}/lib" variables[ "CGO_LDFLAGS" ] = f"-L{bls_dir}/lib"

@ -22,11 +22,7 @@ from .constants import (
from .exceptions import InvalidValidatorError from .exceptions import InvalidValidatorError
from .rpc.exceptions import ( from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError, )
RPCError,
RequestsError,
RequestsTimeoutError,
)
from .staking import get_all_validator_addresses, get_validator_information 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 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 Harmony validator
""" """
def __init__( self, address ): def __init__( self, address ):
if not isinstance( address, str ): if not isinstance( address, str ):
raise InvalidValidatorError(1, "given ONE address was not a string") raise InvalidValidatorError(
1,
"given ONE address was not a string"
)
if not is_valid_address( address ): if not is_valid_address( address ):
raise InvalidValidatorError(1, f"{address} is not valid ONE address") raise InvalidValidatorError(
1,
f"{address} is not valid ONE address"
)
self._address = address self._address = address
self._bls_keys = [] self._bls_keys = []
self._bls_key_sigs = [] self._bls_key_sigs = []
@ -191,7 +193,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
name = self._sanitize_input( name ) name = self._sanitize_input( name )
if len( name ) > NAME_CHAR_LIMIT: if len( name ) > NAME_CHAR_LIMIT:
raise InvalidValidatorError( 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 self._name = name
@ -221,7 +224,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
identity = self._sanitize_input( identity ) identity = self._sanitize_input( identity )
if len( identity ) > IDENTITY_CHAR_LIMIT: if len( identity ) > IDENTITY_CHAR_LIMIT:
raise InvalidValidatorError( 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 self._identity = identity
@ -251,7 +255,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
website = self._sanitize_input( website ) website = self._sanitize_input( website )
if len( website ) > WEBSITE_CHAR_LIMIT: if len( website ) > WEBSITE_CHAR_LIMIT:
raise InvalidValidatorError( 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 self._website = website
@ -312,7 +317,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
details = self._sanitize_input( details ) details = self._sanitize_input( details )
if len( details ) > DETAILS_CHAR_LIMIT: if len( details ) > DETAILS_CHAR_LIMIT:
raise InvalidValidatorError( 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 self._details = details
@ -344,7 +350,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
delegation = Decimal( delegation ) delegation = Decimal( delegation )
except ( TypeError, InvalidOperation ) as exception: except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError( raise InvalidValidatorError(
3, "Min self delegation must be a number" 3,
"Min self delegation must be a number"
) from exception ) from exception
if delegation < MIN_REQUIRED_DELEGATION: if delegation < MIN_REQUIRED_DELEGATION:
raise InvalidValidatorError( raise InvalidValidatorError(
@ -381,7 +388,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
max_delegation = Decimal( max_delegation ) max_delegation = Decimal( max_delegation )
except ( TypeError, InvalidOperation ) as exception: except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError( raise InvalidValidatorError(
3, "Max total delegation must be a number" 3,
"Max total delegation must be a number"
) from exception ) from exception
if self._min_self_delegation: if self._min_self_delegation:
if max_delegation < self._min_self_delegation: if max_delegation < self._min_self_delegation:
@ -392,7 +400,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
) )
else: else:
raise InvalidValidatorError( 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 self._max_total_delegation = max_delegation
@ -423,7 +432,10 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
try: try:
amount = Decimal( amount ) amount = Decimal( amount )
except ( TypeError, InvalidOperation ) as exception: except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(3, "Amount must be a number") from exception raise InvalidValidatorError(
3,
"Amount must be a number"
) from exception
if self._min_self_delegation: if self._min_self_delegation:
if amount < self._min_self_delegation: if amount < self._min_self_delegation:
raise InvalidValidatorError( raise InvalidValidatorError(
@ -433,7 +445,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
) )
else: else:
raise InvalidValidatorError( 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 self._max_total_delegation:
if amount > self._max_total_delegation: if amount > self._max_total_delegation:
@ -444,7 +457,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
) )
else: else:
raise InvalidValidatorError( raise InvalidValidatorError(
4, "Max total delegation must be set before amount" 4,
"Max total delegation must be set before amount"
) )
self._inital_delegation = amount self._inital_delegation = amount
@ -475,7 +489,10 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
try: try:
rate = Decimal( rate ) rate = Decimal( rate )
except ( TypeError, InvalidOperation ) as exception: except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(3, "Max rate must be a number") from exception raise InvalidValidatorError(
3,
"Max rate must be a number"
) from exception
if rate < 0 or rate > 1: if rate < 0 or rate > 1:
raise InvalidValidatorError( 3, "Max rate must be between 0 and 1" ) raise InvalidValidatorError( 3, "Max rate must be between 0 and 1" )
self._max_rate = rate self._max_rate = rate
@ -507,10 +524,14 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
try: try:
rate = Decimal( rate ) rate = Decimal( rate )
except ( TypeError, InvalidOperation ) as exception: except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(3, "Max change rate must be a number") from exception raise InvalidValidatorError(
3,
"Max change rate must be a number"
) from exception
if rate < 0: if rate < 0:
raise InvalidValidatorError( 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 self._max_rate:
if rate > self._max_rate: if rate > self._max_rate:
@ -520,7 +541,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
) )
else: else:
raise InvalidValidatorError( 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 self._max_change_rate = rate
@ -551,13 +573,20 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
try: try:
rate = Decimal( rate ) rate = Decimal( rate )
except ( TypeError, InvalidOperation ) as exception: except ( TypeError, InvalidOperation ) as exception:
raise InvalidValidatorError(3, "Rate must be a number") from exception raise InvalidValidatorError(
3,
"Rate must be a number"
) from exception
if rate < 0: if rate < 0:
raise InvalidValidatorError(3, "Rate must be greater than or equal to 0") raise InvalidValidatorError(
3,
"Rate must be greater than or equal to 0"
)
if self._max_rate: if self._max_rate:
if rate > self._max_rate: if rate > self._max_rate:
raise InvalidValidatorError( 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: else:
raise InvalidValidatorError( 4, "Max rate must be set before rate" ) raise InvalidValidatorError( 4, "Max rate must be set before rate" )
@ -574,7 +603,9 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
return self._rate return self._rate
def does_validator_exist( def does_validator_exist(
self, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT self,
endpoint = DEFAULT_ENDPOINT,
timeout = DEFAULT_TIMEOUT
) -> bool: ) -> bool:
"""Check if validator exists on blockchain. """Check if validator exists on blockchain.
@ -653,10 +684,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
for key in info[ "bls-key-sigs" ]: for key in info[ "bls-key-sigs" ]:
self.add_bls_key_sig( key ) self.add_bls_key_sig( key )
except KeyError as exception: 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( 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 """Import validator information from blockchain with given address At
the moment, this is unable to fetch the BLS Signature, which is not the moment, this is unable to fetch the BLS Signature, which is not
@ -677,17 +713,24 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
try: try:
if not self.does_validator_exist( endpoint, timeout ): if not self.does_validator_exist( endpoint, timeout ):
raise InvalidValidatorError( 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( raise InvalidValidatorError(
5, "Error requesting validator information" 5,
"Error requesting validator information"
) from exception ) from exception
try: try:
validator_info = get_validator_information(self._address, endpoint, timeout) validator_info = get_validator_information(
self._address,
endpoint,
timeout
)
except ( RPCError, RequestsError, RequestsTimeoutError ) as exception: except ( RPCError, RequestsError, RequestsTimeoutError ) as exception:
raise InvalidValidatorError( raise InvalidValidatorError(
5, "Error requesting validator information" 5,
"Error requesting validator information"
) from exception ) from exception
# Skip additional sanity checks when importing from chain # Skip additional sanity checks when importing from chain
@ -711,7 +754,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
self._bls_keys = info[ "bls-public-keys" ] self._bls_keys = info[ "bls-public-keys" ]
except KeyError as exception: except KeyError as exception:
raise InvalidValidatorError( raise InvalidValidatorError(
5, "Error importing validator information from RPC result" 5,
"Error importing validator information from RPC result"
) from exception ) from exception
def export( self ) -> dict: def export( self ) -> dict:
@ -761,7 +805,9 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
""" """
info = self.export().copy() info = self.export().copy()
info[ "directive" ] = Directive.CreateValidator info[ "directive" ] = Directive.CreateValidator
info["validatorAddress"] = info.pop("validator-addr") # change the key info[ "validatorAddress" ] = info.pop(
"validator-addr"
) # change the key
info[ "nonce" ] = nonce info[ "nonce" ] = nonce
info[ "gasPrice" ] = gas_price info[ "gasPrice" ] = gas_price
info[ "gasLimit" ] = gas_limit info[ "gasLimit" ] = gas_limit
@ -802,7 +848,9 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public
self.remove_bls_key( bls_key_to_remove ) self.remove_bls_key( bls_key_to_remove )
info = self.export().copy() info = self.export().copy()
info[ "directive" ] = Directive.EditValidator info[ "directive" ] = Directive.EditValidator
info["validatorAddress"] = info.pop("validator-addr") # change the key info[ "validatorAddress" ] = info.pop(
"validator-addr"
) # change the key
info[ "nonce" ] = nonce info[ "nonce" ] = nonce
info[ "gasPrice" ] = gas_price info[ "gasPrice" ] = gas_price
info[ "gasLimit" ] = gas_limit info[ "gasLimit" ] = gas_limit

@ -46,7 +46,8 @@ def test_bad_bin_set():
def test_bin_set(): def test_bin_set():
cli.set_binary( BINARY_FILE_PATH ) cli.set_binary( BINARY_FILE_PATH )
cli_binary_path = cli.get_binary_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(): def test_update_keystore():

@ -13,8 +13,15 @@ def setup():
timeout = 30 timeout = 30
method = "hmyv2_getNodeMetadata" method = "hmyv2_getNodeMetadata"
params = [] params = []
payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} payload = {
headers = {"Content-Type": "application/json"} "id": "1",
"jsonrpc": "2.0",
"method": method,
"params": params
}
headers = {
"Content-Type": "application/json"
}
try: try:
response = requests.request( response = requests.request(
@ -26,7 +33,10 @@ def setup():
allow_redirects = True, allow_redirects = True,
) )
except Exception as e: except Exception as e:
pytest.skip("can not connect to local blockchain", allow_module_level=True) pytest.skip(
"can not connect to local blockchain",
allow_module_level = True
)
def test_request_connection_error(): def test_request_connection_error():
@ -42,7 +52,8 @@ def test_request_connection_error():
bad_request = None bad_request = None
try: try:
bad_request = request.rpc_request( bad_request = request.rpc_request(
"hmyv2_getNodeMetadata", endpoint=bad_endpoint "hmyv2_getNodeMetadata",
endpoint = bad_endpoint
) )
except Exception as e: except Exception as e:
assert isinstance( e, exceptions.RequestsError ) assert isinstance( e, exceptions.RequestsError )
@ -54,7 +65,10 @@ def test_request_rpc_error():
try: try:
error_request = request.rpc_request( "hmyv2_getBalance" ) error_request = request.rpc_request( "hmyv2_getBalance" )
except ( exceptions.RequestsTimeoutError, exceptions.RequestsError ) as err: except ( exceptions.RequestsTimeoutError, exceptions.RequestsError ) as err:
pytest.skip("can not connect to local blockchain", allow_module_level=True) pytest.skip(
"can not connect to local blockchain",
allow_module_level = True
)
except Exception as e: except Exception as e:
assert isinstance( e, exceptions.RPCError ) assert isinstance( e, exceptions.RPCError )
assert error_request is None assert error_request is None
@ -65,8 +79,15 @@ def test_rpc_request():
timeout = 30 timeout = 30
method = "hmyv2_getNodeMetadata" method = "hmyv2_getNodeMetadata"
params = [] params = []
payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} payload = {
headers = {"Content-Type": "application/json"} "id": "1",
"jsonrpc": "2.0",
"method": method,
"params": params
}
headers = {
"Content-Type": "application/json"
}
response = None response = None
try: try:

@ -11,7 +11,9 @@ import requests
endpoint = "http://localhost:9500" endpoint = "http://localhost:9500"
timeout = 30 timeout = 30
headers = {"Content-Type": "application/json"} headers = {
"Content-Type": "application/json"
}
txs = [ txs = [
# same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce) # same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce)
"0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3", "0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3",
@ -52,7 +54,8 @@ def setup_blockchain():
_send_transaction( tx, endpoint ) _send_transaction( tx, endpoint )
if not _wait_for_transaction_confirmed( tx_hash, endpoint ): if not _wait_for_transaction_confirmed( tx_hash, endpoint ):
pytest.skip( pytest.skip(
"Could not confirm initial transaction #{} on chain".format(i), "Could not confirm initial transaction #{} on chain"
.format( i ),
allow_module_level = True, allow_module_level = True,
) )
@ -62,7 +65,8 @@ def setup_blockchain():
_send_staking_transaction( stx, endpoint ) _send_staking_transaction( stx, endpoint )
if not _wait_for_staking_transaction_confirmed( stx_hash, endpoint ): if not _wait_for_staking_transaction_confirmed( stx_hash, endpoint ):
pytest.skip( pytest.skip(
"Could not confirm initial staking transaction #{} on chain".format(i), "Could not confirm initial staking transaction #{} on chain"
.format( i ),
allow_module_level = True, allow_module_level = True,
) )
@ -126,10 +130,14 @@ def _check_staking_epoch(metadata):
allow_module_level = True, allow_module_level = True,
) )
except Exception as e: except Exception as e:
pytest.skip("Failed to get hmyv2_latestHeader reply", allow_module_level=True) pytest.skip(
"Failed to get hmyv2_latestHeader reply",
allow_module_level = True
)
if metadata and latest_header: if metadata and latest_header:
staking_epoch = metadata["result"]["chain-config"]["staking-epoch"] staking_epoch = metadata[ "result" ][ "chain-config" ][ "staking-epoch"
]
current_epoch = latest_header[ "result" ][ "epoch" ] current_epoch = latest_header[ "result" ][ "epoch" ]
if staking_epoch > current_epoch: if staking_epoch > current_epoch:
pytest.skip( pytest.skip(
@ -162,7 +170,8 @@ def _send_transaction(raw_tx, endpoint):
) )
except Exception as e: except Exception as e:
pytest.skip( pytest.skip(
"Failed to get hmyv2_sendRawTransaction reply", allow_module_level=True "Failed to get hmyv2_sendRawTransaction reply",
allow_module_level = True
) )
@ -186,7 +195,8 @@ def _check_transaction(tx_hash, endpoint):
return tx_data return tx_data
except Exception as e: except Exception as e:
pytest.skip( pytest.skip(
"Failed to get hmyv2_getTransactionByHash reply", allow_module_level=True "Failed to get hmyv2_getTransactionByHash reply",
allow_module_level = True
) )

@ -5,7 +5,6 @@ from pyhmy import account
from pyhmy.rpc import exceptions from pyhmy.rpc import exceptions
explorer_endpoint = "http://localhost:9700" explorer_endpoint = "http://localhost:9700"
endpoint_shard_one = "http://localhost:9502" endpoint_shard_one = "http://localhost:9502"
local_test_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" local_test_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
@ -22,8 +21,8 @@ def _test_account_rpc(fn, *args, **kwargs):
try: try:
response = fn( *args, **kwargs ) response = fn( *args, **kwargs )
except Exception as e: except Exception as e:
if isinstance( if isinstance( e,
e, exceptions.RPCError exceptions.RPCError
) and "does not exist/is not available" in str( e ): ) and "does not exist/is not available" in str( e ):
pytest.skip( f"{str(e)}" ) pytest.skip( f"{str(e)}" )
pytest.fail( f"Unexpected error: {e.__class__} {e}" ) pytest.fail( f"Unexpected error: {e.__class__} {e}" )
@ -38,7 +37,9 @@ def test_get_balance(setup_blockchain):
def test_get_balance_by_block( setup_blockchain ): def test_get_balance_by_block( setup_blockchain ):
balance = _test_account_rpc( 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 assert balance > 0
@ -56,7 +57,9 @@ def test_get_account_nonce(setup_blockchain):
def test_get_transaction_history( setup_blockchain ): def test_get_transaction_history( setup_blockchain ):
tx_history = _test_account_rpc( 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 isinstance( tx_history, list )
assert len( tx_history ) >= 0 assert len( tx_history ) >= 0
@ -73,25 +76,38 @@ def test_get_staking_transaction_history(setup_blockchain):
def test_get_balance_on_all_shards( setup_blockchain ): def test_get_balance_on_all_shards( setup_blockchain ):
balances = _test_account_rpc(account.get_balance_on_all_shards, local_test_address) balances = _test_account_rpc(
account.get_balance_on_all_shards,
local_test_address
)
assert isinstance( balances, list ) assert isinstance( balances, list )
assert len( balances ) == 2 assert len( balances ) == 2
def test_get_total_balance( setup_blockchain ): def test_get_total_balance( setup_blockchain ):
total_balance = _test_account_rpc(account.get_total_balance, local_test_address) total_balance = _test_account_rpc(
account.get_total_balance,
local_test_address
)
assert isinstance( total_balance, int ) assert isinstance( total_balance, int )
assert total_balance > 0 assert total_balance > 0
def test_is_valid_address(): def test_is_valid_address():
assert account.is_valid_address("one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur") assert account.is_valid_address(
assert not account.is_valid_address("one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0") "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( 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 assert tx_count > 0
@ -99,7 +115,10 @@ def test_get_transaction_count(setup_blockchain):
def test_get_transactions_count( setup_blockchain ): def test_get_transactions_count( setup_blockchain ):
tx_count = _test_account_rpc( 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
) )

@ -5,7 +5,6 @@ from pyhmy import blockchain
from pyhmy.rpc import exceptions from pyhmy.rpc import exceptions
test_epoch_number = 0 test_epoch_number = 0
genesis_block_number = 0 genesis_block_number = 0
test_block_number = 1 test_block_number = 1
@ -21,8 +20,8 @@ def _test_blockchain_rpc(fn, *args, **kwargs):
try: try:
response = fn( *args, **kwargs ) response = fn( *args, **kwargs )
except Exception as e: except Exception as e:
if isinstance( if isinstance( e,
e, exceptions.RPCError exceptions.RPCError
) and "does not exist/is not available" in str( e ): ) and "does not exist/is not available" in str( e ):
pytest.skip( f"{str(e)}" ) pytest.skip( f"{str(e)}" )
pytest.fail( f"Unexpected error: {e.__class__} {e}" ) pytest.fail( f"Unexpected error: {e.__class__} {e}" )
@ -35,7 +34,9 @@ def test_get_node_metadata(setup_blockchain):
def test_get_sharding_structure( setup_blockchain ): def test_get_sharding_structure( setup_blockchain ):
sharding_structure = _test_blockchain_rpc(blockchain.get_sharding_structure) sharding_structure = _test_blockchain_rpc(
blockchain.get_sharding_structure
)
assert isinstance( sharding_structure, list ) assert isinstance( sharding_structure, list )
assert len( sharding_structure ) > 0 assert len( sharding_structure ) > 0
@ -78,7 +79,10 @@ def test_get_latest_chain_headers(setup_blockchain):
def test_get_block_by_number( setup_blockchain ): def test_get_block_by_number( setup_blockchain ):
global test_block_hash global test_block_hash
block = _test_blockchain_rpc(blockchain.get_block_by_number, test_block_number) block = _test_blockchain_rpc(
blockchain.get_block_by_number,
test_block_number
)
assert isinstance( block, dict ) assert isinstance( block, dict )
assert "hash" in block.keys() assert "hash" in block.keys()
test_block_hash = block[ "hash" ] test_block_hash = block[ "hash" ]
@ -87,13 +91,17 @@ def test_get_block_by_number(setup_blockchain):
def test_get_block_by_hash( setup_blockchain ): def test_get_block_by_hash( setup_blockchain ):
if not test_block_hash: if not test_block_hash:
pytest.skip( "Failed to get reference block hash" ) pytest.skip( "Failed to get reference block hash" )
block = _test_blockchain_rpc(blockchain.get_block_by_hash, test_block_hash) block = _test_blockchain_rpc(
blockchain.get_block_by_hash,
test_block_hash
)
assert isinstance( block, dict ) assert isinstance( block, dict )
def test_get_block_transaction_count_by_number( setup_blockchain ): def test_get_block_transaction_count_by_number( setup_blockchain ):
tx_count = _test_blockchain_rpc( 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 )
@ -102,14 +110,17 @@ def test_get_block_transaction_count_by_hash(setup_blockchain):
if not test_block_hash: if not test_block_hash:
pytest.skip( "Failed to get reference block hash" ) pytest.skip( "Failed to get reference block hash" )
tx_count = _test_blockchain_rpc( 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( 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 isinstance( blocks, list )
assert len( blocks ) == ( test_block_number - genesis_block_number + 1 ) assert len( blocks ) == ( test_block_number - genesis_block_number + 1 )
@ -117,14 +128,18 @@ def test_get_blocks(setup_blockchain):
def test_get_block_signers( setup_blockchain ): def test_get_block_signers( setup_blockchain ):
block_signers = _test_blockchain_rpc( 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 isinstance( block_signers, list )
assert len( block_signers ) > 0 assert len( block_signers ) > 0
def test_get_validators( setup_blockchain ): def test_get_validators( setup_blockchain ):
validators = _test_blockchain_rpc(blockchain.get_validators, test_epoch_number) validators = _test_blockchain_rpc(
blockchain.get_validators,
test_epoch_number
)
assert isinstance( validators, dict ) assert isinstance( validators, dict )
assert "validators" in validators.keys() assert "validators" in validators.keys()
assert len( validators[ "validators" ] ) > 0 assert len( validators[ "validators" ] ) > 0
@ -154,13 +169,19 @@ def test_get_bad_blocks(setup_blockchain):
def test_get_validator_keys( setup_blockchain ): def test_get_validator_keys( setup_blockchain ):
keys = _test_blockchain_rpc(blockchain.get_validator_keys, test_epoch_number) keys = _test_blockchain_rpc(
blockchain.get_validator_keys,
test_epoch_number
)
assert isinstance( keys, list ) assert isinstance( keys, list )
assert len( keys ) > 0 assert len( keys ) > 0
def test_get_block_signers_keys( setup_blockchain ): def test_get_block_signers_keys( setup_blockchain ):
keys = _test_blockchain_rpc(blockchain.get_block_signers_keys, test_block_number) keys = _test_blockchain_rpc(
blockchain.get_block_signers_keys,
test_block_number
)
assert isinstance( keys, list ) assert isinstance( keys, list )
assert len( keys ) > 0 assert len( keys ) > 0
@ -192,7 +213,9 @@ def test_epoch_last_block(setup_blockchain):
def test_get_circulating_supply( setup_blockchain ): def test_get_circulating_supply( setup_blockchain ):
circulating_supply = _test_blockchain_rpc(blockchain.get_circulating_supply) circulating_supply = _test_blockchain_rpc(
blockchain.get_circulating_supply
)
assert isinstance( circulating_supply, str ) assert isinstance( circulating_supply, str )
@ -223,7 +246,8 @@ def test_get_header_by_number(setup_blockchain):
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( 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 )
@ -232,20 +256,26 @@ def test_get_block_staking_transaction_count_by_hash(setup_blockchain):
if not test_block_hash: if not test_block_hash:
pytest.skip( "Failed to get reference block hash" ) pytest.skip( "Failed to get reference block hash" )
tx_count = _test_blockchain_rpc( 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( 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 ): def test_get_signed_blocks( setup_blockchain ):
signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address) signed_blocks = _test_blockchain_rpc(
blockchain.get_signed_blocks,
address
)
assert isinstance( signed_blocks, int ) assert isinstance( signed_blocks, int )
@ -313,7 +343,10 @@ def test_errors():
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
blockchain.get_block_transaction_count_by_hash( "", fake_shard ) blockchain.get_block_transaction_count_by_hash( "", fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
blockchain.get_block_staking_transaction_count_by_number(0, fake_shard) blockchain.get_block_staking_transaction_count_by_number(
0,
fake_shard
)
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
blockchain.get_block_staking_transaction_count_by_hash( "", fake_shard ) blockchain.get_block_staking_transaction_count_by_hash( "", fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):

@ -19,11 +19,13 @@ def _test_contract_rpc(fn, *args, **kwargs):
try: try:
response = fn( *args, **kwargs ) response = fn( *args, **kwargs )
except Exception as e: except Exception as e:
if isinstance( if isinstance( e,
e, exceptions.RPCError exceptions.RPCError
) and "does not exist/is not available" in str( e ): ) and "does not exist/is not available" in str( e ):
pytest.skip( f"{str(e)}" ) pytest.skip( f"{str(e)}" )
elif isinstance(e, exceptions.RPCError) and "estimateGas returned" in str(e): elif isinstance( e,
exceptions.RPCError
) and "estimateGas returned" in str( e ):
pytest.skip( f"{str(e)}" ) pytest.skip( f"{str(e)}" )
pytest.fail( f"Unexpected error: {e.__class__} {e}" ) pytest.fail( f"Unexpected error: {e.__class__} {e}" )
return response return response
@ -32,7 +34,8 @@ def _test_contract_rpc(fn, *args, **kwargs):
def test_get_contract_address_from_hash( setup_blockchain ): def test_get_contract_address_from_hash( setup_blockchain ):
global contract_address global contract_address
contract_address = _test_contract_rpc( contract_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 )
@ -62,7 +65,10 @@ def test_get_storage_at(setup_blockchain):
if not contract_address: if not contract_address:
pytest.skip( "Contract address not loaded yet" ) pytest.skip( "Contract address not loaded yet" )
storage = _test_contract_rpc( 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" )

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

@ -5,7 +5,6 @@ from pyhmy import staking
from pyhmy.rpc import exceptions from pyhmy.rpc import exceptions
explorer_endpoint = "http://localhost:9700" explorer_endpoint = "http://localhost:9700"
test_validator_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" test_validator_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3"
fake_shard = "http://example.com" fake_shard = "http://example.com"
@ -18,8 +17,8 @@ def _test_staking_rpc(fn, *args, **kwargs):
try: try:
response = fn( *args, **kwargs ) response = fn( *args, **kwargs )
except Exception as e: except Exception as e:
if isinstance( if isinstance( e,
e, exceptions.RPCError exceptions.RPCError
) and "does not exist/is not available" in str( e ): ) and "does not exist/is not available" in str( e ):
pytest.skip( f"{str(e)}" ) pytest.skip( f"{str(e)}" )
pytest.fail( f"Unexpected error: {e.__class__} {e}" ) pytest.fail( f"Unexpected error: {e.__class__} {e}" )
@ -27,26 +26,34 @@ def _test_staking_rpc(fn, *args, **kwargs):
def test_get_all_validator_addresses( setup_blockchain ): def test_get_all_validator_addresses( setup_blockchain ):
validator_addresses = _test_staking_rpc(staking.get_all_validator_addresses) validator_addresses = _test_staking_rpc(
staking.get_all_validator_addresses
)
assert isinstance( validator_addresses, list ) assert isinstance( validator_addresses, list )
assert len( validator_addresses ) > 0 assert len( validator_addresses ) > 0
assert test_validator_address in validator_addresses assert test_validator_address in validator_addresses
def test_get_validator_information( setup_blockchain ): def test_get_validator_information( setup_blockchain ):
info = _test_staking_rpc(staking.get_validator_information, test_validator_address) info = _test_staking_rpc(
staking.get_validator_information,
test_validator_address
)
assert isinstance( info, dict ) assert isinstance( info, dict )
def test_get_all_validator_information( setup_blockchain ): def test_get_all_validator_information( setup_blockchain ):
all_validator_information = _test_staking_rpc(staking.get_all_validator_information) all_validator_information = _test_staking_rpc(
staking.get_all_validator_information
)
assert isinstance( all_validator_information, list ) assert isinstance( all_validator_information, list )
assert len( all_validator_information ) > 0 assert len( all_validator_information ) > 0
def test_get_delegations_by_delegator( setup_blockchain ): def test_get_delegations_by_delegator( setup_blockchain ):
delegations = _test_staking_rpc( 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 isinstance( delegations, list )
assert len( delegations ) > 0 assert len( delegations ) > 0
@ -54,7 +61,8 @@ def test_get_delegations_by_delegator(setup_blockchain):
def test_get_delegations_by_validator( setup_blockchain ): def test_get_delegations_by_validator( setup_blockchain ):
delegations = _test_staking_rpc( 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 isinstance( delegations, list )
assert len( delegations ) > 0 assert len( delegations ) > 0
@ -112,7 +120,9 @@ def test_get_delegations_by_delegator_by_block(setup_blockchain):
def test_get_elected_validator_addresses( setup_blockchain ): def test_get_elected_validator_addresses( setup_blockchain ):
validator_addresses = _test_staking_rpc(staking.get_elected_validator_addresses) validator_addresses = _test_staking_rpc(
staking.get_elected_validator_addresses
)
assert isinstance( validator_addresses, list ) assert isinstance( validator_addresses, list )
assert len( validator_addresses ) > 0 assert len( validator_addresses ) > 0
@ -130,7 +140,8 @@ def test_get_validator_keys(setup_blockchain):
def test_get_validator_self_delegation( setup_blockchain ): def test_get_validator_self_delegation( setup_blockchain ):
self_delegation = _test_staking_rpc( 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 assert self_delegation > 0
@ -138,7 +149,8 @@ def test_get_validator_self_delegation(setup_blockchain):
def test_get_validator_total_delegation( setup_blockchain ): def test_get_validator_total_delegation( setup_blockchain ):
total_delegation = _test_staking_rpc( 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 assert total_delegation > 0
@ -146,7 +158,8 @@ def test_get_validator_total_delegation(setup_blockchain):
def test_get_all_delegation_information( setup_blockchain ): def test_get_all_delegation_information( setup_blockchain ):
delegation_information = _test_staking_rpc( delegation_information = _test_staking_rpc(
staking.get_all_delegation_information, 0 staking.get_all_delegation_information,
0
) )
assert isinstance( delegation_information, list ) assert isinstance( delegation_information, list )
assert len( delegation_information ) > 0 assert len( delegation_information ) > 0
@ -163,7 +176,8 @@ def test_get_delegation_by_delegator_and_validator(setup_blockchain):
def test_get_available_redelegation_balance( setup_blockchain ): def test_get_available_redelegation_balance( setup_blockchain ):
redelgation_balance = _test_staking_rpc( 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 assert redelgation_balance == 0
@ -173,10 +187,10 @@ def test_get_total_staking(setup_blockchain):
total_staking = _test_staking_rpc( staking.get_total_staking ) total_staking = _test_staking_rpc( staking.get_total_staking )
assert isinstance( total_staking, int ) assert isinstance( total_staking, int )
if ( if (
staking.get_validator_information(test_validator_address, explorer_endpoint)[ staking.get_validator_information(
"active-status" test_validator_address,
] explorer_endpoint
== "active" )[ "active-status" ] == "active"
): ):
assert total_staking > 0 assert total_staking > 0
@ -201,13 +215,21 @@ def test_errors():
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
staking.get_validator_total_delegation( "", fake_shard ) staking.get_validator_total_delegation( "", fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
staking.get_all_validator_information_by_block_number(1, 1, fake_shard) staking.get_all_validator_information_by_block_number(
1,
1,
fake_shard
)
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
staking.get_all_delegation_information( 1, fake_shard ) staking.get_all_delegation_information( 1, fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
staking.get_delegations_by_delegator( "", fake_shard ) staking.get_delegations_by_delegator( "", fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
staking.get_delegations_by_delegator_by_block_number("", 1, fake_shard) staking.get_delegations_by_delegator_by_block_number(
"",
1,
fake_shard
)
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
staking.get_delegation_by_delegator_and_validator( "", "", fake_shard ) staking.get_delegation_by_delegator_and_validator( "", "", fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):

@ -5,7 +5,6 @@ from pyhmy.numbers import convert_one_to_atto
# other transactions (create/edit validator) are in test_validator.py # other transactions (create/edit validator) are in test_validator.py
# test_delegate is the same as test_undelegate (except the directive) so it has been omitted # test_delegate is the same as test_undelegate (except the directive) so it has been omitted
# staking transactions without a chain id have been omitted as well, since the node does not accept them anyway # staking transactions without a chain id have been omitted as well, since the node does not accept them anyway
""" """
let stakingTx let stakingTx
let stakeMsg3: CollectRewards = new CollectRewards( let stakeMsg3: CollectRewards = new CollectRewards(
@ -33,7 +32,6 @@ console.log(signed)
# } # }
# signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') # signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48')
# assert signed_tx.rawTransaction.hex() == '0xf85a04d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5823a0490e4ceb747563ba40da3e0db8a65133cf6f6ae4c48a24866cd6aa1f0d6c2414a06dbd51a67b35b5685e7b7420cba26e63b0e7d3c696fc6cb69d48e54fcad280e9' # assert signed_tx.rawTransaction.hex() == '0xf85a04d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5823a0490e4ceb747563ba40da3e0db8a65133cf6f6ae4c48a24866cd6aa1f0d6c2414a06dbd51a67b35b5685e7b7420cba26e63b0e7d3c696fc6cb69d48e54fcad280e9'
""" """
let stakingTx let stakingTx
let stakeMsg3: CollectRewards = new CollectRewards( let stakeMsg3: CollectRewards = new CollectRewards(
@ -67,8 +65,8 @@ def test_collect_rewards_chain_id():
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
) )
assert ( assert (
signed_tx.rawTransaction.hex() signed_tx.rawTransaction.hex() ==
== "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099" "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099"
) )
@ -109,6 +107,6 @@ def test_delegate():
"4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48",
) )
assert ( assert (
signed_tx.rawTransaction.hex() signed_tx.rawTransaction.hex() ==
== "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6" "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6"
) )

@ -4,7 +4,6 @@ from pyhmy import transaction
from pyhmy.rpc import exceptions from pyhmy.rpc import exceptions
endpoint = "http://localhost:9500" endpoint = "http://localhost:9500"
endpoint_shard_one = "http://localhost:9502" endpoint_shard_one = "http://localhost:9502"
fake_shard = "http://example.com" fake_shard = "http://example.com"
@ -37,8 +36,8 @@ def _test_transaction_rpc(fn, *args, **kwargs):
try: try:
response = fn( *args, **kwargs ) response = fn( *args, **kwargs )
except Exception as e: except Exception as e:
if isinstance( if isinstance( e,
e, exceptions.RPCError exceptions.RPCError
) and "does not exist/is not available" in str( e ): ) and "does not exist/is not available" in str( e ):
pytest.skip( f"{str(e)}" ) pytest.skip( f"{str(e)}" )
pytest.fail( f"Unexpected error: {e.__class__} {e}" ) pytest.fail( f"Unexpected error: {e.__class__} {e}" )
@ -52,7 +51,9 @@ def test_get_pending_transactions(setup_blockchain):
def test_get_transaction_by_hash( setup_blockchain ): def test_get_transaction_by_hash( setup_blockchain ):
tx = _test_transaction_rpc( 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 tx
assert isinstance( tx, dict ) assert isinstance( tx, dict )
@ -94,7 +95,9 @@ def test_get_transaction_by_block_number_and_index(setup_blockchain):
def test_get_transaction_receipt( setup_blockchain ): def test_get_transaction_receipt( setup_blockchain ):
tx_receipt = _test_transaction_rpc( 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 tx_receipt
assert isinstance( tx_receipt, dict ) assert isinstance( tx_receipt, dict )
@ -108,7 +111,8 @@ def test_get_transaction_error_sink(setup_blockchain):
def test_send_and_confirm_raw_transaction( setup_blockchain ): def test_send_and_confirm_raw_transaction( setup_blockchain ):
# Note: this test is not yet idempotent since the localnet will reject transactions which were previously finalized. # Note: this test is not yet idempotent since the localnet will reject transactions which were previously finalized.
test_tx = _test_transaction_rpc( 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 isinstance( test_tx, dict )
assert test_tx[ "hash" ] == raw_tx_hash assert test_tx[ "hash" ] == raw_tx_hash
@ -121,7 +125,9 @@ def test_get_pending_cx_receipts(setup_blockchain):
def test_get_cx_receipt_by_hash( setup_blockchain ): def test_get_cx_receipt_by_hash( setup_blockchain ):
cx = _test_transaction_rpc( 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 cx
assert isinstance( cx, dict ) assert isinstance( cx, dict )
@ -135,7 +141,8 @@ def test_resend_cx_receipt(setup_blockchain):
def test_get_staking_transaction_by_hash( setup_blockchain ): def test_get_staking_transaction_by_hash( setup_blockchain ):
staking_tx = _test_transaction_rpc( 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 staking_tx
assert isinstance( staking_tx, dict ) assert isinstance( staking_tx, dict )
@ -174,13 +181,17 @@ def test_get_transaction_by_block_number_and_index(setup_blockchain):
def test_get_staking_transaction_error_sink( setup_blockchain ): def test_get_staking_transaction_error_sink( setup_blockchain ):
errors = _test_transaction_rpc(transaction.get_staking_transaction_error_sink) errors = _test_transaction_rpc(
transaction.get_staking_transaction_error_sink
)
assert isinstance( errors, list ) assert isinstance( errors, list )
def test_send_raw_staking_transaction( setup_blockchain ): def test_send_raw_staking_transaction( setup_blockchain ):
test_stx = _test_transaction_rpc( 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 isinstance( test_stx, dict )
assert test_stx[ "hash" ] == raw_stx_hash assert test_stx[ "hash" ] == raw_stx_hash
@ -188,14 +199,16 @@ def test_send_raw_staking_transaction(setup_blockchain):
def test_get_pool_stats( setup_blockchain ): def test_get_pool_stats( setup_blockchain ):
test_pool_stats = _test_transaction_rpc( 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( 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 )
@ -210,9 +223,17 @@ def test_errors():
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
transaction.get_transaction_by_hash( "", endpoint = fake_shard ) transaction.get_transaction_by_hash( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
transaction.get_transaction_by_block_hash_and_index("", 1, endpoint=fake_shard) transaction.get_transaction_by_block_hash_and_index(
"",
1,
endpoint = fake_shard
)
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
transaction.get_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard) transaction.get_transaction_by_block_number_and_index(
1,
1,
endpoint = fake_shard
)
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
transaction.get_transaction_receipt( "", endpoint = fake_shard ) transaction.get_transaction_receipt( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
@ -227,11 +248,15 @@ def test_errors():
transaction.get_staking_transaction_by_hash( "", endpoint = fake_shard ) transaction.get_staking_transaction_by_hash( "", endpoint = fake_shard )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
transaction.get_staking_transaction_by_block_hash_and_index( 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( transaction.get_staking_transaction_by_block_number_and_index(
1, 1, endpoint=fake_shard 1,
1,
endpoint = fake_shard
) )
with pytest.raises( exceptions.RPCError ): with pytest.raises( exceptions.RPCError ):
transaction.get_staking_transaction_error_sink( endpoint = fake_shard ) transaction.get_staking_transaction_error_sink( endpoint = fake_shard )

@ -89,8 +89,8 @@ def test_create_validator_sign(setup_blockchain):
2, 2,
).rawTransaction.hex() ).rawTransaction.hex()
assert ( assert (
signed_hash signed_hash ==
== "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd" "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd"
) )
@ -152,8 +152,8 @@ def test_edit_validator_sign(setup_blockchain):
2, 2,
).rawTransaction.hex() ).rawTransaction.hex()
assert ( assert (
signed_hash signed_hash ==
== "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995" "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995"
) )
@ -193,8 +193,8 @@ def test_validator_getters(setup_blockchain):
if not ( test_validator_object or test_validator_loaded ): if not ( test_validator_object or test_validator_loaded ):
pytest.skip( "Validator not instantiated yet" ) pytest.skip( "Validator not instantiated yet" )
assert ( assert (
test_validator_object.get_address() test_validator_object.get_address() ==
== "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9"
) )
assert test_validator_object.add_bls_key( "5" ) assert test_validator_object.add_bls_key( "5" )
assert test_validator_object.remove_bls_key( "5" ) assert test_validator_object.remove_bls_key( "5" )
@ -203,8 +203,14 @@ def test_validator_getters(setup_blockchain):
assert test_validator_object.get_website() == "alice.harmony.one" assert test_validator_object.get_website() == "alice.harmony.one"
assert test_validator_object.get_security_contact() == "Bob" assert test_validator_object.get_security_contact() == "Bob"
assert test_validator_object.get_details() == "Don't mess with me!!!" assert test_validator_object.get_details() == "Don't mess with me!!!"
assert isinstance(test_validator_object.get_min_self_delegation(), Decimal) assert isinstance(
assert isinstance(test_validator_object.get_max_total_delegation(), Decimal) 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_amount(), Decimal )
assert isinstance( test_validator_object.get_max_rate(), 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_max_change_rate(), Decimal )

@ -22,7 +22,12 @@ def test_json_load():
dec = util.json_load( "1.1", parse_float = decimal.Decimal ) dec = util.json_load( "1.1", parse_float = decimal.Decimal )
assert isinstance( dec, decimal.Decimal ) assert isinstance( dec, decimal.Decimal )
assert float( dec ) == 1.1 assert float( dec ) == 1.1
ref_dict = {"test": "val", "arr": [1, 2, 3]} ref_dict = {
"test": "val",
"arr": [ 1,
2,
3 ]
}
loaded_dict = util.json_load( json.dumps( ref_dict ) ) loaded_dict = util.json_load( json.dumps( ref_dict ) )
assert str( ref_dict ) == str( loaded_dict ) assert str( ref_dict ) == str( loaded_dict )

Loading…
Cancel
Save