From 8b62d70d4acdbc4653f2194ac6a648512e5f00ad Mon Sep 17 00:00:00 2001 From: Max <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 22 Oct 2021 18:36:51 +0000 Subject: [PATCH 01/23] Create __init__.py --- pyhmy/bech32/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 pyhmy/bech32/__init__.py diff --git a/pyhmy/bech32/__init__.py b/pyhmy/bech32/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyhmy/bech32/__init__.py @@ -0,0 +1 @@ + From 9f5e912b229cd3b74fa944eb970c2de8e99450bf Mon Sep 17 00:00:00 2001 From: Max <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 22 Oct 2021 18:39:22 +0000 Subject: [PATCH 02/23] transaction.py: use correct endpoint Fix for ` send_and_confirm_raw_transaction` --- pyhmy/transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index 930adf6..a8c1786 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -430,7 +430,7 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time ------------- https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0 """ - tx_hash = send_raw_transaction(signed_tx) + tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint) start_time = time.time() while((time.time() - start_time) <= timeout): tx_response = get_transaction_by_hash(tx_hash) From c81f9eff411b7f557e7043c6cf83da221bec88c5 Mon Sep 17 00:00:00 2001 From: Max <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 22 Oct 2021 18:41:25 +0000 Subject: [PATCH 03/23] transaction.py: use correct endpoint Fix for `send_and_confirm_raw_transaction` in getting tx_hash --- pyhmy/transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index a8c1786..e256c8c 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -433,7 +433,7 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint) start_time = time.time() while((time.time() - start_time) <= timeout): - tx_response = get_transaction_by_hash(tx_hash) + tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint) if tx_response is not None: if tx_response[ 'blockHash' ] != '0x0000000000000000000000000000000000000000000000000000000000000000': return tx_response From 4f266619faa4924f64d4de4e48b919ee00e5f905 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Sat, 30 Oct 2021 00:34:41 +0530 Subject: [PATCH 04/23] Add signature options to validator signing The JSON RPC API has changed and now demands a BLS signature with the BLS key(s) to be added. --- pyhmy/staking_signing.py | 5 +++ pyhmy/staking_structures.py | 1 + pyhmy/validator.py | 55 +++++++++++++++++++++++++++++-- tests/sdk-pyhmy/test_validator.py | 20 +++++++---- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index d1cd69a..1b6a203 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -271,6 +271,9 @@ def _sign_create_validator(transaction_dict, private_key): bls_keys = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter sanitized_transaction.pop('bls-public-keys') ) + bls_key_sigs = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter + sanitized_transaction.pop('bls-key-sigs') + ) sanitized_transaction['stakeMsg'] = \ apply_formatters_to_sequence( [ hexstr_if_str(to_bytes), # address @@ -279,6 +282,7 @@ def _sign_create_validator(transaction_dict, private_key): hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped identity, # bls public keys + identity, # bls key sigs hexstr_if_str(to_int), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) ], [ convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), @@ -287,6 +291,7 @@ def _sign_create_validator(transaction_dict, private_key): math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly math.floor(sanitized_transaction.pop('max-total-delegation')), bls_keys, + bls_key_sigs, math.floor(sanitized_transaction.pop('amount')), ] ) diff --git a/pyhmy/staking_structures.py b/pyhmy/staking_structures.py index de64ed7..d8907f3 100644 --- a/pyhmy/staking_structures.py +++ b/pyhmy/staking_structures.py @@ -134,6 +134,7 @@ class CreateValidator: big_endian_int, # min self delegation big_endian_int, # max total delegation CountableList(Binary.fixed_length(48, allow_empty=True)), # bls-public-keys array of unspecified length, each key of 48 + CountableList(Binary.fixed_length(96, allow_empty=True)), # bls-key-sigs array of unspecified length, each sig of 96 big_endian_int, # amount ], True)), # strictly these number of elements ('nonce', big_endian_int), diff --git a/pyhmy/validator.py b/pyhmy/validator.py index 111644a..ccc2b14 100644 --- a/pyhmy/validator.py +++ b/pyhmy/validator.py @@ -58,6 +58,7 @@ class Validator: raise InvalidValidatorError(1, f'{address} is not valid ONE address') self._address = address self._bls_keys = [] + self._bls_key_sigs = [] self._name = None self._identity = None @@ -151,6 +152,47 @@ class Validator: """ return self._bls_keys + def add_bls_key_sig(self, key) -> bool: + """ + Add BLS public key to validator BLS keys if not already in list + + Returns + ------- + bool + If adding BLS key succeeded + """ + key = self._sanitize_input(key) + if key not in self._bls_key_sigs: + self._bls_key_sigs.append(key) + return True + return False + + def remove_bls_key_sig(self, key) -> bool: + """ + Remove BLS public key from validator BLS keys if exists + + Returns + ------- + bool + If removing BLS key succeeded + """ + key = self._sanitize_input(key) + if key in self._bls_key_sigs: + self._bls_key_sigs.remove(key) + return True + return False + + def get_bls_key_sigs(self) -> list: + """ + Get list of validator BLS keys + + Returns + ------- + list + List of validator BLS keys (strings) + """ + return self._bls_key_sigs + def set_name(self, name): """ Set validator name @@ -578,6 +620,7 @@ class Validator: "max-rate": '0', "max-change-rate": '0', "bls-public-keys": [ "" ] + "bls-key-sigs": [ "" ] } Raises @@ -603,12 +646,18 @@ class Validator: self._bls_keys = [] for key in info['bls-public-keys']: self.add_bls_key(key) + + self._bls_key_sigs = [] + for key in info['bls-key-sigs']: + self.add_bls_key_sig(key) except KeyError as e: raise InvalidValidatorError(3, 'Info has missing key') from e def load_from_blockchain(self, endpoint=_default_endpoint, timeout=_default_timeout): """ Import validator information from blockchain with given address + At the moment, this is unable to fetch the BLS Signature, which is not implemented + in the Node API Parameters ---------- @@ -674,7 +723,8 @@ class Validator: "rate": self._rate, "max-rate": self._max_rate, "max-change-rate": self._max_change_rate, - "bls-public-keys": self._bls_keys + "bls-public-keys": self._bls_keys, + "bls-key-sigs": self._bls_key_sigs } return info @@ -705,7 +755,7 @@ class Validator: info['chainId'] = chain_id return sign_staking_transaction(info, private_key) - def sign_edit_validator_transaction(self, nonce, gas_price, gas_limit, rate, bls_key_to_add, bls_key_to_remove, private_key, chain_id=None) -> SignedTransaction: + def sign_edit_validator_transaction(self, nonce, gas_price, gas_limit, rate, bls_key_to_remove, bls_key_to_add, bls_key_to_add_sig, private_key, chain_id=None) -> SignedTransaction: """ Create but not post a transaction to Edit the Validator using private_key @@ -737,6 +787,7 @@ class Validator: _ = info.pop('amount') # also unused info['bls-key-to-remove'] = bls_key_to_remove info['bls-key-to-add'] = bls_key_to_add + info['bls-key-to-add-sig'] = bls_key_to_add_sig if chain_id: info['chainId'] = chain_id return sign_staking_transaction(info, private_key) diff --git a/tests/sdk-pyhmy/test_validator.py b/tests/sdk-pyhmy/test_validator.py index 34bc436..990b741 100644 --- a/tests/sdk-pyhmy/test_validator.py +++ b/tests/sdk-pyhmy/test_validator.py @@ -49,7 +49,8 @@ def test_load_validator(setup_blockchain): 'max-rate': '0.9', 'max-change-rate': '0.05', 'rate': '0.01', - 'bls-public-keys': ['0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611'], + 'bls-public-keys': ['0x30b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202'], + 'bls-key-sigs': ['0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414'], 'max-total-delegation': convert_one_to_atto(40000) } test_validator_object.load(info) @@ -57,7 +58,9 @@ def test_load_validator(setup_blockchain): test_validator_loaded = True """ -TypeScript signature source +TypeScript signature source (is outdated because the JS SDK has not been updated for SlotKeySigs) +For now I have checked that the below transaction to localnet works +--- const description: Description = new Description('Alice', 'alice', 'alice.harmony.one', 'Bob', "Don't mess with me!!!") const commissionRates: CommissionRate = new CommissionRate(new Decimal('0.01'), new Decimal('0.9'), new Decimal('0.05')) const stakeMsg: CreateValidator = new CreateValidator( @@ -91,10 +94,12 @@ def test_create_validator_sign(setup_blockchain): 100, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', None).rawTransaction.hex() - assert signed_hash == '0xf9010580f8bf94ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b622476086118a021e27c1806e59a4000024a047c6d444971d4d3c48e8b255aa0e543ebb47b60f761582694e5af5330445aba5a04db1ffea9cca9f9e56e8f782c689db680992903acfd9c06f4593f7fd9a781bd7' + assert signed_hash == '0xf9016a80f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000023a0650feed3d7bbcb9ed36575c829bfdc600d53cd7a4fcc0e78ddf26d740f1900a8a027798fc245ee5c66847327b7454670b3b84cb3998527c4fbc0f967b787f56bc2' """ -Signature matched from TypeScript +Signature matched from TypeScript (is outdated because the JS SDK has not been updated for SlotKeyToAddSig) +For now I have checked that the below transaction to localnet works +--- import { CreateValidator, EditValidator, @@ -141,11 +146,12 @@ def test_edit_validator_sign(setup_blockchain): int(convert_one_to_atto(1)), 100, '0.06', - '0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612', # add key - "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # remove key + '0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612', # remove key + "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # add key + "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414", # add key sig '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', 2).rawTransaction.hex() - assert signed_hash == '0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861202880de0b6b3a76400006428a0656d6741687ec1e42d1699274584a1777964e939b0ef11f3ff0e161859da21a2a03fc51e067f9fb6c96bee5ceccad4104f5b4b334a86a36a2f53d10b9a8e4a268a' + assert signed_hash == '0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995' @pytest.mark.run(order=4) def test_invalid_validator(setup_blockchain): From cdbdae85d1f7ca163c5bce660bd3058c91666cc3 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Sat, 30 Oct 2021 07:38:54 +0530 Subject: [PATCH 05/23] Upgrade eth_account version --- pyhmy/signing.py | 4 ++-- pyhmy/staking_signing.py | 2 +- setup.cfg | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pyhmy/signing.py b/pyhmy/signing.py index c773936..9482ecf 100644 --- a/pyhmy/signing.py +++ b/pyhmy/signing.py @@ -29,10 +29,10 @@ from eth_account._utils.signing import ( sign_transaction_hash ) -from eth_account._utils.transactions import ( +from eth_account._utils.legacy_transactions import ( Transaction as SignedEthereumTxData, UnsignedTransaction as UnsignedEthereumTxData, - TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS, + LEGACY_TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS, TRANSACTION_DEFAULTS, chain_id_to_v, UNSIGNED_TRANSACTION_FIELDS diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index 1b6a203..a60afa7 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -26,7 +26,7 @@ from eth_account._utils.signing import ( sign_transaction_hash ) -from eth_account._utils.transactions import ( +from eth_account._utils.legacy_transactions import ( chain_id_to_v ) diff --git a/setup.cfg b/setup.cfg index 27a6ef2..6e8a7d9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,7 +27,7 @@ install_requires = requests incremental eth-rlp - eth-account == 0.5.4 + eth-account >= 0.5.5 eth-utils hexbytes cytoolz From 6bc1bbbb34d51790cbee39245ebe307d2c5ebe9e Mon Sep 17 00:00:00 2001 From: mikefreemanwd Date: Thu, 28 Jul 2022 09:07:26 -0400 Subject: [PATCH 06/23] [bugfix]: Fix test command in Makefile. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2028746..1267c9f 100755 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ dev: python3 -m pip install pytest-ordering test: - python3 -m py.test -r s -s tests + python3 -m pytest -r s -s tests install: python3 -m pip install -e . From ce1ae2bc4b4651443670dab605fd379ba569e6f0 Mon Sep 17 00:00:00 2001 From: mikefreemanwd Date: Thu, 28 Jul 2022 10:58:54 -0400 Subject: [PATCH 07/23] [bugfix]: Add bech32 submodule declaration. --- pyhmy/bech32/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pyhmy/bech32/__init__.py diff --git a/pyhmy/bech32/__init__.py b/pyhmy/bech32/__init__.py new file mode 100644 index 0000000..e69de29 From 25a058d5ddb384ee7a8fb00ec2089fb07832eecb Mon Sep 17 00:00:00 2001 From: mikefreemanwd Date: Thu, 28 Jul 2022 11:22:59 -0400 Subject: [PATCH 08/23] [feature]: Added utility function to convert from hex to one (other way from one to hex). Added vim .swp files to gitignore. --- .gitignore | 5 ++++- pyhmy/util.py | 11 +++++++++++ tests/bech32-pyhmy/test_bech32.py | 1 + tests/util-pyhmy/test_util.py | 5 +++++ 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 99e493a..46d35bd 100644 --- a/.gitignore +++ b/.gitignore @@ -130,4 +130,7 @@ dmypy.json .pyre/ # IDE -.idea \ No newline at end of file +.idea + +# VIM +*.swp diff --git a/pyhmy/util.py b/pyhmy/util.py index 6ef6fc5..df2dbd6 100644 --- a/pyhmy/util.py +++ b/pyhmy/util.py @@ -22,6 +22,7 @@ from .account import ( from .bech32.bech32 import ( bech32_decode, + bech32_encode, convertbits ) @@ -96,6 +97,16 @@ def convert_one_to_hex(addr): address = '0x' + ''.join('{:02x}'.format(x) for x in buf) return to_checksum_address(address) +def convert_hex_to_one(addr): + """ + Given a hex address, convert it to a one address + """ + if is_valid_address(addr): + return addr + checksum_addr = to_checksum_address(addr) + data = bytearray.fromhex(checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr) + buf = convertbits(data, 8, 5) + return bech32_encode("one", buf) def is_active_shard(endpoint, delay_tolerance=60): """ diff --git a/tests/bech32-pyhmy/test_bech32.py b/tests/bech32-pyhmy/test_bech32.py index 325bc08..2d58938 100644 --- a/tests/bech32-pyhmy/test_bech32.py +++ b/tests/bech32-pyhmy/test_bech32.py @@ -7,3 +7,4 @@ def test_encode(): def test_decode(): bech32.decode('one', 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') + diff --git a/tests/util-pyhmy/test_util.py b/tests/util-pyhmy/test_util.py index d502d11..e278ef2 100644 --- a/tests/util-pyhmy/test_util.py +++ b/tests/util-pyhmy/test_util.py @@ -47,6 +47,11 @@ def test_convert_one_to_hex(): assert util.convert_one_to_hex('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' assert util.convert_one_to_hex('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' +def test_convert_hex_to_one(): + assert util.convert_hex_to_one('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' + assert util.convert_hex_to_one('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' + + def test_get_bls_build_variables(): assert isinstance(util.get_bls_build_variables(), dict) From 2f0d840f6ec2807cd5aaf8af9e237d7d4e1e751b Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 12:32:18 +0000 Subject: [PATCH 09/23] fix(signing): require `chainId` for staking signs --- pyhmy/staking_signing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index a60afa7..afa5b52 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -405,6 +405,8 @@ def sign_staking_transaction(transaction_dict, private_key): https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts """ assert isinstance(transaction_dict, dict), 'Only dictionaries are supported' # OrderedDict is a subclass + # chain_id missing => 'rlp: input string too long for uint64, decoding into (types.StakingTransaction)(types.txdata).GasLimit' + assert 'chainId' in transaction_dict, 'chainId missing' assert 'directive' in transaction_dict, 'Staking transaction type not specified' assert isinstance(transaction_dict['directive'], Directive), 'Unknown staking transaction type' if transaction_dict['directive'] == Directive.CollectRewards: From 6d6c417fe531d72cdbf5be723b678db2e9c2d282 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 12:33:48 +0000 Subject: [PATCH 10/23] fix(util): do not validate integer chainId anybody can create a network with a new chainId. stop validating the integers, but do validate the strings. as a side effect, this means we accept Harmony's ethereum compatible chain ids. --- pyhmy/util.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pyhmy/util.py b/pyhmy/util.py index df2dbd6..2c10031 100644 --- a/pyhmy/util.py +++ b/pyhmy/util.py @@ -64,12 +64,11 @@ def chain_id_to_int(chainId): HmyLocal = 2, HmyPangaea = 3, ) + + # do not validate integer chainids, only known strings if isinstance(chainId, str): - assert chainId in chainIds, f'ChainId {chainId} is not valid' + assert chainId in chainIds, f'Chain {chainId} unknown, specify an integer chainId' return chainIds.get(chainId) - elif isinstance(chainId, int): - assert chainId in chainIds.values(), f'Unknown chain id {chainId}' - return chainId else: raise TypeError( 'chainId must be str or int' ) From 218fee615b4e13c50d437880db64b8e5f828e2b4 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 13:32:49 +0000 Subject: [PATCH 11/23] fix(account): add default param, get_nonce - add a default `block_num` parameter 'latest' to get_account_nonce - add get_nonce as an alias of get_account_nonce --- pyhmy/account.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pyhmy/account.py b/pyhmy/account.py index d057fe7..60c0494 100644 --- a/pyhmy/account.py +++ b/pyhmy/account.py @@ -125,7 +125,7 @@ def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_account_nonce(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: +def get_account_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the account nonce @@ -165,6 +165,12 @@ def get_account_nonce(address, block_num, endpoint=_default_endpoint, timeout=_d except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e +def get_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int: + """ + See get_account_nonce + """ + return get_account_nonce(address, block_num, endpoint, timeout) + def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the number of transactions the given address has sent for the given block number From cfd550b55306308efc826788b7251e3a14db7027 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 13:39:06 +0000 Subject: [PATCH 12/23] feat(transaction): add send + confirm staking tx --- pyhmy/transaction.py | 50 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index e256c8c..337efea 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -2,7 +2,8 @@ from .rpc.request import ( rpc_request ) from .exceptions import ( - TxConfirmationTimedoutError + TxConfirmationTimedoutError, + InvalidRPCReplyError ) import time import random @@ -435,7 +436,9 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time while((time.time() - start_time) <= timeout): tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint) if tx_response is not None: - if tx_response[ 'blockHash' ] != '0x0000000000000000000000000000000000000000000000000000000000000000': + block_hash = tx_response.get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + if unique_chars != "0": return tx_response time.sleep(random.uniform(0.2, 0.5)) raise TxConfirmationTimedoutError("Could not confirm transactions on-chain.") @@ -745,3 +748,46 @@ def send_raw_staking_transaction(raw_tx, endpoint=_default_endpoint, timeout=_de return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + +def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + """ + Send signed staking transaction and wait for it to be confirmed + + Parameters + ---------- + signed_tx: str + Hex representation of signed staking transaction + endpoint: :obj:`str`, optional + Endpoint to send request to + timeout: :obj:`int`, optional + Timeout in seconds + + Returns + ------- + str + Transaction, see get_transaction_by_hash for structure + + Raises + ------ + InvalidRPCReplyError + If received unknown result from endpoint, or + RPCError + If transaction failed to be added to the pool + TxConfirmationTimedoutError + If transaction could not be confirmed within the timeout period + + API Reference + ------------- + https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401 + """ + tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint) + start_time = time.time() + while((time.time() - start_time) <= timeout): + tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint) + if tx_response is not None: + block_hash = tx_response.get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + if unique_chars != "0": + return tx_response + time.sleep(random.uniform(0.2, 0.5)) + raise TxConfirmationTimedoutError("Could not confirm transactions on-chain.") \ No newline at end of file From 3e4533d48642b9f715a7e034df284cf98bd6a302 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 14:09:56 +0000 Subject: [PATCH 13/23] fix(util): pass int `chainId` through --- pyhmy/util.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyhmy/util.py b/pyhmy/util.py index 2c10031..36c350f 100644 --- a/pyhmy/util.py +++ b/pyhmy/util.py @@ -69,6 +69,8 @@ def chain_id_to_int(chainId): if isinstance(chainId, str): assert chainId in chainIds, f'Chain {chainId} unknown, specify an integer chainId' return chainIds.get(chainId) + elif isinstance(chainId, int): + return chainId else: raise TypeError( 'chainId must be str or int' ) From 6e83b2c8fd79b94ccd622602edb28ae94fc2cea8 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:16:20 +0000 Subject: [PATCH 14/23] fix(contract): remove invalid reference to method --- pyhmy/contract.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyhmy/contract.py b/pyhmy/contract.py index 90b41df..66dee63 100644 --- a/pyhmy/contract.py +++ b/pyhmy/contract.py @@ -6,6 +6,10 @@ from .transaction import ( get_transaction_receipt ) +from .exceptions import ( + InvalidRPCReplyError +) + _default_endpoint = 'http://localhost:9500' _default_timeout = 30 @@ -237,4 +241,4 @@ def get_contract_address_from_hash(tx_hash, endpoint=_default_endpoint, timeout= try: return get_transaction_receipt(tx_hash, endpoint, timeout)["contractAddress"] except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + raise InvalidRPCReplyError("hmyv2_getTransactionReceipt", endpoint) from e From c9125ba2de7c22e3c98ae2be5af08a3c7d021372 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:17:39 +0000 Subject: [PATCH 15/23] fix(staking): remove -1 as default parameter In Harmony release v4.3.8, this parameter was deprecated and it is no longer possible to retrieve all pages. --- pyhmy/staking.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pyhmy/staking.py b/pyhmy/staking.py index 4186073..312f9d5 100644 --- a/pyhmy/staking.py +++ b/pyhmy/staking.py @@ -2,6 +2,10 @@ from .rpc.request import ( rpc_request ) +from .exceptions import ( + InvalidRPCReplyError +) + _default_endpoint = 'http://localhost:9500' _default_timeout = 30 @@ -261,7 +265,7 @@ def get_validator_information_by_block_number(validator_addr, block_num, endpoin except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_all_validator_information(page=-1, endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_all_validator_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get validator information for all validators on chain @@ -366,7 +370,7 @@ def get_validator_total_delegation(address, endpoint=_default_endpoint, timeout= except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_all_validator_information_by_block_number(block_num, page=-1, endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_all_validator_information_by_block_number(block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get validator information at block number for all validators on chain @@ -408,7 +412,7 @@ def get_all_validator_information_by_block_number(block_num, page=-1, endpoint=_ ################### # Delegation RPCs # ################### -def get_all_delegation_information(page=-1, endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_all_delegation_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get delegation information for all delegators on chain From eb322498c4de56fadd46106f8340e006941b95b1 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:20:15 +0000 Subject: [PATCH 16/23] fix(transaction): make error message singular --- pyhmy/transaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index 337efea..2e34914 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -441,7 +441,7 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time if unique_chars != "0": return tx_response time.sleep(random.uniform(0.2, 0.5)) - raise TxConfirmationTimedoutError("Could not confirm transactions on-chain.") + raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") ############################### # CrossShard Transaction RPCs # @@ -790,4 +790,4 @@ def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoi if unique_chars != "0": return tx_response time.sleep(random.uniform(0.2, 0.5)) - raise TxConfirmationTimedoutError("Could not confirm transactions on-chain.") \ No newline at end of file + raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") \ No newline at end of file From 2135d1050ca576fef0a2392f5f1e08579011e5ed Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 18:23:30 +0000 Subject: [PATCH 17/23] test: update tests to be independent of localnet remove the requirement that tests must be run after `make test-rpc`. in fact, these tests must be run independently of those. test: document raw tx generation --- pytest.ini | 3 +- tests/GenerateRawTransactions.ipynb | 578 ++++++++++++++++++++++++ tests/cli-pyhmy/test_cli.py | 4 - tests/numbers-pyhmy/test_numbers.py | 4 - tests/request-pyhmy/test_request.py | 3 - tests/sdk-pyhmy/conftest.py | 166 ++++--- tests/sdk-pyhmy/test_account.py | 26 +- tests/sdk-pyhmy/test_blockchain.py | 44 +- tests/sdk-pyhmy/test_contract.py | 11 +- tests/sdk-pyhmy/test_staking.py | 35 +- tests/sdk-pyhmy/test_staking_signing.py | 26 +- tests/sdk-pyhmy/test_transaction.py | 99 ++-- tests/sdk-pyhmy/test_validator.py | 28 +- 13 files changed, 732 insertions(+), 295 deletions(-) create mode 100644 tests/GenerateRawTransactions.ipynb diff --git a/pytest.ini b/pytest.ini index 02ae413..b816cb7 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,3 +1,2 @@ [pytest] -addopts = -v --showlocals -python_paths = . +addopts = -v --showlocals \ No newline at end of file diff --git a/tests/GenerateRawTransactions.ipynb b/tests/GenerateRawTransactions.ipynb new file mode 100644 index 0000000..057053b --- /dev/null +++ b/tests/GenerateRawTransactions.ipynb @@ -0,0 +1,578 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 105, + "id": "feee22ef", + "metadata": {}, + "outputs": [], + "source": [ + "from pyhmy import signing, staking_signing, numbers, transaction, account, validator as validator_module, staking_structures, contract\n", + "from web3 import Web3" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "55b8db60", + "metadata": {}, + "outputs": [], + "source": [ + "# we need five transactions in conftest\n", + "# simple transfer (from localnet address)\n", + "# contract creation (from second address)\n", + "# cross shard transfer (from second address)\n", + "# validator creation (from localnet address)\n", + "# delegation (from second address)" + ] + }, + { + "cell_type": "markdown", + "id": "e104724c", + "metadata": {}, + "source": [ + "### Simple Transfer" + ] + }, + { + "cell_type": "code", + "execution_count": 144, + "id": "d7fa35f8", + "metadata": {}, + "outputs": [], + "source": [ + "pk = \"1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc\"\n", + "tx = {\n", + " 'from': 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3',\n", + " # 3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65\n", + " 'to': 'one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37',\n", + " 'gasPrice': Web3.toWei( 100, 'gwei' ),\n", + " 'gas': 21000,\n", + " 'chainId': 2, # localnet\n", + " 'value': int( numbers.convert_one_to_atto( 503 ) ),\n", + " 'nonce': 0,\n", + " 'shardID': 0,\n", + " 'toShardID': 0,\n", + "}\n", + "raw_tx = signing.sign_transaction(tx, pk).rawTransaction.hex()\n", + "tx_hash = transaction.send_raw_transaction(raw_tx)" + ] + }, + { + "cell_type": "code", + "execution_count": 145, + "id": "ed907d4b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3\n", + "0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43\n" + ] + } + ], + "source": [ + "print( raw_tx )\n", + "print( tx_hash )" + ] + }, + { + "cell_type": "markdown", + "id": "1bbee37b", + "metadata": {}, + "source": [ + "### Contract Creation" + ] + }, + { + "cell_type": "code", + "execution_count": 147, + "id": "b143507b", + "metadata": {}, + "outputs": [], + "source": [ + "pk = '3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65'\n", + "data = \"0x6080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033\"\n", + "tx = {\n", + " 'gasPrice': Web3.toWei( 100, 'gwei' ),\n", + " 'gas': 100000,\n", + " 'chainId': 2,\n", + " 'nonce': 0,\n", + " 'shardID': 0,\n", + " 'toShardID': 0,\n", + " 'data': data,\n", + "}\n", + "raw_tx = signing.sign_transaction(tx, pk).rawTransaction.hex()\n", + "tx_hash = transaction.send_raw_transaction(raw_tx)" + ] + }, + { + "cell_type": "code", + "execution_count": 148, + "id": "53dbcbff", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0xf8e88085174876e800830186a080808080b8946080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f003327a08bf26ee0120c296b17af507f62606abdb5c5f09a65642c3d30b349b8bfbb3d69a03ec7be51c615bcbf2f1d63f6eaa56cf8d7be81671717f90239619830a81ebc9f\n", + "0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f\n" + ] + } + ], + "source": [ + "print( raw_tx )\n", + "print( tx_hash )" + ] + }, + { + "cell_type": "code", + "execution_count": 130, + "id": "6e66392b", + "metadata": {}, + "outputs": [], + "source": [ + "contract_address = transaction.get_transaction_receipt( tx_hash ).get( 'contractAddress' )\n", + "deployed = contract.get_code( contract_address, 'latest' )" + ] + }, + { + "cell_type": "code", + "execution_count": 131, + "id": "ead2f9d4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033\n", + "0x6080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033\n" + ] + } + ], + "source": [ + "print( deployed )\n", + "print( data )" + ] + }, + { + "cell_type": "code", + "execution_count": 132, + "id": "453a34d6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3300f080003436c6f63746dc447adeb4d82da06660bc16489e70d315a93fde5dd4f95a1387620d8e0af3af0221228537660796462aef3f091002063009180915092518100615080406b5df080006b575d2064163dc63943608c10e065300067582060163400605b5df08000675f0065108432504060806x0\n", + "3300f080003436c6f63746dc447adeb4d82da06660bc16489e70d315a93fde5dd4f95a1387620d8e0af3af0221228537660796462aef3f091002063009180915092518100615080406b5df080006b575d2064163dc63943608c10e065300067582060163400605b5df08000675f0065108432504060806ef3f0006930006d10608770605b5df08000675f0065108432504060806x0\n" + ] + } + ], + "source": [ + "print( \"\".join( [ deployed[ len( deployed ) - ( i + 1 ) ] for i in range( len( deployed ) ) ] ) )\n", + "print( \"\".join( [ data[ len( data ) - ( i + 1 ) ] for i in range( len( data ) ) ] ) )" + ] + }, + { + "cell_type": "code", + "execution_count": 133, + "id": "d251d1bf", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 133, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\"0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033\" == deployed" + ] + }, + { + "cell_type": "markdown", + "id": "812e033c", + "metadata": {}, + "source": [ + "### Cross Shard Transfer" + ] + }, + { + "cell_type": "code", + "execution_count": 149, + "id": "d7c70614", + "metadata": {}, + "outputs": [], + "source": [ + "pk = '3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65'\n", + "tx = {\n", + " 'from': 'one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37',\n", + " 'gasPrice': Web3.toWei( 100, 'gwei' ),\n", + " 'gas': 21000,\n", + " 'chainId': 2,\n", + " 'nonce': 1,\n", + " 'shardID': 0,\n", + " 'toShardID': 1,\n", + " 'to': 'one1e8rdglh97t37prtnv7k35ymnh2wazujpzsmzes',\n", + " 'value': Web3.toWei( 100, 'gwei' ),\n", + "}\n", + "raw_tx = signing.sign_transaction(tx, pk).rawTransaction.hex()\n", + "tx_hash = transaction.send_raw_transaction(raw_tx)" + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "f20990f1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0xf86b0185174876e800825208800194c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724185174876e8008027a02501c517220e9499f14e97c20b0a88cd3b7ba80637bba43ed295422e69a3f300a079b8e1213c9506184aed6ac2eb0b2cb00594c3f9fcdd6c088937ce17fe47107c\n", + "0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf\n" + ] + } + ], + "source": [ + "print( raw_tx )\n", + "print( tx_hash )" + ] + }, + { + "cell_type": "code", + "execution_count": 153, + "id": "66f024b9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 153, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "account.get_balance( 'one1e8rdglh97t37prtnv7k35ymnh2wazujpzsmzes', 'http://localhost:9502' )" + ] + }, + { + "cell_type": "markdown", + "id": "2b2446df", + "metadata": {}, + "source": [ + "### Validator Creation" + ] + }, + { + "cell_type": "code", + "execution_count": 154, + "id": "c3513c37", + "metadata": {}, + "outputs": [], + "source": [ + "pk = \"1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc\"\n", + "address = \"one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3\"\n", + "info = {\n", + " \"name\": \"Alice\",\n", + " \"identity\": \"alice\",\n", + " \"website\": \"alice.harmony.one\",\n", + " \"security-contact\": \"Bob\",\n", + " \"details\": \"Are you even reading this?\",\n", + " \"min-self-delegation\": int( numbers.convert_one_to_atto( 10000 ) ),\n", + " \"max-total-delegation\": int( numbers.convert_one_to_atto( 100000 ) ),\n", + " \"rate\": \"0.1\",\n", + " \"max-rate\": \"0.9\",\n", + " \"max-change-rate\": \"0.05\",\n", + " \"bls-public-keys\": [\n", + " # private key is b1f2a5029f5f43c8c933a61ce936ced030b2c9379f8e2478fc888fa670cdbc89b8cd1ebc29b5b00a81d3152bb3aaa3a337404f50bee5e434430ca3693a94a1c102a765cf3b0887b8b0bcf5317d33f4bec60a97feae2498a39ab7a1c2\n", + " # blspass.txt is empty\n", + " \"0xa20e70089664a874b00251c5e85d35a73871531306f3af43e02138339d294e6bb9c4eb82162199c6a852afeaa8d68712\",\n", + " ],\n", + " \"amount\": int( numbers.convert_one_to_atto( 10000 ) ),\n", + " \"bls-key-sigs\": [\n", + " \"0xef2c49a2f31fbbd23c21bc176eaf05cd0bebe6832033075d81fea7cff6f9bc1ab42f3b6895c5493fe645d8379d2eaa1413de55a9d3ce412a4f747cb57d52cc4da4754bfb2583ec9a41fe5dd48287f964f276336699959a5fcef3391dc24df00d\",\n", + " ]\n", + "}\n", + "validator = validator_module.Validator( address )\n", + "validator.load( info )\n", + "raw_tx = validator.sign_create_validator_transaction(\n", + " 1,\n", + " Web3.toWei( 100, 'gwei' ),\n", + " 55000000, # gas limit\n", + " pk,\n", + " 2 # chain id\n", + ").rawTransaction.hex()\n", + "tx_hash = transaction.send_raw_staking_transaction(\n", + " raw_tx,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 155, + "id": "9b12f75f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0xf9017c80f9012994a5241513da9f4463f1d4874b548dfbac29d91f34f83d85416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f629a41726520796f75206576656e2072656164696e6720746869733fddc988016345785d8a0000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a152d02c7e14af6800000f1b0a20e70089664a874b00251c5e85d35a73871531306f3af43e02138339d294e6bb9c4eb82162199c6a852afeaa8d68712f862b860ef2c49a2f31fbbd23c21bc176eaf05cd0bebe6832033075d81fea7cff6f9bc1ab42f3b6895c5493fe645d8379d2eaa1413de55a9d3ce412a4f747cb57d52cc4da4754bfb2583ec9a41fe5dd48287f964f276336699959a5fcef3391dc24df00d8a021e19e0c9bab24000000185174876e8008403473bc028a08c1146305eaef981aa24c2f17c8519664d10c99ee42acedbc258749930d31a7ca031dadf114ee6ab9bd09933208094c65037b66c796bcfc57a70158106b37357b0\n", + "0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968\n" + ] + } + ], + "source": [ + "print( raw_tx )\n", + "print( tx_hash )" + ] + }, + { + "cell_type": "markdown", + "id": "4c2ff645", + "metadata": {}, + "source": [ + "### Delegation" + ] + }, + { + "cell_type": "code", + "execution_count": 156, + "id": "458d81b8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0xf88302f4941f2213a52f7409ff4f103458e6d202e0b3aa805a94a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000000285174876e80082c35028a02c5e953062dcdfa2de9723639b63bab45705eb6dfbfe7f44536ed266c3c7ca20a0742964e646338e7431874f70715565d99c01c762324355c69db34a9ed9de81d7\n", + "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f\n" + ] + } + ], + "source": [ + "pk = \"3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65\"\n", + "tx = {\n", + " 'directive': staking_structures.Directive.Delegate,\n", + " 'delegatorAddress': 'one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37',\n", + " 'validatorAddress': 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3',\n", + " 'amount': Web3.toWei( 100, 'ether' ),\n", + " 'nonce': 2,\n", + " 'gasPrice': Web3.toWei( 100, 'gwei' ),\n", + " 'gasLimit': 50000,\n", + " 'chainId': 2,\n", + "}\n", + "raw_tx = staking_signing.sign_staking_transaction(\n", + " tx,\n", + " pk,\n", + ").rawTransaction.hex()\n", + "tx_hash = transaction.send_raw_staking_transaction(\n", + " raw_tx,\n", + ")\n", + "print( raw_tx )\n", + "print( tx_hash )" + ] + }, + { + "cell_type": "markdown", + "id": "8efa5536", + "metadata": {}, + "source": [ + "### test_transaction.py - transfer 105 ONE to another address" + ] + }, + { + "cell_type": "code", + "execution_count": 157, + "id": "c3295fee", + "metadata": {}, + "outputs": [], + "source": [ + "pk = '3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65'\n", + "tx = {\n", + " 'from': 'one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37',\n", + " 'gasPrice': Web3.toWei( 100, 'gwei' ),\n", + " 'gas': 21000,\n", + " 'chainId': 2,\n", + " 'nonce': 3,\n", + " 'shardID': 0,\n", + " 'toShardID': 0,\n", + " 'to': 'one1e8rdglh97t37prtnv7k35ymnh2wazujpzsmzes',\n", + " 'value': Web3.toWei( 105, 'ether' ),\n", + "}\n", + "raw_tx = signing.sign_transaction(tx, pk).rawTransaction.hex()\n", + "tx_hash = transaction.send_raw_transaction(raw_tx)" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "id": "af515c7e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0\n", + "0x7ccd80f8513f76ec58b357c7a82a12a95e025d88f1444e953f90e3d86e222571\n" + ] + } + ], + "source": [ + "print( raw_tx )\n", + "print( tx_hash )" + ] + }, + { + "cell_type": "markdown", + "id": "bb546b3e", + "metadata": {}, + "source": [ + "### test_transaction.py - staking transaction" + ] + }, + { + "cell_type": "code", + "execution_count": 168, + "id": "c14e2d6d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000008085174876e80082c35027a0808ea7d27adf3b1f561e8da4676814084bb75ac541b616bece87c6446e6cc54ea02f19f0b14240354bd42ad60b0c7189873c0be87044e13072b0981a837ca76f64\n", + "0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629\n" + ] + } + ], + "source": [ + "pk = \"ff9ef6b00a61672b4b7bedd5ac653439b56ac8ee808c99a1bd871cf51b7d60eb\"\n", + "tx = {\n", + " 'directive': staking_structures.Directive.Delegate,\n", + " 'delegatorAddress': 'one1e8rdglh97t37prtnv7k35ymnh2wazujpzsmzes',\n", + " 'validatorAddress': 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3',\n", + " 'amount': Web3.toWei( 100, 'ether' ),\n", + " 'nonce': 0,\n", + " 'gasPrice': Web3.toWei( 100, 'gwei' ),\n", + " 'gasLimit': 50000,\n", + " 'chainId': 2,\n", + "}\n", + "raw_tx = staking_signing.sign_staking_transaction(\n", + " tx,\n", + " pk,\n", + ").rawTransaction.hex()\n", + "tx_hash = transaction.send_raw_staking_transaction(\n", + " raw_tx,\n", + ")\n", + "print( raw_tx )\n", + "print( tx_hash )" + ] + }, + { + "cell_type": "code", + "execution_count": 162, + "id": "ebf296aa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'blockHash': '0xf55f1fb3c9be76fb74370e8a7d8580327797d2d6082040074783207a171e2de6',\n", + " 'blockNumber': 34,\n", + " 'from': 'one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37',\n", + " 'hash': '0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf',\n", + " 'shardID': 0,\n", + " 'to': 'one1e8rdglh97t37prtnv7k35ymnh2wazujpzsmzes',\n", + " 'toShardID': 1,\n", + " 'value': 100000000000}" + ] + }, + "execution_count": 162, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transaction.get_cx_receipt_by_hash( '0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf', 'http://localhost:9502' )" + ] + }, + { + "cell_type": "code", + "execution_count": 166, + "id": "ff0229ce", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'blockHash': '0x0000000000000000000000000000000000000000000000000000000000000000',\n", + " 'blockNumber': None,\n", + " 'from': 'one1e8rdglh97t37prtnv7k35ymnh2wazujpzsmzes',\n", + " 'gas': 50000,\n", + " 'gasPrice': 100000000000,\n", + " 'hash': '0x279935278d20d778cbe4fdfa5d51be9eb1eb184053dc9a7cb88ad3365df73060',\n", + " 'msg': {'amount': 100000000000000000000,\n", + " 'delegatorAddress': 'one1e8rdglh97t37prtnv7k35ymnh2wazujpzsmzes',\n", + " 'validatorAddress': 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3'},\n", + " 'nonce': 2,\n", + " 'r': '0x8660a63c10af06f2fb3f24b92cf61d4f319044a1f1931c4f4e54ce986ff563c',\n", + " 's': '0x597785559c4283d3ece2df37cbf37077966487a2a2dc0f4cdbbf75a8f20bc1a8',\n", + " 'timestamp': 0,\n", + " 'transactionIndex': 0,\n", + " 'type': 'Delegate',\n", + " 'v': '0x27'}" + ] + }, + "execution_count": 166, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "transaction.get_staking_transaction_by_hash( \"0x279935278d20d778cbe4fdfa5d51be9eb1eb184053dc9a7cb88ad3365df73060\" )" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/cli-pyhmy/test_cli.py b/tests/cli-pyhmy/test_cli.py index 1cb9802..e6596f5 100644 --- a/tests/cli-pyhmy/test_cli.py +++ b/tests/cli-pyhmy/test_cli.py @@ -16,14 +16,12 @@ def setup(): os.makedirs(TEMP_DIR, exist_ok=True) -@pytest.mark.run(order=0) def test_download_cli(): env = cli.download(BINARY_FILE_PATH, replace=False, verbose=False) cli.environment.update(env) assert os.path.exists(BINARY_FILE_PATH) -@pytest.mark.run(order=1) def test_is_valid(): bad_file_path = os.path.realpath(f"{TEMP_DIR}/test_is_valid/bad_hmy") shutil.rmtree(Path(bad_file_path).parent, ignore_errors=True) @@ -35,7 +33,6 @@ def test_is_valid(): assert not cli.is_valid_binary(bad_file_path) -@pytest.mark.run(order=2) def test_bad_bin_set(): bad_file_path = os.path.realpath(f"{TEMP_DIR}/test_bad_bin_set/hmy") shutil.rmtree(Path(bad_file_path).parent, ignore_errors=True) @@ -46,7 +43,6 @@ def test_bad_bin_set(): assert cli.get_binary_path() != bad_file_path -@pytest.mark.run(order=3) def test_bin_set(): cli.set_binary(BINARY_FILE_PATH) cli_binary_path = cli.get_binary_path() diff --git a/tests/numbers-pyhmy/test_numbers.py b/tests/numbers-pyhmy/test_numbers.py index 569432f..02fad81 100644 --- a/tests/numbers-pyhmy/test_numbers.py +++ b/tests/numbers-pyhmy/test_numbers.py @@ -1,13 +1,10 @@ from decimal import Decimal -import pytest - from pyhmy import ( numbers ) -@pytest.mark.run(order=1) def test_convert_atto_to_one(): a = numbers.convert_atto_to_one(1e18) assert Decimal(1) == a @@ -21,7 +18,6 @@ def test_convert_atto_to_one(): d = numbers.convert_atto_to_one(Decimal(1e18)) assert Decimal(1) == d -@pytest.mark.run(order=2) def test_convert_one_to_atto(): a = numbers.convert_one_to_atto(1e-18) assert Decimal(1) == a diff --git a/tests/request-pyhmy/test_request.py b/tests/request-pyhmy/test_request.py index a7fcc97..19acdeb 100644 --- a/tests/request-pyhmy/test_request.py +++ b/tests/request-pyhmy/test_request.py @@ -33,7 +33,6 @@ def setup(): pytest.skip("can not connect to local blockchain", allow_module_level=True) -@pytest.mark.run(order=1) def test_request_connection_error(): # Find available port s = socket.socket() @@ -52,7 +51,6 @@ def test_request_connection_error(): assert bad_request is None -@pytest.mark.run(order=2) def test_request_rpc_error(): error_request = None try: @@ -64,7 +62,6 @@ def test_request_rpc_error(): assert error_request is None -@pytest.mark.run(order=3) def test_rpc_request(): endpoint = 'http://localhost:9500' timeout = 30 diff --git a/tests/sdk-pyhmy/conftest.py b/tests/sdk-pyhmy/conftest.py index ef421ec..5767b6c 100644 --- a/tests/sdk-pyhmy/conftest.py +++ b/tests/sdk-pyhmy/conftest.py @@ -1,73 +1,65 @@ import json import time +import random import pytest import requests -test_validator_address = 'one18tvf56zqjkjnak686lwutcp5mqfnvee35xjnhc' -transfer_raw_transaction = '0xf86f80843b9aca008252080180943ad89a684095a53edb47d7ddc5e034d8133667318a152d02c7e14af68000008027a0ec6c8ad0f70b3c826fa77574c6815a8f73936fafb7b2701a7082ad7d278c95a9a0429f9f166b1c1d385a4ec8f8b86604c26e427c2b0a1c85d9cf4ec6bbd0719508' -tx_hash = '0x1fa20537ea97f162279743139197ecf0eac863278ac1c8ada9a6be5d1e31e633' -create_validator_raw_transaction = '0xf9015680f90105943ad89a684095a53edb47d7ddc5e034d813366731d984746573748474657374847465737484746573748474657374ddc988016345785d8a0000c9880c7d713b49da0000c887b1a2bc2ec500008a022385a827e8155000008b084595161401484a000000f1b0282554f2478661b4844a05a9deb1837aac83931029cb282872f0dcd7239297c499c02ea8da8746d2f08ca2b037e89891f862b86003557e18435c201ecc10b1664d1aea5b4ec59dbfe237233b953dbd9021b86bc9770e116ed3c413fe0334d89562568a10e133d828611f29fee8cdab9719919bbcc1f1bf812c73b9ccd0f89b4f0b9ca7e27e66d58bbb06fcf51c295b1d076cfc878a0228f16f86157860000080843b9aca008351220027a018385211a150ca032c3526cef0aba6a75f99a18cb73f547f67bab746be0c7a64a028be921002c6eb949b3932afd010dfe1de2459ec7fe84403b9d9d8892394a78c' -staking_tx_hash = '0x57ec011aabdeb078a4816502224022f291fa8b07c82bbae8476f514a1d71c730' -contract_tx_hash = '0xa13414dd152173395c69a11e79dea31bf029660f747a42a53744181d05571e70' -contract_raw_transaction = '0xf9025080843b9aca008366916c80808080b901fc608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061019c806100606000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063445df0ac146100465780638da5cb5b14610064578063fdacd576146100ae575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b61006c6100e2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100da600480360360208110156100c457600080fd5b8101908080359060200190929190505050610107565b005b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561016457806001819055505b5056fea265627a7a723158209b80813a158b44af65aee232b44c0ac06472c48f4abbe298852a39f0ff34a9f264736f6c6343000510003227a03a3ad2b7c2934a8325fc04d04daad740d337bb1f589482bbb1d091e1be804d29a00c46772871866a34f254e6197a526bebc2067f75edc53c488b31d84e07c3c685' +# private keys +# 1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc / one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 (genesis) +# 3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65 / one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (transferred 503) endpoint = 'http://localhost:9500' -endpoint_shard_one = 'http://localhost:9501' timeout = 30 headers = { 'Content-Type': 'application/json' } +txs = [ + # same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce) + "0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3", + # contract creation by one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce) + "0xf8e88085174876e800830186a080808080b8946080604052348015600f57600080fd5b50607780601d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f003327a08bf26ee0120c296b17af507f62606abdb5c5f09a65642c3d30b349b8bfbb3d69a03ec7be51c615bcbf2f1d63f6eaa56cf8d7be81671717f90239619830a81ebc9f", + # cross shard transfer by one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (1 nonce) + "0xf86b0185174876e800825208800194c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724185174876e8008027a02501c517220e9499f14e97c20b0a88cd3b7ba80637bba43ed295422e69a3f300a079b8e1213c9506184aed6ac2eb0b2cb00594c3f9fcdd6c088937ce17fe47107c", +] +stxs = [ + # creation of one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 as validator (1 nonce) + "0xf9017c80f9012994a5241513da9f4463f1d4874b548dfbac29d91f34f83d85416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f629a41726520796f75206576656e2072656164696e6720746869733fddc988016345785d8a0000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a152d02c7e14af6800000f1b0a20e70089664a874b00251c5e85d35a73871531306f3af43e02138339d294e6bb9c4eb82162199c6a852afeaa8d68712f862b860ef2c49a2f31fbbd23c21bc176eaf05cd0bebe6832033075d81fea7cff6f9bc1ab42f3b6895c5493fe645d8379d2eaa1413de55a9d3ce412a4f747cb57d52cc4da4754bfb2583ec9a41fe5dd48287f964f276336699959a5fcef3391dc24df00d8a021e19e0c9bab24000000185174876e8008403473bc028a08c1146305eaef981aa24c2f17c8519664d10c99ee42acedbc258749930d31a7ca031dadf114ee6ab9bd09933208094c65037b66c796bcfc57a70158106b37357b0", + # delegation by one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 to one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 (2 nonce) + "0xf88302f4941f2213a52f7409ff4f103458e6d202e0b3aa805a94a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000000285174876e80082c35028a02c5e953062dcdfa2de9723639b63bab45705eb6dfbfe7f44536ed266c3c7ca20a0742964e646338e7431874f70715565d99c01c762324355c69db34a9ed9de81d7", +] +tx_hashes = [ + "0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43", + "0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f", + "0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf", +] +stx_hashes = [ + "0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968", + "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f", +] +assert len( txs ) == len( tx_hashes ), "Mismatch in tx and tx_hash count" +assert len( stxs ) == len( stx_hashes ), "Mismatch in stx and stx_hash count" @pytest.fixture(scope="session", autouse=True) def setup_blockchain(): + # return metadata = _check_connection() _check_staking_epoch(metadata) - tx_data = _check_funding_transaction() - - if not tx_data['result']: - _send_funding_transaction() - time.sleep(20) # Sleep to let cross shard transaction finalize - - tx_data = _check_funding_transaction() - if 'error' in tx_data: - pytest.skip(f"Error in hmyv2_getTransactionByHash reply: {tx_data['error']}", allow_module_level=True) - if not tx_data['result']: - pytest.skip(f"Funding transaction failed: {tx_hash}", allow_module_level=True) - - - stx_data = _check_staking_transaction() - - if not stx_data['result']: - _send_staking_transaction() - time.sleep(30) # Sleep to let transaction finalize - - stx_data = _check_staking_transaction() - if 'error' in stx_data: - pytest.skip(f"Error in hmyv2_getStakingTransactionByHash reply: {stx_data['error']}", allow_module_level=True) - if not stx_data['result']: - pytest.skip(f"Staking transaction failed: {staking_tx_hash}", allow_module_level=True) - - contract_data = _check_contract_transaction() - - if not contract_data['result']: - _send_contract_transaction() - times.sleep(30) - - contract_data = _check_contract_transaction() - if 'error' in contract_data: - pytest.skip(f"Error in hmyv2_getStakingTransactionByHash reply: {contract_data['error']}", allow_module_level=True) - if not contract_data['result']: - pytest.skip(f"Staking transaction failed: {contract_tx_hash}", allow_module_level=True) - - # TODO: Build data object to return data instead of hard coded values in the test files - try: - return int(stx_data['result']['blockNumber']) - except (TypeError, KeyError) as e: - pytest.skip(f"Unexpected reply for hmyv2_getStakingTransactionByHash: {stx_data['result']}", allow_module_level=True) - + for i in range( len( txs ) ): + tx = txs[ i ] + tx_hash = tx_hashes[ i ] + _send_transaction( tx, endpoint ) + if not _wait_for_transaction_confirmed( tx_hash, endpoint ): + pytest.skip("Could not confirm initial transaction #{} on chain".format( i ), allow_module_level = True) + + for i in range( len( stxs ) ): + stx = stxs[ i ] + stx_hash = stx_hashes[ i ] + _send_staking_transaction( stx, endpoint ) + if not _wait_for_staking_transaction_confirmed( stx_hash, endpoint ): + pytest.skip("Could not confirm initial staking transaction #{} on chain".format( i ), allow_module_level = True) def _check_connection(): try: @@ -111,15 +103,15 @@ def _check_staking_epoch(metadata): if staking_epoch > current_epoch: pytest.skip(f'Not staking epoch: current {current_epoch}, staking {staking_epoch}', allow_module_level=True) -def _send_funding_transaction(): +def _send_transaction(raw_tx, endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", "method": 'hmyv2_sendRawTransaction', - "params": [transfer_raw_transaction] + "params": [raw_tx] } - response = requests.request('POST', endpoint_shard_one, headers=headers, + response = requests.request('POST', endpoint, headers=headers, data=json.dumps(payload), timeout=timeout, allow_redirects=True) tx = json.loads(response.content) if 'error' in tx: @@ -127,7 +119,7 @@ def _send_funding_transaction(): except Exception as e: pytest.skip('Failed to get hmyv2_sendRawTransaction reply', allow_module_level=True) -def _check_funding_transaction(): +def _check_transaction(tx_hash, endpoint): try: payload = { "id": "1", @@ -135,21 +127,6 @@ def _check_funding_transaction(): "method": 'hmyv2_getTransactionByHash', "params": [tx_hash] } - response = requests.request('POST', endpoint_shard_one, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) - tx_data = json.loads(response.content) - return tx_data - except Exception as e: - pytest.skip('Failed to get hmyv2_getTransactionByHash reply', allow_module_level=True) - -def _check_contract_transaction(): - try: - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": 'hmyv2_getTransactionByHash', - "params": [contract_tx_hash] - } response = requests.request('POST', endpoint, headers=headers, data=json.dumps(payload), timeout=timeout, allow_redirects=True) tx_data = json.loads(response.content) @@ -157,29 +134,25 @@ def _check_contract_transaction(): except Exception as e: pytest.skip('Failed to get hmyv2_getTransactionByHash reply', allow_module_level=True) -def _send_contract_transaction(): - try: - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": 'hmyv2_sendRawTransaction', - "params": [contract_raw_transaction] - } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) - tx_data = json.loads(response.content) - if 'error' in staking_tx: - pytest.skip(f"Error in hmyv2_sendRawTransaction reply: {tx_data['error']}", allow_module_level=True) - except Exception as e: - pytest.skip('Failed to get hmyv2_sendRawTransaction reply', allow_module_level=True) - -def _send_staking_transaction(): +def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout = 30): + start_time = time.time() + while((time.time() - start_time) <= timeout): + tx_data = _check_transaction(tx_hash, endpoint) + if tx_data is not None: + block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + if unique_chars != "0": + return True + time.sleep(random.uniform(0.2, 0.5)) + return False + +def _send_staking_transaction(raw_tx, endpoint = endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", "method": 'hmyv2_sendRawStakingTransaction', - "params": [create_validator_raw_transaction] + "params": [raw_tx] } response = requests.request('POST', endpoint, headers=headers, data=json.dumps(payload), timeout=timeout, allow_redirects=True) @@ -189,13 +162,13 @@ def _send_staking_transaction(): except Exception as e: pytest.skip('Failed to get hmyv2_sendRawStakingTransaction reply', allow_module_level=True) -def _check_staking_transaction(): +def _check_staking_transaction(stx_hash, endpoint = endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", "method": 'hmyv2_getStakingTransactionByHash', - "params": [staking_tx_hash] + "params": [stx_hash] } response = requests.request('POST', endpoint, headers=headers, data=json.dumps(payload), timeout=timeout, allow_redirects=True) @@ -203,3 +176,16 @@ def _check_staking_transaction(): return stx_data except Exception as e: pytest.skip('Failed to get hmyv2_getStakingTransactionByHash reply', allow_module_level=True) + +def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout = 30): + answer = False + start_time = time.time() + while((time.time() - start_time) <= timeout): + tx_data = _check_staking_transaction(tx_hash, endpoint) + if tx_data is not None: + block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + if unique_chars != "0": + answer = True + time.sleep(random.uniform(0.2, 0.5)) + return answer diff --git a/tests/sdk-pyhmy/test_account.py b/tests/sdk-pyhmy/test_account.py index 8bf0706..1c1aa4a 100644 --- a/tests/sdk-pyhmy/test_account.py +++ b/tests/sdk-pyhmy/test_account.py @@ -10,10 +10,10 @@ from pyhmy.rpc import ( ) -explorer_endpoint = 'http://localhost:9599' -endpoint_shard_one = 'http://localhost:9501' -local_test_address = 'one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur' -test_validator_address = 'one18tvf56zqjkjnak686lwutcp5mqfnvee35xjnhc' +explorer_endpoint = 'http://localhost:9700' +endpoint_shard_one = 'http://localhost:9502' +local_test_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' +test_validator_address = local_test_address genesis_block_number = 0 test_block_number = 1 fake_shard = 'http://example.com' @@ -31,68 +31,56 @@ def _test_account_rpc(fn, *args, **kwargs): return response -@pytest.mark.run(order=1) def test_get_balance(setup_blockchain): balance = _test_account_rpc(account.get_balance, local_test_address) assert isinstance(balance, int) assert balance > 0 -@pytest.mark.run(order=2) def test_get_balance_by_block(setup_blockchain): balance = _test_account_rpc(account.get_balance_by_block, local_test_address, genesis_block_number) assert isinstance(balance, int) assert balance > 0 -@pytest.mark.run(order=3) def test_get_account_nonce(setup_blockchain): true_nonce = _test_account_rpc(account.get_account_nonce, local_test_address, test_block_number, endpoint=endpoint_shard_one) assert isinstance(true_nonce, int) -@pytest.mark.run(order=4) def test_get_transaction_history(setup_blockchain): tx_history = _test_account_rpc(account.get_transaction_history, local_test_address, endpoint=explorer_endpoint) assert isinstance(tx_history, list) assert len(tx_history) >= 0 -@pytest.mark.run(order=5) def test_get_staking_transaction_history(setup_blockchain): staking_tx_history = _test_account_rpc(account.get_staking_transaction_history, test_validator_address, endpoint=explorer_endpoint) assert isinstance(staking_tx_history, list) assert len(staking_tx_history) > 0 -@pytest.mark.run(order=6) def test_get_balance_on_all_shards(setup_blockchain): balances = _test_account_rpc(account.get_balance_on_all_shards, local_test_address) assert isinstance(balances, list) assert len(balances) == 2 -@pytest.mark.run(order=7) def test_get_total_balance(setup_blockchain): total_balance = _test_account_rpc(account.get_total_balance, local_test_address) assert isinstance(total_balance, int) assert total_balance > 0 -@pytest.mark.run(order=0) def test_is_valid_address(): assert account.is_valid_address('one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur') assert not account.is_valid_address('one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0') -@pytest.mark.run(order=8) def test_get_transaction_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_transaction_count, local_test_address, 'latest') + tx_count = _test_account_rpc(account.get_transaction_count, local_test_address, 'latest', explorer_endpoint) assert isinstance(tx_count, int) assert tx_count > 0 -@pytest.mark.run(order=9) def test_get_transactions_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_transactions_count, local_test_address, 'ALL') + tx_count = _test_account_rpc(account.get_transactions_count, local_test_address, 'ALL', explorer_endpoint) -@pytest.mark.run(order=10) def test_get_staking_transactions_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_staking_transactions_count, local_test_address, 'ALL') + tx_count = _test_account_rpc(account.get_staking_transactions_count, local_test_address, 'ALL', explorer_endpoint) assert isinstance(tx_count, int) -@pytest.mark.run(order=10) def test_errors(): with pytest.raises(exceptions.RPCError): account.get_balance('', fake_shard) diff --git a/tests/sdk-pyhmy/test_blockchain.py b/tests/sdk-pyhmy/test_blockchain.py index edbfb10..4d5efc8 100644 --- a/tests/sdk-pyhmy/test_blockchain.py +++ b/tests/sdk-pyhmy/test_blockchain.py @@ -15,6 +15,7 @@ genesis_block_number = 0 test_block_number = 1 test_block_hash = None fake_shard = 'http://example.com' +address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' def _test_blockchain_rpc(fn, *args, **kwargs): if not callable(fn): @@ -28,54 +29,44 @@ def _test_blockchain_rpc(fn, *args, **kwargs): pytest.fail(f'Unexpected error: {e.__class__} {e}') return response -@pytest.mark.run(order=1) def test_get_node_metadata(setup_blockchain): metadata = _test_blockchain_rpc(blockchain.get_node_metadata) assert isinstance(metadata, dict) -@pytest.mark.run(order=2) def test_get_sharding_structure(setup_blockchain): sharding_structure = _test_blockchain_rpc(blockchain.get_sharding_structure) assert isinstance(sharding_structure, list) assert len(sharding_structure) > 0 -@pytest.mark.run(order=3) def test_get_leader_address(setup_blockchain): leader = _test_blockchain_rpc(blockchain.get_leader_address) assert isinstance(leader, str) assert 'one1' in leader -@pytest.mark.run(order=4) def test_get_block_number(setup_blockchain): current_block_number = _test_blockchain_rpc(blockchain.get_block_number) assert isinstance(current_block_number, int) -@pytest.mark.run(order=5) def test_get_current_epoch(setup_blockchain): current_epoch = _test_blockchain_rpc(blockchain.get_current_epoch) assert isinstance(current_epoch, int) -@pytest.mark.run(order=6) def tset_get_gas_price(setup_blockchain): gas = _test_blockchain_rpc(blockchain.get_gas_price) assert isinstance(gas, int) -@pytest.mark.run(order=7) def test_get_num_peers(setup_blockchain): peers = _test_blockchain_rpc(blockchain.get_num_peers) assert isinstance(peers, int) -@pytest.mark.run(order=8) def test_get_latest_header(setup_blockchain): header = _test_blockchain_rpc(blockchain.get_latest_header) assert isinstance(header, dict) -@pytest.mark.run(order=9) def test_get_latest_chain_headers(setup_blockchain): header_pair = _test_blockchain_rpc(blockchain.get_latest_chain_headers) assert isinstance(header_pair, dict) -@pytest.mark.run(order=10) def test_get_block_by_number(setup_blockchain): global test_block_hash block = _test_blockchain_rpc(blockchain.get_block_by_number, test_block_number) @@ -83,163 +74,134 @@ def test_get_block_by_number(setup_blockchain): assert 'hash' in block.keys() test_block_hash = block['hash'] -@pytest.mark.run(order=11) def test_get_block_by_hash(setup_blockchain): if not test_block_hash: pytest.skip('Failed to get reference block hash') block = _test_blockchain_rpc(blockchain.get_block_by_hash, test_block_hash) assert isinstance(block, dict) -@pytest.mark.run(order=12) def test_get_block_transaction_count_by_number(setup_blockchain): tx_count = _test_blockchain_rpc(blockchain.get_block_transaction_count_by_number, test_block_number) assert isinstance(tx_count, int) -@pytest.mark.run(order=13) def test_get_block_transaction_count_by_hash(setup_blockchain): if not test_block_hash: pytest.skip('Failed to get reference block hash') tx_count = _test_blockchain_rpc(blockchain.get_block_transaction_count_by_hash, test_block_hash) assert isinstance(tx_count, int) -@pytest.mark.run(order=14) def test_get_blocks(setup_blockchain): blocks = _test_blockchain_rpc(blockchain.get_blocks, genesis_block_number, test_block_number) assert isinstance(blocks, list) assert len(blocks) == (test_block_number - genesis_block_number + 1) -@pytest.mark.run(order=15) def test_get_block_signers(setup_blockchain): block_signers = _test_blockchain_rpc(blockchain.get_block_signers, test_block_number) assert isinstance(block_signers, list) assert len(block_signers) > 0 -@pytest.mark.run(order=16) def test_get_validators(setup_blockchain): validators = _test_blockchain_rpc(blockchain.get_validators, test_epoch_number) assert isinstance(validators, dict) assert 'validators' in validators.keys() assert len(validators['validators']) > 0 -@pytest.mark.run(order=17) def test_get_shard(setup_blockchain): shard = _test_blockchain_rpc(blockchain.get_shard) assert isinstance(shard, int) assert shard == 0 -@pytest.mark.run(order=18) def test_get_staking_epoch(setup_blockchain): staking_epoch = _test_blockchain_rpc(blockchain.get_staking_epoch) assert isinstance(staking_epoch, int) -@pytest.mark.run(order=19) def test_get_prestaking_epoch(setup_blockchain): prestaking_epoch = _test_blockchain_rpc(blockchain.get_prestaking_epoch) assert isinstance(prestaking_epoch, int) -@pytest.mark.run(order=20) def test_get_bad_blocks(setup_blockchain): # TODO: Remove skip when RPC is fixed pytest.skip("Known error with hmyv2_getCurrentBadBlocks") bad_blocks = _test_blockchain_rpc(blockchain.get_bad_blocks) assert isinstance(bad_blocks, list) -@pytest.mark.run(order=21) def test_get_validator_keys(setup_blockchain): keys = _test_blockchain_rpc(blockchain.get_validator_keys, test_epoch_number) assert isinstance(keys, list) assert len(keys) > 0 -@pytest.mark.run(order=22) def test_get_block_signers_keys(setup_blockchain): keys = _test_blockchain_rpc(blockchain.get_block_signers_keys, test_block_number) assert isinstance(keys, list) assert len(keys) > 0 -@pytest.mark.run(order=23) def test_chain_id(setup_blockchain): chain_id = _test_blockchain_rpc(blockchain.chain_id) assert isinstance(chain_id, int) -@pytest.mark.run(order=24) def test_get_peer_info(setup_blockchain): peer_info = _test_blockchain_rpc(blockchain.get_peer_info) assert isinstance(peer_info, dict) -@pytest.mark.run(order=25) def test_protocol_version(setup_blockchain): protocol_version = _test_blockchain_rpc(blockchain.protocol_version) assert isinstance(protocol_version, int) -@pytest.mark.run(order=26) def test_is_last_block(setup_blockchain): is_last_block = _test_blockchain_rpc(blockchain.is_last_block, 0) assert isinstance(is_last_block, bool) assert not is_last_block -@pytest.mark.run(order=27) def test_epoch_last_block(setup_blockchain): epoch_last_block = _test_blockchain_rpc(blockchain.epoch_last_block, 0) assert isinstance(epoch_last_block, int) -@pytest.mark.run(order=28) def test_get_circulating_supply(setup_blockchain): circulating_supply = _test_blockchain_rpc(blockchain.get_circulating_supply) assert isinstance(circulating_supply, str) -@pytest.mark.run(order=29) def test_get_total_supply(setup_blockchain): total_supply = _test_blockchain_rpc(blockchain.get_total_supply) assert isinstance(total_supply, str) or total_supply == None -@pytest.mark.run(order=30) def test_get_last_cross_links(setup_blockchain): last_cross_links = _test_blockchain_rpc(blockchain.get_last_cross_links) assert isinstance(last_cross_links, list) -@pytest.mark.run(order=31) def test_get_gas_price(setup_blockchain): gas_price = _test_blockchain_rpc(blockchain.get_gas_price) assert isinstance(gas_price, int) -@pytest.mark.run(order=32) def test_get_version(setup_blockchain): version = _test_blockchain_rpc(blockchain.get_version) assert isinstance(version, int) -@pytest.mark.run(order=33) def test_get_header_by_number(setup_blockchain): header_pair = _test_blockchain_rpc(blockchain.get_header_by_number, 0) assert isinstance(header_pair, dict) -@pytest.mark.run(order=34) def test_get_block_staking_transaction_count_by_number(setup_blockchain): tx_count = _test_blockchain_rpc(blockchain.get_block_staking_transaction_count_by_number, test_block_number) assert isinstance(tx_count, int) -@pytest.mark.run(order=35) def test_get_block_staking_transaction_count_by_hash(setup_blockchain): if not test_block_hash: pytest.skip('Failed to get reference block hash') tx_count = _test_blockchain_rpc(blockchain.get_block_staking_transaction_count_by_hash, test_block_hash) assert isinstance(tx_count, int) -@pytest.mark.run(order=36) def test_is_block_signer(setup_blockchain): - is_signer = _test_blockchain_rpc(blockchain.is_block_signer, test_block_number, '0x0') + is_signer = _test_blockchain_rpc(blockchain.is_block_signer, test_block_number, address) assert isinstance(is_signer, bool) -@pytest.mark.run(order=37) def test_get_signed_blocks(setup_blockchain): - signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, '0x0') + signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address) assert isinstance(signed_blocks, int) -@pytest.mark.run(order=38) def test_in_sync(setup_blockchain): in_sync = _test_blockchain_rpc(blockchain.in_sync) assert isinstance(in_sync, bool) -@pytest.mark.run(order=38) def test_beacon_in_sync(setup_blockchain): beacon_in_sync = _test_blockchain_rpc(blockchain.beacon_in_sync) assert isinstance(beacon_in_sync, bool) diff --git a/tests/sdk-pyhmy/test_contract.py b/tests/sdk-pyhmy/test_contract.py index a5cbea8..59dacc4 100644 --- a/tests/sdk-pyhmy/test_contract.py +++ b/tests/sdk-pyhmy/test_contract.py @@ -9,7 +9,9 @@ from pyhmy.rpc import ( ) explorer_endpoint = 'http://localhost:9599' -contract_tx_hash = '0xa13414dd152173395c69a11e79dea31bf029660f747a42a53744181d05571e70' +contract_tx_hash = '0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f' +# deployedBytecode from json file +contract_code = '0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033' contract_address = None fake_shard = 'http://example.com' @@ -27,34 +29,29 @@ def _test_contract_rpc(fn, *args, **kwargs): pytest.fail(f'Unexpected error: {e.__class__} {e}') return response -@pytest.mark.run(order=1) def test_get_contract_address_from_hash(setup_blockchain): global contract_address contract_address = _test_contract_rpc(contract.get_contract_address_from_hash, contract_tx_hash) assert isinstance(contract_address, str) -@pytest.mark.run(order=2) def test_call(setup_blockchain): if not contract_address: pytest.skip('Contract address not loaded yet') called = _test_contract_rpc(contract.call, contract_address, 'latest') assert isinstance(called, str) and called.startswith('0x') -@pytest.mark.run(order=3) def test_estimate_gas(setup_blockchain): if not contract_address: pytest.skip('Contract address not loaded yet') gas = _test_contract_rpc(contract.estimate_gas, contract_address) assert isinstance(gas, int) -@pytest.mark.run(order=4) def test_get_code(setup_blockchain): if not contract_address: pytest.skip('Contract address not loaded yet') code = _test_contract_rpc(contract.get_code, contract_address, 'latest') - assert code == '0x608060405234801561001057600080fd5b50600436106100415760003560e01c8063445df0ac146100465780638da5cb5b14610064578063fdacd576146100ae575b600080fd5b61004e6100dc565b6040518082815260200191505060405180910390f35b61006c6100e2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6100da600480360360208110156100c457600080fd5b8101908080359060200190929190505050610107565b005b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561016457806001819055505b5056fea265627a7a723158209b80813a158b44af65aee232b44c0ac06472c48f4abbe298852a39f0ff34a9f264736f6c63430005100032' + assert code == contract_code -@pytest.mark.run(order=5) def test_get_storage_at(setup_blockchain): if not contract_address: pytest.skip('Contract address not loaded yet') diff --git a/tests/sdk-pyhmy/test_staking.py b/tests/sdk-pyhmy/test_staking.py index 115f834..f84375a 100644 --- a/tests/sdk-pyhmy/test_staking.py +++ b/tests/sdk-pyhmy/test_staking.py @@ -10,8 +10,8 @@ from pyhmy.rpc import ( ) -explorer_endpoint = 'http://localhost:9599' -test_validator_address = 'one18tvf56zqjkjnak686lwutcp5mqfnvee35xjnhc' +explorer_endpoint = 'http://localhost:9700' +test_validator_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' fake_shard = 'http://example.com' def _test_staking_rpc(fn, *args, **kwargs): @@ -26,126 +26,105 @@ def _test_staking_rpc(fn, *args, **kwargs): pytest.fail(f'Unexpected error: {e.__class__} {e}') return response -@pytest.mark.run(order=1) def test_get_all_validator_addresses(setup_blockchain): validator_addresses = _test_staking_rpc(staking.get_all_validator_addresses) assert isinstance(validator_addresses, list) assert len(validator_addresses) > 0 assert test_validator_address in validator_addresses -@pytest.mark.run(order=2) def test_get_validator_information(setup_blockchain): info = _test_staking_rpc(staking.get_validator_information, test_validator_address) assert isinstance(info, dict) -@pytest.mark.run(order=3) def test_get_all_validator_information(setup_blockchain): all_validator_information = _test_staking_rpc(staking.get_all_validator_information) assert isinstance(all_validator_information, list) assert len(all_validator_information) > 0 -@pytest.mark.run(order=4) def test_get_delegations_by_delegator(setup_blockchain): delegations = _test_staking_rpc(staking.get_delegations_by_delegator, test_validator_address) assert isinstance(delegations, list) assert len(delegations) > 0 -@pytest.mark.run(order=5) def test_get_delegations_by_validator(setup_blockchain): delegations = _test_staking_rpc(staking.get_delegations_by_validator, test_validator_address) assert isinstance(delegations, list) assert len(delegations) > 0 -@pytest.mark.run(order=6) def test_get_current_utility_metrics(setup_blockchain): metrics = _test_staking_rpc(staking.get_current_utility_metrics) assert isinstance(metrics, dict) -@pytest.mark.run(order=7) def test_get_staking_network_info(setup_blockchain): info = _test_staking_rpc(staking.get_staking_network_info) assert isinstance(info, dict) -@pytest.mark.run(order=8) def test_get_super_committees(setup_blockchain): committee = _test_staking_rpc(staking.get_super_committees) assert isinstance(committee, dict) -@pytest.mark.run(order=9) def test_get_raw_median_stake_snapshot(setup_blockchain): median_stake = _test_staking_rpc(staking.get_raw_median_stake_snapshot) assert isinstance(median_stake, dict) -@pytest.mark.run(order=10) def test_get_validator_information_by_block(setup_blockchain): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block - info = _test_staking_rpc(staking.get_validator_information_by_block_number, test_validator_address, setup_blockchain + 1, endpoint=explorer_endpoint) + info = _test_staking_rpc(staking.get_validator_information_by_block_number, test_validator_address, 'latest', endpoint=explorer_endpoint) assert isinstance(info, dict) -@pytest.mark.run(order=11) def test_get_validator_information_by_block(setup_blockchain): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block - info = _test_staking_rpc(staking.get_all_validator_information_by_block_number, setup_blockchain + 1, endpoint=explorer_endpoint) + info = _test_staking_rpc(staking.get_all_validator_information_by_block_number, 'latest', endpoint=explorer_endpoint) assert isinstance(info, list) -@pytest.mark.run(order=12) def test_get_delegations_by_delegator_by_block(setup_blockchain): - delegations = _test_staking_rpc(staking.get_delegations_by_delegator_by_block_number, test_validator_address, setup_blockchain + 1, endpoint=explorer_endpoint) + delegations = _test_staking_rpc(staking.get_delegations_by_delegator_by_block_number, test_validator_address, 'latest', endpoint=explorer_endpoint) assert isinstance(delegations, list) -@pytest.mark.run(order=13) def test_get_elected_validator_addresses(setup_blockchain): validator_addresses = _test_staking_rpc(staking.get_elected_validator_addresses) assert isinstance(validator_addresses, list) assert len(validator_addresses) > 0 -@pytest.mark.run(order=14) def test_get_validators(setup_blockchain): validators = _test_staking_rpc(staking.get_validators, 2) assert isinstance(validators, dict) assert len(validators['validators']) > 0 -@pytest.mark.run(order=15) def test_get_validator_keys(setup_blockchain): validators = _test_staking_rpc(staking.get_validator_keys, 2) assert isinstance(validators, list) -@pytest.mark.run(order=16) def test_get_validator_self_delegation(setup_blockchain): self_delegation = _test_staking_rpc(staking.get_validator_self_delegation, test_validator_address) assert isinstance(self_delegation, int) assert self_delegation > 0 -@pytest.mark.run(order=17) def test_get_validator_total_delegation(setup_blockchain): total_delegation = _test_staking_rpc(staking.get_validator_total_delegation, test_validator_address) assert isinstance(total_delegation, int) assert total_delegation > 0 -@pytest.mark.run(order=18) def test_get_all_delegation_information(setup_blockchain): delegation_information = _test_staking_rpc(staking.get_all_delegation_information, 0) assert isinstance(delegation_information, list) assert len(delegation_information) > 0 -@pytest.mark.run(order=19) def test_get_delegation_by_delegator_and_validator(setup_blockchain): delegation_information = _test_staking_rpc(staking.get_delegation_by_delegator_and_validator, test_validator_address, test_validator_address) assert isinstance(delegation_information, dict) -@pytest.mark.run(order=20) def test_get_available_redelegation_balance(setup_blockchain): redelgation_balance = _test_staking_rpc(staking.get_available_redelegation_balance, test_validator_address) assert isinstance(redelgation_balance, int) assert redelgation_balance == 0 -@pytest.mark.run(order=21) def test_get_total_staking(setup_blockchain): total_staking = _test_staking_rpc(staking.get_total_staking) assert isinstance(total_staking, int) - assert total_staking > 0 + if staking.get_validator_information(test_validator_address, explorer_endpoint)[ 'active-status' ] == 'active': + assert total_staking > 0 -@pytest.mark.run(order=22) def test_errors(): with pytest.raises(exceptions.RPCError): staking.get_all_validator_addresses(fake_shard) diff --git a/tests/sdk-pyhmy/test_staking_signing.py b/tests/sdk-pyhmy/test_staking_signing.py index 2b15c3d..9e7aefe 100644 --- a/tests/sdk-pyhmy/test_staking_signing.py +++ b/tests/sdk-pyhmy/test_staking_signing.py @@ -9,6 +9,7 @@ from pyhmy.numbers import ( # other transactions (create/edit validator) are in test_validator.py # test_delegate is the same as test_undelegate (except the directive) so it has been omitted +# staking transactions without a chain id have been omitted as well, since the node does not accept them anyway """ let stakingTx @@ -27,16 +28,16 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ -def test_collect_rewards_no_chain_id(): - transaction_dict = { - 'directive': staking_structures.Directive.CollectRewards, - 'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', - 'nonce': 2, - 'gasPrice': int(convert_one_to_atto(1)), - 'gasLimit': 100, - } - signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf85a04d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5823a0490e4ceb747563ba40da3e0db8a65133cf6f6ae4c48a24866cd6aa1f0d6c2414a06dbd51a67b35b5685e7b7420cba26e63b0e7d3c696fc6cb69d48e54fcad280e9' +# def test_collect_rewards_no_chain_id(): +# transaction_dict = { +# 'directive': staking_structures.Directive.CollectRewards, +# 'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', +# 'nonce': 2, +# 'gasPrice': int(convert_one_to_atto(1)), +# 'gasLimit': 100, +# } +# signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') +# assert signed_tx.rawTransaction.hex() == '0xf85a04d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5823a0490e4ceb747563ba40da3e0db8a65133cf6f6ae4c48a24866cd6aa1f0d6c2414a06dbd51a67b35b5685e7b7420cba26e63b0e7d3c696fc6cb69d48e54fcad280e9' """ let stakingTx @@ -80,7 +81,7 @@ stakingTx = new StakingTransaction( 2, // nonce numberToHex(new Unit('1').asOne().toWei()), // gasPrice 100, // gasLimit - null, // chainId + 2, // chainId ); const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') console.log( 'Signed transaction' ) @@ -95,6 +96,7 @@ def test_delegate(): 'nonce': 2, 'gasPrice': int(convert_one_to_atto(1)), 'gasLimit': 100, + 'chainId': 2, } signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf87002eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580523a0aceff4166ec0ecd0cc664fed865270fe77b35e408138950f802129f1f3d06a74a06f9aca402fb6b4842bff8d65f430d82eefa95645e9046b102195d1044993f9fe' + assert signed_tx.rawTransaction.hex() == '0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6' diff --git a/tests/sdk-pyhmy/test_transaction.py b/tests/sdk-pyhmy/test_transaction.py index fde7c1b..2bbcd61 100644 --- a/tests/sdk-pyhmy/test_transaction.py +++ b/tests/sdk-pyhmy/test_transaction.py @@ -8,29 +8,30 @@ from pyhmy.rpc import ( exceptions ) -from pyhmy.exceptions import ( - TxConfirmationTimedoutError -) +endpoint = 'http://localhost:9500' +endpoint_shard_one = 'http://localhost:9502' +fake_shard = 'http://example.com' -localhost_shard_one = 'http://localhost:9501' -tx_hash = '0x1fa20537ea97f162279743139197ecf0eac863278ac1c8ada9a6be5d1e31e633' +# previously sent txs to get and check +tx_hash = '0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43' tx_block_num = None tx_block_hash = None -cx_hash = '0x1fa20537ea97f162279743139197ecf0eac863278ac1c8ada9a6be5d1e31e633' -stx_hash = '0x57ec011aabdeb078a4816502224022f291fa8b07c82bbae8476f514a1d71c730' +tx_index = None + +cx_hash = '0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf' + +stx_hash = '0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f' stx_block_num = None stx_block_hash = None -test_index = 0 -fake_shard = 'http://example.com' +stx_index = None -# raw_txt generated via: -# hmy transfer --from one12fuf7x9rgtdgqg7vgq0962c556m3p7afsxgvll --to one12fuf7x9rgtdgqg7vgq0962c556m3p7afsxgvll -# --from-shard 0 --to-shard 1 --amount 0.1 --dry-run -raw_tx = '0xf86d01843b9aca0082520880019452789f18a342da8023cc401e5d2b14a6b710fba988016345785d8a00008028a01095f775386e0e3203446179a7a62e5ce1e765c200b5d885f6bb5b141155bd4da0651350a31e1797035cbf878e4c26069e9895845071d01308573532512cca5820' -raw_tx_hash = '0x86bce2e7765937b776bdcf927339c85421b95c70ddf06ba8e4cc0441142b0f53' +# new txs to send and check +raw_tx = '0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0' +raw_tx_hash = '0x7ccd80f8513f76ec58b357c7a82a12a95e025d88f1444e953f90e3d86e222571' -raw_stx = '0xf9015680f90105943ad89a684095a53edb47d7ddc5e034d813366731d984746573748474657374847465737484746573748474657374ddc988016345785d8a0000c9880c7d713b49da0000c887b1a2bc2ec500008a022385a827e8155000008b084595161401484a000000f1b0282554f2478661b4844a05a9deb1837aac83931029cb282872f0dcd7239297c499c02ea8da8746d2f08ca2b037e89891f862b86003557e18435c201ecc10b1664d1aea5b4ec59dbfe237233b953dbd9021b86bc9770e116ed3c413fe0334d89562568a10e133d828611f29fee8cdab9719919bbcc1f1bf812c73b9ccd0f89b4f0b9ca7e27e66d58bbb06fcf51c295b1d076cfc878a0228f16f86157860000080843b9aca008351220027a018385211a150ca032c3526cef0aba6a75f99a18cb73f547f67bab746be0c7a64a028be921002c6eb949b3932afd010dfe1de2459ec7fe84403b9d9d8892394a78c' +raw_stx = '0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000008085174876e80082c35027a0808ea7d27adf3b1f561e8da4676814084bb75ac541b616bece87c6446e6cc54ea02f19f0b14240354bd42ad60b0c7189873c0be87044e13072b0981a837ca76f64' +raw_stx_hash = '0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629' def _test_transaction_rpc(fn, *args, **kwargs): if not callable(fn): @@ -41,19 +42,15 @@ def _test_transaction_rpc(fn, *args, **kwargs): except Exception as e: if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): pytest.skip(f'{str(e)}') - if isinstance(e, TxConfirmationTimedoutError): - pytest.skip(f'{str(e)}') pytest.fail(f'Unexpected error: {e.__class__} {e}') return response -@pytest.mark.run(order=1) def test_get_pending_transactions(setup_blockchain): pool = _test_transaction_rpc(transaction.get_pending_transactions) assert isinstance(pool, list) -@pytest.mark.run(order=2) def test_get_transaction_by_hash(setup_blockchain): - tx = _test_transaction_rpc(transaction.get_transaction_by_hash, tx_hash, endpoint=localhost_shard_one) + tx = _test_transaction_rpc(transaction.get_transaction_by_hash, tx_hash, endpoint=endpoint) assert tx assert isinstance(tx, dict) assert 'blockNumber' in tx.keys() @@ -62,76 +59,55 @@ def test_get_transaction_by_hash(setup_blockchain): tx_block_num = int(tx['blockNumber']) global tx_block_hash tx_block_hash = tx['blockHash'] + global tx_index + tx_index = int(tx[ 'transactionIndex' ]) -@pytest.mark.run(order=3) def test_get_transaction_by_block_hash_and_index(setup_blockchain): if not tx_block_hash: pytest.skip('Failed to get reference block hash') tx = _test_transaction_rpc(transaction.get_transaction_by_block_hash_and_index, - tx_block_hash, test_index, endpoint=localhost_shard_one) + tx_block_hash, tx_index, endpoint=endpoint) assert tx assert isinstance(tx, dict) -@pytest.mark.run(order=4) def test_get_transaction_by_block_number_and_index(setup_blockchain): if not tx_block_num: pytest.skip('Failed to get reference block num') - tx = _test_transaction_rpc(transaction.get_transaction_by_block_number_and_index, tx_block_num, test_index, - endpoint=localhost_shard_one) + tx = _test_transaction_rpc(transaction.get_transaction_by_block_number_and_index, tx_block_num, tx_index, + endpoint=endpoint) assert tx assert isinstance(tx, dict) -@pytest.mark.run(order=5) def test_get_transaction_receipt(setup_blockchain): - tx_receipt = _test_transaction_rpc(transaction.get_transaction_receipt, tx_hash, endpoint=localhost_shard_one) + tx_receipt = _test_transaction_rpc(transaction.get_transaction_receipt, tx_hash, endpoint=endpoint) assert tx_receipt assert isinstance(tx_receipt, dict) -@pytest.mark.run(order=6) def test_get_transaction_error_sink(setup_blockchain): errors = _test_transaction_rpc(transaction.get_transaction_error_sink) assert isinstance(errors, list) -@pytest.mark.run(order=7) 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. - # Secondly, this is a test that seems to return None values - for example the below curl call has the same null value - # curl --location --request POST 'http://localhost:9501' \ - # --header 'Content-Type: application/json' \ - # --data-raw '{ - # "jsonrpc": "2.0", - # "id": 1, - # "method": "hmyv2_getTransactionByHash", - # "params": [ - # "0x86bce2e7765937b776bdcf927339c85421b95c70ddf06ba8e4cc0441142b0f53" - # ] - # }' - # {"jsonrpc":"2.0","id":1,"result":null} test_tx = _test_transaction_rpc(transaction.send_and_confirm_raw_transaction, - raw_tx) # mining stops by the time this transaction is submitted - # so it never confirms, which is why TxConfirmationTimedoutError - # is in the set up call + raw_tx) assert isinstance(test_tx, dict) assert test_tx[ 'hash' ] == raw_tx_hash -@pytest.mark.run(order=8) def test_get_pending_cx_receipts(setup_blockchain): pending = _test_transaction_rpc(transaction.get_pending_cx_receipts) assert isinstance(pending, list) -@pytest.mark.run(order=9) def test_get_cx_receipt_by_hash(setup_blockchain): - cx = _test_transaction_rpc(transaction.get_cx_receipt_by_hash, cx_hash) + cx = _test_transaction_rpc(transaction.get_cx_receipt_by_hash, cx_hash, endpoint_shard_one) assert cx assert isinstance(cx, dict) -@pytest.mark.run(order=10) def test_resend_cx_receipt(setup_blockchain): sent = _test_transaction_rpc(transaction.resend_cx_receipt, cx_hash) assert isinstance(sent, bool) - assert not sent + assert sent -@pytest.mark.run(order=11) def test_get_staking_transaction_by_hash(setup_blockchain): staking_tx = _test_transaction_rpc(transaction.get_staking_transaction_by_hash, stx_hash) assert staking_tx @@ -142,45 +118,40 @@ def test_get_staking_transaction_by_hash(setup_blockchain): stx_block_num = int(staking_tx['blockNumber']) global stx_block_hash stx_block_hash = staking_tx['blockHash'] + global stx_index + stx_index = int(staking_tx['transactionIndex']) -@pytest.mark.run(order=12) def test_get_transaction_by_block_hash_and_index(setup_blockchain): if not stx_block_hash: pytest.skip('Failed to get reference block hash') - stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_hash_and_index, stx_block_hash, test_index) + stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_hash_and_index, stx_block_hash, stx_index) assert stx assert isinstance(stx, dict) -@pytest.mark.run(order=13) def test_get_transaction_by_block_number_and_index(setup_blockchain): if not stx_block_num: pytest.skip('Failed to get reference block num') - stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_number_and_index, stx_block_num, test_index) + stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_number_and_index, stx_block_num, stx_index) assert stx assert isinstance(stx, dict) -@pytest.mark.run(order=14) def test_get_staking_transaction_error_sink(setup_blockchain): errors = _test_transaction_rpc(transaction.get_staking_transaction_error_sink) assert isinstance(errors, list) -@pytest.mark.run(order=15) def test_send_raw_staking_transaction(setup_blockchain): - test_stx_hash = _test_transaction_rpc(transaction.send_raw_staking_transaction, raw_stx, endpoint=localhost_shard_one) - assert isinstance(test_stx_hash, str) - assert test_stx_hash == stx_hash + test_stx = _test_transaction_rpc(transaction.send_and_confirm_raw_staking_transaction, raw_stx, endpoint=endpoint) + assert isinstance(test_stx, dict) + assert test_stx[ 'hash' ] == raw_stx_hash -@pytest.mark.run(order=16) def test_get_pool_stats(setup_blockchain): - test_pool_stats = _test_transaction_rpc(transaction.get_pool_stats, endpoint=localhost_shard_one) + test_pool_stats = _test_transaction_rpc(transaction.get_pool_stats, endpoint=endpoint) assert isinstance(test_pool_stats, dict) -@pytest.mark.run(order=17) def test_get_pending_staking_transactions(setup_blockchain): - pending_staking_transactions = _test_transaction_rpc(transaction.get_pending_staking_transactions, endpoint=localhost_shard_one) + pending_staking_transactions = _test_transaction_rpc(transaction.get_pending_staking_transactions, endpoint=endpoint) assert isinstance(pending_staking_transactions, list) -@pytest.mark.run(order=18) def test_errors(): with pytest.raises(exceptions.RPCError): transaction.get_pending_transactions(fake_shard) diff --git a/tests/sdk-pyhmy/test_validator.py b/tests/sdk-pyhmy/test_validator.py index 990b741..f6269e8 100644 --- a/tests/sdk-pyhmy/test_validator.py +++ b/tests/sdk-pyhmy/test_validator.py @@ -1,5 +1,4 @@ import pytest -import requests from decimal import ( Decimal ) @@ -8,10 +7,6 @@ from pyhmy import ( validator ) -from pyhmy.rpc import ( - exceptions -) - from pyhmy.numbers import ( convert_one_to_atto ) @@ -20,21 +15,17 @@ from pyhmy.exceptions import ( InvalidValidatorError ) -import sys - test_epoch_number = 0 genesis_block_number = 0 test_block_number = 1 test_validator_object = None test_validator_loaded = False -@pytest.mark.run(order=0) def test_instantiate_validator(setup_blockchain): global test_validator_object test_validator_object = validator.Validator('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') assert isinstance(test_validator_object, validator.Validator) -@pytest.mark.run(order=1) def test_load_validator(setup_blockchain): if not test_validator_object: pytest.skip('Validator not instantiated yet') @@ -84,17 +75,16 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ -@pytest.mark.run(order=2) def test_create_validator_sign(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not ready yet') + pytest.skip('Validator not instantiated yet') signed_hash = test_validator_object.sign_create_validator_transaction( 2, int(convert_one_to_atto(1)), 100, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', - None).rawTransaction.hex() - assert signed_hash == '0xf9016a80f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000023a0650feed3d7bbcb9ed36575c829bfdc600d53cd7a4fcc0e78ddf26d740f1900a8a027798fc245ee5c66847327b7454670b3b84cb3998527c4fbc0f967b787f56bc2' + 2).rawTransaction.hex() + assert signed_hash == '0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd' """ Signature matched from TypeScript (is outdated because the JS SDK has not been updated for SlotKeyToAddSig) @@ -137,10 +127,9 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ -@pytest.mark.run(order=3) def test_edit_validator_sign(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not ready yet') + pytest.skip('Validator not instantiated yet') signed_hash = test_validator_object.sign_edit_validator_transaction( 2, int(convert_one_to_atto(1)), @@ -153,10 +142,9 @@ def test_edit_validator_sign(setup_blockchain): 2).rawTransaction.hex() assert signed_hash == '0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995' -@pytest.mark.run(order=4) def test_invalid_validator(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not ready yet') + pytest.skip('Validator not instantiated yet') with pytest.raises(InvalidValidatorError): info = { 'name': 'Alice', @@ -185,10 +173,9 @@ def test_invalid_validator(setup_blockchain): with pytest.raises(InvalidValidatorError): test_validator_object.set_rate('-2.0') -@pytest.mark.run(order=5) def test_validator_getters(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not ready yet') + pytest.skip('Validator not instantiated yet') assert test_validator_object.get_address() == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' assert test_validator_object.add_bls_key('5') assert test_validator_object.remove_bls_key('5') @@ -205,7 +192,6 @@ def test_validator_getters(setup_blockchain): assert isinstance(test_validator_object.get_rate(), Decimal) assert len(test_validator_object.get_bls_keys()) > 0 -@pytest.mark.run(order=6) def test_validator_load_from_blockchain(setup_blockchain): - test_validator_object2 = validator.Validator('one109r0tns7av5sjew7a7fkekg4fs3pw0h76pp45e') + test_validator_object2 = validator.Validator('one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3') test_validator_object2.load_from_blockchain() From a3295b9650e87e500241f063c72aaa30e5b97a60 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 19:05:57 +0000 Subject: [PATCH 18/23] chore: run `black` on all files --- pyhmy/__init__.py | 20 +- pyhmy/_version.py | 2 +- pyhmy/account.py | 227 +++++++------ pyhmy/bech32/bech32.py | 17 +- pyhmy/blockchain.py | 417 +++++++++++++++--------- pyhmy/cli.py | 113 +++++-- pyhmy/contract.py | 125 ++++--- pyhmy/exceptions.py | 25 +- pyhmy/logging.py | 41 ++- pyhmy/rpc/exceptions.py | 8 +- pyhmy/rpc/request.py | 45 ++- pyhmy/signing.py | 155 ++++----- pyhmy/staking.py | 280 +++++++++------- pyhmy/staking_signing.py | 363 ++++++++++++--------- pyhmy/staking_structures.py | 215 ++++++++---- pyhmy/transaction.py | 260 ++++++++------- pyhmy/util.py | 98 +++--- pyhmy/validator.py | 289 +++++++++------- tests/bech32-pyhmy/test_bech32.py | 11 +- tests/logging-pyhmy/test_logging.py | 4 +- tests/numbers-pyhmy/test_numbers.py | 9 +- tests/request-pyhmy/test_request.py | 69 ++-- tests/sdk-pyhmy/conftest.py | 224 +++++++++---- tests/sdk-pyhmy/test_account.py | 98 ++++-- tests/sdk-pyhmy/test_blockchain.py | 117 +++++-- tests/sdk-pyhmy/test_contract.py | 71 ++-- tests/sdk-pyhmy/test_signing.py | 55 ++-- tests/sdk-pyhmy/test_staking.py | 124 +++++-- tests/sdk-pyhmy/test_staking_signing.py | 62 ++-- tests/sdk-pyhmy/test_transaction.py | 174 ++++++---- tests/sdk-pyhmy/test_validator.py | 150 +++++---- tests/util-pyhmy/test_util.py | 43 ++- 32 files changed, 2374 insertions(+), 1537 deletions(-) diff --git a/pyhmy/__init__.py b/pyhmy/__init__.py index 5aefb14..ebdebee 100644 --- a/pyhmy/__init__.py +++ b/pyhmy/__init__.py @@ -3,28 +3,16 @@ import warnings from ._version import __version__ -from .util import ( - Typgpy, - get_gopath, - get_goversion, - get_bls_build_variables, - json_load -) +from .util import Typgpy, get_gopath, get_goversion, get_bls_build_variables, json_load if sys.version_info.major < 3: warnings.simplefilter("always", DeprecationWarning) warnings.warn( - DeprecationWarning( - "`pyhmy` does not support Python 2. Please use Python 3." - ) + DeprecationWarning("`pyhmy` does not support Python 2. Please use Python 3.") ) warnings.resetwarnings() -if sys.platform.startswith('win32') or sys.platform.startswith('cygwin'): +if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"): warnings.simplefilter("always", ImportWarning) - warnings.warn( - ImportWarning( - "`pyhmy` does not work on Windows or Cygwin." - ) - ) + warnings.warn(ImportWarning("`pyhmy` does not work on Windows or Cygwin.")) warnings.resetwarnings() diff --git a/pyhmy/_version.py b/pyhmy/_version.py index 09a940e..dc1891a 100644 --- a/pyhmy/_version.py +++ b/pyhmy/_version.py @@ -7,5 +7,5 @@ Provides pyhmy version information. from incremental import Version -__version__ = Version('pyhmy', 20, 5, 20) +__version__ = Version("pyhmy", 20, 5, 20) __all__ = ["__version__"] diff --git a/pyhmy/account.py b/pyhmy/account.py index 60c0494..5c53b3b 100644 --- a/pyhmy/account.py +++ b/pyhmy/account.py @@ -1,26 +1,14 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .rpc.exceptions import ( - RPCError, - RequestsError, - RequestsTimeoutError -) +from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -from .blockchain import ( - get_sharding_structure -) +from .blockchain import get_sharding_structure -from .bech32.bech32 import ( - bech32_decode -) +from .bech32.bech32 import bech32_decode -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 _address_length = 42 @@ -40,13 +28,14 @@ def is_valid_address(address) -> bool: bool Is valid address """ - if not address.startswith('one1'): + if not address.startswith("one1"): return False hrp, _ = bech32_decode(address) if not hrp: return False return True + def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current account balance @@ -74,17 +63,20 @@ def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) - ------------- https://api.hmny.io/#da8901d2-d237-4c3b-9d7d-10af9def05c4 """ - method = 'hmyv2_getBalance' - params = [ - address - ] + method = "hmyv2_getBalance" + params = [address] try: - balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] - return int(balance) # v2 returns the result as it is - except TypeError as e: # check will work if rpc returns None + balance = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] + return int(balance) # v2 returns the result as it is + except TypeError as e: # check will work if rpc returns None raise InvalidRPCReplyError(method, endpoint) from e -def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_balance_by_block( + address, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get account balance for address at a given block number @@ -114,18 +106,20 @@ def get_balance_by_block(address, block_num, endpoint=_default_endpoint, timeout https://api.hmny.io/#9aeae4b8-1a09-4ed2-956b-d7c96266dd33 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92 """ - method = 'hmyv2_getBalanceByBlockNumber' - params = [ - address, - block_num - ] + method = "hmyv2_getBalanceByBlockNumber" + params = [address, block_num] try: - balance = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + balance = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] return int(balance) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_account_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_account_nonce( + address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the account nonce @@ -154,24 +148,29 @@ def get_account_nonce(address, block_num='latest', endpoint=_default_endpoint, t ------------- https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L51 """ - method = 'hmyv2_getAccountNonce' - params = [ - address, - block_num - ] + method = "hmyv2_getAccountNonce" + params = [address, block_num] try: - nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] return int(nonce) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_nonce(address, block_num='latest', endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_nonce( + address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ See get_account_nonce """ return get_account_nonce(address, block_num, endpoint, timeout) -def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_transaction_count( + address, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the number of transactions the given address has sent for the given block number Legacy for apiv1. For apiv2, please use get_account_nonce/get_transactions_count/get_staking_transactions_count apis for @@ -202,18 +201,20 @@ def get_transaction_count(address, block_num, endpoint=_default_endpoint, timeou ------------- https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L69 """ - method = 'hmyv2_getTransactionCount' - params = [ - address, - block_num - ] + method = "hmyv2_getTransactionCount" + params = [address, block_num] try: - nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] return int(nonce) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_transactions_count( + address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the number of regular transactions from genesis of input type @@ -244,18 +245,20 @@ def get_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout https://api.hmny.io/#fc97aed2-e65e-4cf4-bc01-8dadb76732c0 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114 """ - method = 'hmyv2_getTransactionsCount' - params = [ - address, - tx_type - ] + method = "hmyv2_getTransactionsCount" + params = [address, tx_type] try: - tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + tx_count = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] return int(tx_count) except TypeError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transactions_count(address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_staking_transactions_count( + address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL") @@ -286,20 +289,27 @@ def get_staking_transactions_count(address, tx_type, endpoint=_default_endpoint, https://api.hmny.io/#ddc1b029-f341-4c4d-ba19-74b528d6e5e5 https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134 """ - method = 'hmyv2_getStakingTransactionsCount' - params = [ - address, - tx_type - ] + method = "hmyv2_getStakingTransactionsCount" + params = [address, tx_type] try: - tx_count = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + tx_count = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] return int(tx_count) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL', - order='ASC', endpoint=_default_endpoint, timeout=_default_timeout - ) -> list: + +def get_transaction_history( + address, + page=0, + page_size=1000, + include_full_tx=False, + tx_type="ALL", + order="ASC", + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> list: """ Get list of transactions sent and/or received by the account @@ -345,24 +355,34 @@ def get_transaction_history(address, page=0, page_size=1000, include_full_tx=Fal """ params = [ { - 'address': address, - 'pageIndex': page, - 'pageSize': page_size, - 'fullTx': include_full_tx, - 'txType': tx_type, - 'order': order + "address": address, + "pageIndex": page, + "pageSize": page_size, + "fullTx": include_full_tx, + "txType": tx_type, + "order": order, } ] - method = 'hmyv2_getTransactionsHistory' + method = "hmyv2_getTransactionsHistory" try: - tx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout) - return tx_history['result']['transactions'] + tx_history = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + ) + return tx_history["result"]["transactions"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_history(address, page=0, page_size=1000, include_full_tx=False, tx_type='ALL', - order='ASC', endpoint=_default_endpoint, timeout=_default_timeout - ) -> list: + +def get_staking_transaction_history( + address, + page=0, + page_size=1000, + include_full_tx=False, + tx_type="ALL", + order="ASC", + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> list: """ Get list of staking transactions sent by the account @@ -419,23 +439,28 @@ def get_staking_transaction_history(address, page=0, page_size=1000, include_ful """ params = [ { - 'address': address, - 'pageIndex': page, - 'pageSize': page_size, - 'fullTx': include_full_tx, - 'txType': tx_type, - 'order': order + "address": address, + "pageIndex": page, + "pageSize": page_size, + "fullTx": include_full_tx, + "txType": tx_type, + "order": order, } ] # Using v2 API, because getStakingTransactionHistory not implemented in v1 - method = 'hmyv2_getStakingTransactionsHistory' + method = "hmyv2_getStakingTransactionsHistory" try: - stx_history = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] - return stx_history['staking_transactions'] + stx_history = rpc_request( + method, params=params, endpoint=endpoint, timeout=timeout + )["result"] + return stx_history["staking_transactions"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_balance_on_all_shards(address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_balance_on_all_shards( + address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get current account balance in all shards & optionally report errors getting account balance for a shard @@ -467,19 +492,23 @@ def get_balance_on_all_shards(address, skip_error=True, endpoint=_default_endpoi sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout) for shard in sharding_structure: try: - balances.append({ - 'shard': shard['shardID'], - 'balance': get_balance(address, endpoint=shard['http'], timeout=timeout) - }) + balances.append( + { + "shard": shard["shardID"], + "balance": get_balance( + address, endpoint=shard["http"], timeout=timeout + ), + } + ) except (KeyError, RPCError, RequestsError, RequestsTimeoutError): if not skip_error: - balances.append({ - 'shard': shard['shardID'], - 'balance': None - }) + balances.append({"shard": shard["shardID"], "balance": None}) return balances -def get_total_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_total_balance( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get total account balance on all shards @@ -507,7 +536,9 @@ def get_total_balance(address, endpoint=_default_endpoint, timeout=_default_time get_balance_on_all_shards """ try: - balances = get_balance_on_all_shards(address, skip_error=False, endpoint=endpoint, timeout=timeout) - return sum(b['balance'] for b in balances) + balances = get_balance_on_all_shards( + address, skip_error=False, endpoint=endpoint, timeout=timeout + ) + return sum(b["balance"] for b in balances) except TypeError as e: raise RuntimeError from e diff --git a/pyhmy/bech32/bech32.py b/pyhmy/bech32/bech32.py index d450080..212705d 100644 --- a/pyhmy/bech32/bech32.py +++ b/pyhmy/bech32/bech32.py @@ -26,11 +26,11 @@ CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" def bech32_polymod(values): """Internal function that computes the Bech32 checksum.""" - generator = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3] + generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3] chk = 1 for value in values: top = chk >> 25 - chk = (chk & 0x1ffffff) << 5 ^ value + chk = (chk & 0x1FFFFFF) << 5 ^ value for i in range(5): chk ^= generator[i] if ((top >> i) & 1) else 0 return chk @@ -56,22 +56,23 @@ def bech32_create_checksum(hrp, data): def bech32_encode(hrp, data): """Compute a Bech32 string given HRP and data values.""" combined = data + bech32_create_checksum(hrp, data) - return hrp + '1' + ''.join([CHARSET[d] for d in combined]) + return hrp + "1" + "".join([CHARSET[d] for d in combined]) def bech32_decode(bech): """Validate a Bech32 string, and determine HRP and data.""" - if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or - (bech.lower() != bech and bech.upper() != bech)): + if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or ( + bech.lower() != bech and bech.upper() != bech + ): return (None, None) bech = bech.lower() - pos = bech.rfind('1') + pos = bech.rfind("1") if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: return (None, None) - if not all(x in CHARSET for x in bech[pos+1:]): + if not all(x in CHARSET for x in bech[pos + 1 :]): return (None, None) hrp = bech[:pos] - data = [CHARSET.find(x) for x in bech[pos+1:]] + data = [CHARSET.find(x) for x in bech[pos + 1 :]] if not bech32_verify_checksum(hrp, data): return (None, None) return (hrp, data[:-6]) diff --git a/pyhmy/blockchain.py b/pyhmy/blockchain.py index 50bb3b3..9962c14 100644 --- a/pyhmy/blockchain.py +++ b/pyhmy/blockchain.py @@ -1,12 +1,8 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ############################# @@ -37,12 +33,13 @@ def get_bad_blocks(endpoint=_default_endpoint, timeout=_default_timeout) -> list ------------- https://api.hmny.io/#0ba3c7b6-6aa9-46b8-9c84-f8782e935951 """ - method = 'hmyv2_getCurrentBadBlocks' + method = "hmyv2_getCurrentBadBlocks" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Chain id of the chain @@ -67,13 +64,14 @@ def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: ------------- https://github.com/harmony-one/harmony/blob/343dbe89b3c105f8104ab877769070ba6fdd0133/rpc/blockchain.go#L44 """ - method = 'hmyv2_chainId' + method = "hmyv2_chainId" try: data = rpc_request(method, endpoint=endpoint, timeout=timeout) - return data['result'] + return data["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get config for the node @@ -143,13 +141,14 @@ def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> d https://github.com/harmony-one/harmony/blob/v1.10.2/internal/params/config.go#L233 for chain-config dict https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/node/api.go#L110 for consensus dict """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: metadata = rpc_request(method, endpoint=endpoint, timeout=timeout) - return metadata['result'] + return metadata["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get peer info for the node @@ -181,12 +180,13 @@ def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: -------- get_node_metadata """ - method = 'hmyv2_getPeerInfo' + method = "hmyv2_getPeerInfo" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get the current Harmony protocol version this node supports @@ -212,13 +212,14 @@ def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> in ------------- https://api.hmny.io/#cab9fcc2-e3cd-4bc9-b62a-13e4e046e2fd """ - method = 'hmyv2_protocolVersion' + method = "hmyv2_protocolVersion" try: value = rpc_request(method, endpoint=endpoint, timeout=timeout) - return value['result'] + return value["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get number of peers connected to the node @@ -244,12 +245,15 @@ def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int: ------------- https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1 """ - method = 'net_peerCount' - try: # Number of peers represented as a hex string - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'], 16) + method = "net_peerCount" + try: # Number of peers represented as a hex string + return int( + rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get version of the EVM network (https://chainid.network/) @@ -275,12 +279,15 @@ def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: ------------- https://api.hmny.io/#09287e0b-5b61-4d18-a0f1-3afcfc3369c1 """ - method = 'net_version' + method = "net_version" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result'], 16) # this is hexadecimal + return int( + rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 + ) # this is hexadecimal except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: """ Whether the shard chain is in sync or syncing (not out of sync) @@ -305,12 +312,13 @@ def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L690 """ - method = 'hmyv2_inSync' + method = "hmyv2_inSync" try: - return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: """ Whether the beacon chain is in sync or syncing (not out of sync) @@ -335,12 +343,13 @@ def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L695 """ - method = 'hmyv2_beaconInSync' + method = "hmyv2_beaconInSync" try: - return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get epoch number when blockchain switches to EPoS election @@ -370,13 +379,14 @@ def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i ------ get_node_metadata """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: - data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] - return int(data['chain-config']['staking-epoch']) + data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return int(data["chain-config"]["staking-epoch"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get epoch number when blockchain switches to allow staking features without election @@ -406,13 +416,14 @@ def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) - ------ get_node_metadata """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: - data = rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] - return int(data['chain-config']['prestaking-epoch']) + data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return int(data["chain-config"]["prestaking-epoch"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + ######################## # Sharding information # ######################## @@ -441,13 +452,18 @@ def get_shard(endpoint=_default_endpoint, timeout=_default_timeout) -> int: -------- get_node_metadata """ - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result']['shard-id'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"][ + "shard-id" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_sharding_structure(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_sharding_structure( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get network sharding structure @@ -475,12 +491,13 @@ def get_sharding_structure(endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://api.hmny.io/#9669d49e-43c1-47d9-a3fd-e7786e5879df """ - method = 'hmyv2_getShardingStructure' + method = "hmyv2_getShardingStructure" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ############################# # Current status of network # ############################# @@ -509,13 +526,16 @@ def get_leader_address(endpoint=_default_endpoint, timeout=_default_timeout) -> ------------- https://api.hmny.io/#8b08d18c-017b-4b44-a3c3-356f9c12dacd """ - method = 'hmyv2_getLeader' + method = "hmyv2_getLeader" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def is_last_block(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def is_last_block( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ If the block at block_num is the last block @@ -544,13 +564,20 @@ def is_last_block(block_num, endpoint=_default_endpoint, timeout=_default_timeou params = [ block_num, ] - method = 'hmyv2_isLastBlock' + method = "hmyv2_isLastBlock" try: - return bool(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return bool( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def epoch_last_block(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def epoch_last_block( + epoch, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Returns the number of the last block in the epoch @@ -579,12 +606,17 @@ def epoch_last_block(epoch, endpoint=_default_endpoint, timeout=_default_timeout params = [ epoch, ] - method = 'hmyv2_epochLastBlock' + method = "hmyv2_epochLastBlock" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current circulation supply of tokens in ONE @@ -610,12 +642,13 @@ def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://api.hmny.io/#8398e818-ac2d-4ad8-a3b4-a00927395044 """ - method = 'hmyv2_getCirculatingSupply' + method = "hmyv2_getCirculatingSupply" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get total number of pre-mined tokens @@ -641,12 +674,13 @@ def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> in ------------- https://api.hmny.io/#3dcea518-9e9a-4a20-84f4-c7a0817b2196 """ - method = 'hmyv2_getTotalSupply' + method = "hmyv2_getTotalSupply" try: - rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current block number @@ -672,12 +706,13 @@ def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> in ------------- https://api.hmny.io/#2602b6c4-a579-4b7c-bce8-85331e0db1a7 """ - method = 'hmyv2_blockNumber' + method = "hmyv2_blockNumber" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get current epoch number @@ -703,12 +738,13 @@ def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i ------------- https://api.hmny.io/#9b8e98b0-46d1-4fa0-aaa6-317ff1ddba59 """ - method = 'hmyv2_getEpoch' + method = "hmyv2_getEpoch" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get last cross shard links @@ -741,12 +777,13 @@ def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) - ------------- https://api.hmny.io/#4994cdf9-38c4-4b1d-90a8-290ddaa3040e """ - method = 'hmyv2_getLastCrossLinks' + method = "hmyv2_getLastCrossLinks" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get network gas price @@ -772,12 +809,13 @@ def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int: ------------- https://api.hmny.io/#1d53fd59-a89f-436c-a171-aec9d9623f48 """ - method = 'hmyv2_gasPrice' + method = "hmyv2_gasPrice" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + ############## # Block RPCs # ############## @@ -822,13 +860,16 @@ def get_latest_header(endpoint=_default_endpoint, timeout=_default_timeout) -> d ------------- https://api.hmny.io/#73fc9b97-b048-4b85-8a93-4d2bf1da54a6 """ - method = 'hmyv2_latestHeader' + method = "hmyv2_latestHeader" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_header_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_header_by_number( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get block header of block at block_num @@ -854,16 +895,19 @@ def get_header_by_number(block_num, endpoint=_default_endpoint, timeout=_default ------------- https://api.hmny.io/#01148e4f-72bb-426d-a123-718a161eaec0 """ - method = 'hmyv2_getHeaderByNumber' - params = [ - block_num - ] + method = "hmyv2_getHeaderByNumber" + params = [block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_latest_chain_headers(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_latest_chain_headers( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get block header of latest block for beacon chain & shard chain @@ -908,14 +952,22 @@ def get_latest_chain_headers(endpoint=_default_endpoint, timeout=_default_timeou ------------- https://api.hmny.io/#7625493d-16bf-4611-8009-9635d063b4c0 """ - method = 'hmyv2_getLatestChainHeaders' + method = "hmyv2_getLatestChainHeaders" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_by_number(block_num, full_tx=False, include_tx=False, include_staking_tx=False, - include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_block_by_number( + block_num, + full_tx=False, + include_tx=False, + include_staking_tx=False, + include_signers=False, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> dict: """ Get block by number @@ -979,20 +1031,30 @@ def get_block_by_number(block_num, full_tx=False, include_tx=False, include_stak params = [ block_num, { - 'inclTx': include_tx, - 'fullTx': full_tx, - 'inclStaking': include_staking_tx, - 'withSigners': include_signers, + "inclTx": include_tx, + "fullTx": full_tx, + "inclStaking": include_staking_tx, + "withSigners": include_signers, }, ] - method = 'hmyv2_getBlockByNumber' + method = "hmyv2_getBlockByNumber" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_by_hash(block_hash, full_tx=False, include_tx=False, include_staking_tx=False, - include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_block_by_hash( + block_hash, + full_tx=False, + include_tx=False, + include_staking_tx=False, + include_signers=False, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> dict: """ Get block by hash @@ -1027,19 +1089,24 @@ def get_block_by_hash(block_hash, full_tx=False, include_tx=False, include_staki params = [ block_hash, { - 'inclTx': include_tx, - 'fullTx': full_tx, - 'inclStaking': include_staking_tx, - 'withSigners': include_signers, + "inclTx": include_tx, + "fullTx": full_tx, + "inclStaking": include_staking_tx, + "withSigners": include_signers, }, ] - method = 'hmyv2_getBlockByHash' + method = "hmyv2_getBlockByHash" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_transaction_count_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_transaction_count_by_number( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get transaction count for specific block number @@ -1068,16 +1135,21 @@ def get_block_transaction_count_by_number(block_num, endpoint=_default_endpoint, ------------- https://api.hmny.io/#26c5adfb-d757-4595-9eb7-c6efef63df32 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockTransactionCountByNumber' + params = [block_num] + method = "hmyv2_getBlockTransactionCountByNumber" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_transaction_count_by_hash( + block_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get transaction count for specific block hash @@ -1106,16 +1178,21 @@ def get_block_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, ------------- https://api.hmny.io/#66c68844-0208-49bb-a83b-08722bc113eb """ - params = [ - block_hash - ] - method = 'hmyv2_getBlockTransactionCountByHash' + params = [block_hash] + method = "hmyv2_getBlockTransactionCountByHash" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_staking_transaction_count_by_number(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_staking_transaction_count_by_number( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get staking transaction count for specific block number @@ -1144,16 +1221,21 @@ def get_block_staking_transaction_count_by_number(block_num, endpoint=_default_e ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L494 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockStakingTransactionCountByNumber' + params = [block_num] + method = "hmyv2_getBlockStakingTransactionCountByNumber" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_staking_transaction_count_by_hash(block_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_block_staking_transaction_count_by_hash( + block_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get staking transaction count for specific block hash @@ -1182,18 +1264,28 @@ def get_block_staking_transaction_count_by_hash(block_hash, endpoint=_default_en ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L523 """ - params = [ - block_hash - ] - method = 'hmyv2_getBlockStakingTransactionCountByHash' + params = [block_hash] + method = "hmyv2_getBlockStakingTransactionCountByHash" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_blocks(start_block, end_block, full_tx=False, include_tx=False, include_staking_tx=False, - include_signers=False, endpoint=_default_endpoint, timeout=_default_timeout - ) -> list: + +def get_blocks( + start_block, + end_block, + full_tx=False, + include_tx=False, + include_staking_tx=False, + include_signers=False, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> list: """ Get list of blocks from a range @@ -1233,19 +1325,24 @@ def get_blocks(start_block, end_block, full_tx=False, include_tx=False, include_ start_block, end_block, { - 'withSigners': include_signers, - 'fullTx': full_tx, - 'inclStaking': include_staking_tx, - 'inclTx': include_tx + "withSigners": include_signers, + "fullTx": full_tx, + "inclStaking": include_staking_tx, + "inclTx": include_tx, }, ] - method = 'hmyv2_getBlocks' + method = "hmyv2_getBlocks" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_signers(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_block_signers( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of block signers for specific block number @@ -1272,16 +1369,19 @@ def get_block_signers(block_num, endpoint=_default_endpoint, timeout=_default_ti ------------- https://api.hmny.io/#1e4b5f41-9db6-4dea-92fb-4408db78e622 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockSigners' + params = [block_num] + method = "hmyv2_getBlockSigners" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_block_signers_keys(block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_block_signers_keys( + block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of block signer public bls keys for specific block number @@ -1308,16 +1408,19 @@ def get_block_signers_keys(block_num, endpoint=_default_endpoint, timeout=_defau ------------- https://api.hmny.io/#9f9c8298-1a4e-4901-beac-f34b59ed02f1 """ - params = [ - block_num - ] - method = 'hmyv2_getBlockSignerKeys' + params = [block_num] + method = "hmyv2_getBlockSignerKeys" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def is_block_signer(block_num, address, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def is_block_signer( + block_num, address, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ Determine if the account at address is a signer for the block at block_num @@ -1345,17 +1448,19 @@ def is_block_signer(block_num, address, endpoint=_default_endpoint, timeout=_def ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L368 """ - params = [ - block_num, - address - ] - method = 'hmyv2_isBlockSigner' + params = [block_num, address] + method = "hmyv2_isBlockSigner" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_signed_blocks(address, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def get_signed_blocks( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ The number of blocks a particular validator signed for last blocksPeriod (1 epoch) @@ -1381,15 +1486,18 @@ def get_signed_blocks(address, endpoint=_default_endpoint, timeout=_default_time ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L406 """ - params = [ - address - ] - method = 'hmyv2_getSignedBlocks' + params = [address] + method = "hmyv2_getSignedBlocks" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get list of validators for specific epoch number @@ -1420,16 +1528,19 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://api.hmny.io/#4dfe91ad-71fa-4c7d-83f3-d1c86a804da5 """ - params = [ - epoch - ] - method = 'hmyv2_getValidators' + params = [epoch] + method = "hmyv2_getValidators" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_validator_keys( + epoch, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of validator public bls keys for specific epoch number @@ -1456,11 +1567,11 @@ def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeo ------------- https://api.hmny.io/#1439b580-fa3c-4d44-a79d-303390997a8c """ - params = [ - epoch - ] - method = 'hmyv2_getValidatorKeys' + params = [epoch] + method = "hmyv2_getValidatorKeys" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e diff --git a/pyhmy/cli.py b/pyhmy/cli.py index 060eefa..fd96890 100644 --- a/pyhmy/cli.py +++ b/pyhmy/cli.py @@ -54,9 +54,21 @@ import requests from .util import get_bls_build_variables, get_gopath if sys.platform.startswith("linux"): - _libs = {"libbls384_256.so", "libcrypto.so.10", "libgmp.so.10", "libgmpxx.so.4", "libmcl.so"} + _libs = { + "libbls384_256.so", + "libcrypto.so.10", + "libgmp.so.10", + "libgmpxx.so.4", + "libmcl.so", + } else: - _libs = {"libbls384_256.dylib", "libcrypto.1.0.0.dylib", "libgmp.10.dylib", "libgmpxx.4.dylib", "libmcl.dylib"} + _libs = { + "libbls384_256.dylib", + "libcrypto.1.0.0.dylib", + "libgmp.10.dylib", + "libgmpxx.4.dylib", + "libmcl.dylib", + } _accounts = {} # Internal accounts keystore, make sure to sync when needed. _account_keystore_path = "~/.hmy/account-keys" # Internal path to account keystore, will match the current binary. _binary_path = "hmy" # Internal binary path. @@ -149,14 +161,16 @@ def _make_call_command(command): if isinstance(command, list): command_toks = command else: - all_strings = sorted(re.findall(r'"(.*?)"', command), key=lambda e: len(e), reverse=True) + all_strings = sorted( + re.findall(r'"(.*?)"', command), key=lambda e: len(e), reverse=True + ) for i, string in enumerate(all_strings): command = command.replace(string, f"{_arg_prefix}_{i}") command_toks_prefix = [el for el in command.split(" ") if el] command_toks = [] for el in command_toks_prefix: if el.startswith(f'"{_arg_prefix}_') and el.endswith(f'"'): - index = int(el.replace(f'"{_arg_prefix}_', '').replace('"', '')) + index = int(el.replace(f'"{_arg_prefix}_', "").replace('"', "")) command_toks.append(all_strings[index]) else: command_toks.append(el) @@ -183,8 +197,12 @@ def is_valid_binary(path): path = os.path.realpath(path) os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) try: - proc = subprocess.Popen([path, "version"], env=environment, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + [path, "version"], + env=environment, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out, err = proc.communicate() if not err: return False @@ -223,12 +241,18 @@ def get_version(): """ :return: The version string of the CLI binary. """ - proc = subprocess.Popen([_binary_path, "version"], env=environment, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + proc = subprocess.Popen( + [_binary_path, "version"], + env=environment, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) out, err = proc.communicate() if not err: - raise RuntimeError(f"Could not get version.\n" - f"\tGot exit code {proc.returncode}. Expected non-empty error message.") + raise RuntimeError( + f"Could not get version.\n" + f"\tGot exit code {proc.returncode}. Expected non-empty error message." + ) return err.decode().strip() @@ -280,8 +304,9 @@ def remove_account(name): try: shutil.rmtree(keystore_path) except (shutil.Error, FileNotFoundError) as err: - raise RuntimeError(f"Failed to delete dir: {keystore_path}\n" - f"\tException: {err}") from err + raise RuntimeError( + f"Failed to delete dir: {keystore_path}\n" f"\tException: {err}" + ) from err _sync_accounts() @@ -304,11 +329,14 @@ def single_call(command, timeout=60, error_ok=False): """ command_toks = [_binary_path] + _make_call_command(command) try: - return subprocess.check_output(command_toks, env=environment, timeout=timeout).decode() + return subprocess.check_output( + command_toks, env=environment, timeout=timeout + ).decode() except subprocess.CalledProcessError as err: if not error_ok: - raise RuntimeError(f"Bad CLI args: `{command}`\n " - f"\tException: {err}") from err + raise RuntimeError( + f"Bad CLI args: `{command}`\n " f"\tException: {err}" + ) from err return err.output.decode() @@ -321,11 +349,14 @@ def expect_call(command, timeout=60): """ command_toks = _make_call_command(command) try: - proc = pexpect.spawn(f"{_binary_path}", command_toks, env=environment, timeout=timeout) + proc = pexpect.spawn( + f"{_binary_path}", command_toks, env=environment, timeout=timeout + ) proc.delaybeforesend = None except pexpect.ExceptionPexpect as err: - raise RuntimeError(f"Bad CLI args: `{command}`\n " - f"\tException: {err}") from err + raise RuntimeError( + f"Bad CLI args: `{command}`\n " f"\tException: {err}" + ) from err return proc @@ -341,28 +372,43 @@ def download(path="./bin/hmy", replace=True, verbose=True): """ path = os.path.realpath(path) parent_dir = Path(path).parent - assert not os.path.isdir(path), f"path `{path}` must specify a file, not a directory." + assert not os.path.isdir( + path + ), f"path `{path}` must specify a file, not a directory." if not os.path.exists(path) or replace: old_cwd = os.getcwd() os.makedirs(parent_dir, exist_ok=True) os.chdir(parent_dir) hmy_script_path = os.path.join(parent_dir, "hmy.sh") - with open(hmy_script_path, 'w') as f: - f.write(requests.get("https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh") - .content.decode()) + with open(hmy_script_path, "w") as f: + f.write( + requests.get( + "https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh" + ).content.decode() + ) os.chmod(hmy_script_path, os.stat(hmy_script_path).st_mode | stat.S_IEXEC) same_name_file = False - if os.path.exists(os.path.join(parent_dir, "hmy")) and Path(path).name != "hmy": # Save same name file. + if ( + os.path.exists(os.path.join(parent_dir, "hmy")) and Path(path).name != "hmy" + ): # Save same name file. same_name_file = True - os.rename(os.path.join(parent_dir, "hmy"), os.path.join(parent_dir, ".hmy_tmp")) + os.rename( + os.path.join(parent_dir, "hmy"), os.path.join(parent_dir, ".hmy_tmp") + ) if verbose: - subprocess.call([hmy_script_path, '-d']) + subprocess.call([hmy_script_path, "-d"]) else: - subprocess.call([hmy_script_path, '-d'], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) + subprocess.call( + [hmy_script_path, "-d"], + stdout=open(os.devnull, "w"), + stderr=subprocess.STDOUT, + ) os.rename(os.path.join(parent_dir, "hmy"), path) if same_name_file: - os.rename(os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy")) + os.rename( + os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy") + ) if verbose: print(f"Saved harmony binary to: `{path}`") os.chdir(old_cwd) @@ -373,11 +419,16 @@ def download(path="./bin/hmy", replace=True, verbose=True): files_in_parent_dir = set(os.listdir(parent_dir)) if files_in_parent_dir.intersection(_libs) == _libs: env["DYLD_FALLBACK_LIBRARY_PATH"] = parent_dir - elif os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/bls") \ - and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"): + elif os.path.exists( + f"{get_gopath()}/src/github.com/harmony-one/bls" + ) and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"): env.update(get_bls_build_variables()) else: - raise RuntimeWarning(f"Could not get environment for downloaded hmy CLI at `{path}`") + raise RuntimeWarning( + f"Could not get environment for downloaded hmy CLI at `{path}`" + ) except Exception as e: - raise RuntimeWarning(f"Could not get environment for downloaded hmy CLI at `{path}`") from e + raise RuntimeWarning( + f"Could not get environment for downloaded hmy CLI at `{path}`" + ) from e return env diff --git a/pyhmy/contract.py b/pyhmy/contract.py index 66dee63..abcfa5a 100644 --- a/pyhmy/contract.py +++ b/pyhmy/contract.py @@ -1,25 +1,28 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .transaction import ( - get_transaction_receipt -) +from .transaction import get_transaction_receipt -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ######################### # Smart contract RPCs ######################### -def call(to, block_num, from_address=None, gas=None, gas_price=None, value=None, data=None, - endpoint=_default_endpoint, timeout=_default_timeout) -> str: +def call( + to, + block_num, + from_address=None, + gas=None, + gas_price=None, + value=None, + data=None, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> str: """ Execute a smart contract without saving state @@ -60,23 +63,34 @@ def call(to, block_num, from_address=None, gas=None, gas_price=None, value=None, """ params = [ { - 'to': to, - 'from': from_address, - 'gas': gas, - 'gasPrice': gas_price, - 'value': value, - 'data': data + "to": to, + "from": from_address, + "gas": gas, + "gasPrice": gas_price, + "value": value, + "data": data, }, - block_num + block_num, ] - method = 'hmyv2_call' + method = "hmyv2_call" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def estimate_gas(to, from_address=None, gas=None, gas_price=None, value=None, data=None, - endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def estimate_gas( + to, + from_address=None, + gas=None, + gas_price=None, + value=None, + data=None, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> int: """ Estimate the gas price needed for a smart contract call @@ -113,21 +127,31 @@ def estimate_gas(to, from_address=None, gas=None, gas_price=None, value=None, da ------------- https://api.hmny.io/?version=latest#b9bbfe71-8127-4dda-b26c-ff95c4c22abd """ - params = [ { - 'to': to, - 'from': from_address, - 'gas': gas, - 'gasPrice': gas_price, - 'value': value, - 'data': data - } ] - method = 'hmyv2_estimateGas' + params = [ + { + "to": to, + "from": from_address, + "gas": gas, + "gasPrice": gas_price, + "value": value, + "data": data, + } + ] + method = "hmyv2_estimateGas" try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'], 16) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ], + 16, + ) except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_code(address, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def get_code( + address, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Get the code stored at the given address in the state for the given block number @@ -157,17 +181,19 @@ def get_code(address, block_num, endpoint=_default_endpoint, timeout=_default_ti https://api.hmny.io/?version=latest#e13e9d78-9322-4dc8-8917-f2e721a8e556 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L59 """ - params = [ - address, - block_num - ] - method = 'hmyv2_getCode' + params = [address, block_num] + method = "hmyv2_getCode" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_storage_at(address, key, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def get_storage_at( + address, key, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Get the storage from the state at the given address, the key and the block number @@ -199,18 +225,19 @@ def get_storage_at(address, key, block_num, endpoint=_default_endpoint, timeout= https://api.hmny.io/?version=latest#fa8ac8bd-952d-4149-968c-857ca76da43f https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L84 """ - params = [ - address, - key, - block_num - ] - method = 'hmyv2_getStorageAt' + params = [address, key, block_num] + method = "hmyv2_getStorageAt" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_contract_address_from_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def get_contract_address_from_hash( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Get address of the contract which was deployed in the transaction represented by tx_hash diff --git a/pyhmy/exceptions.py b/pyhmy/exceptions.py index 2238169..91a6882 100644 --- a/pyhmy/exceptions.py +++ b/pyhmy/exceptions.py @@ -1,8 +1,5 @@ -from .rpc.exceptions import ( - RPCError, - RequestsError, - RequestsTimeoutError -) +from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError + class InvalidRPCReplyError(RuntimeError): """ @@ -11,7 +8,8 @@ class InvalidRPCReplyError(RuntimeError): """ def __init__(self, method, endpoint): - super().__init__(f'Unexpected reply for {method} from {endpoint}') + super().__init__(f"Unexpected reply for {method} from {endpoint}") + class InvalidValidatorError(ValueError): """ @@ -19,11 +17,11 @@ class InvalidValidatorError(ValueError): """ errors = { - 1: 'Invalid ONE address', - 2: 'Field not initialized', - 3: 'Invalid field input', - 4: 'Error checking blockchain', - 5: 'Unable to import validator information from blockchain' + 1: "Invalid ONE address", + 2: "Field not initialized", + 3: "Invalid field input", + 4: "Error checking blockchain", + 5: "Unable to import validator information from blockchain", } def __init__(self, err_code, msg): @@ -32,7 +30,8 @@ class InvalidValidatorError(ValueError): super().__init__(msg) def __str__(self): - return f'[Errno {self.code}] {self.errors[self.code]}: {self.msg}' + return f"[Errno {self.code}] {self.errors[self.code]}: {self.msg}" + class TxConfirmationTimedoutError(AssertionError): """ @@ -41,4 +40,4 @@ class TxConfirmationTimedoutError(AssertionError): """ def __init__(self, msg): - super().__init__(f'{msg}') + super().__init__(f"{msg}") diff --git a/pyhmy/logging.py b/pyhmy/logging.py index 4b4787a..498cf70 100644 --- a/pyhmy/logging.py +++ b/pyhmy/logging.py @@ -9,8 +9,8 @@ import logging.handlers class _GZipRotator: def __call__(self, source, dest): os.rename(source, dest) - f_in = open(dest, 'rb') - f_out = gzip.open("%s.gz" % dest, 'wb') + f_in = open(dest, "rb") + f_out = gzip.open("%s.gz" % dest, "wb") f_out.writelines(f_in) f_out.close() f_in.close() @@ -27,13 +27,14 @@ class ControlledLogger: :param logger_name: The name of the logger and logfile :param log_dir: The directory in which to save this log file (can be abs or relative). """ - if log_dir.endswith('/'): + if log_dir.endswith("/"): log_dir = log_dir[:-1] log_dir = os.path.realpath(log_dir) os.makedirs(log_dir, exist_ok=True) - handler = logging.handlers.TimedRotatingFileHandler(f"{log_dir}/{logger_name}.log", 'midnight', 1, - backupCount=backup_count) - handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) + handler = logging.handlers.TimedRotatingFileHandler( + f"{log_dir}/{logger_name}.log", "midnight", 1, backupCount=backup_count + ) + handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) handler.rotator = _GZipRotator() self.filename = handler.baseFilename @@ -63,8 +64,9 @@ class ControlledLogger: :param msg: The info message to log """ self._lock.acquire() - self.info_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.info_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def debug(self, msg): @@ -72,8 +74,9 @@ class ControlledLogger: :param msg: The debug message to log """ self._lock.acquire() - self.debug_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.debug_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def warning(self, msg): @@ -81,8 +84,9 @@ class ControlledLogger: :param msg: The warning message to log """ self._lock.acquire() - self.warning_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.warning_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def error(self, msg): @@ -90,33 +94,34 @@ class ControlledLogger: :param msg: The error message to log """ self._lock.acquire() - self.error_buffer.append(f"[{threading.get_ident()}] " - f"{datetime.datetime.utcnow()} : {msg}") + self.error_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) self._lock.release() def print_info(self): """ Prints the current info buffer but does not flush it to log file. """ - print('\n'.join(self.info_buffer)) + print("\n".join(self.info_buffer)) def print_debug(self): """ Prints the current debug buffer but does not flush it to log file. """ - print('\n'.join(self.debug_buffer)) + print("\n".join(self.debug_buffer)) def print_warning(self): """ Prints the current warning buffer but does not flush it to log file. """ - print('\n'.join(self.warning_buffer)) + print("\n".join(self.warning_buffer)) def print_error(self): """ Prints the current error buffer but does not flush it to log file. """ - print('\n'.join(self.error_buffer)) + print("\n".join(self.error_buffer)) def write(self): """ diff --git a/pyhmy/rpc/exceptions.py b/pyhmy/rpc/exceptions.py index a14dfc6..a6ad3ac 100644 --- a/pyhmy/rpc/exceptions.py +++ b/pyhmy/rpc/exceptions.py @@ -8,7 +8,8 @@ class RPCError(RuntimeError): def __init__(self, method, endpoint, error): self.error = error - super().__init__(f'Error in reply from {endpoint}: {method} returned {error}') + super().__init__(f"Error in reply from {endpoint}: {method} returned {error}") + class RequestsError(requests.exceptions.RequestException): """ @@ -16,7 +17,8 @@ class RequestsError(requests.exceptions.RequestException): """ def __init__(self, endpoint): - super().__init__(f'Error connecting to {endpoint}') + super().__init__(f"Error connecting to {endpoint}") + class RequestsTimeoutError(requests.exceptions.Timeout): """ @@ -24,4 +26,4 @@ class RequestsTimeoutError(requests.exceptions.Timeout): """ def __init__(self, endpoint): - super().__init__(f'Error connecting to {endpoint}') + super().__init__(f"Error connecting to {endpoint}") diff --git a/pyhmy/rpc/request.py b/pyhmy/rpc/request.py index 81f32b0..be1c4e8 100644 --- a/pyhmy/rpc/request.py +++ b/pyhmy/rpc/request.py @@ -2,18 +2,16 @@ import json import requests -from .exceptions import ( - RequestsError, - RequestsTimeoutError, - RPCError -) +from .exceptions import RequestsError, RequestsTimeoutError, RPCError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 -def base_request(method, params=None, endpoint=_default_endpoint, timeout=_default_timeout) -> str: +def base_request( + method, params=None, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Basic RPC request @@ -45,21 +43,20 @@ def base_request(method, params=None, endpoint=_default_endpoint, timeout=_defau if params is None: params = [] elif not isinstance(params, list): - raise TypeError(f'invalid type {params.__class__}') + raise TypeError(f"invalid type {params.__class__}") try: - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": method, - "params": params - } - headers = { - 'Content-Type': 'application/json' - } - - resp = requests.request('POST', endpoint, headers=headers, data=json.dumps(payload), - timeout=timeout, allow_redirects=True) + payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} + headers = {"Content-Type": "application/json"} + + resp = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) return resp.content except requests.exceptions.Timeout as err: raise RequestsTimeoutError(endpoint) from err @@ -67,7 +64,9 @@ def base_request(method, params=None, endpoint=_default_endpoint, timeout=_defau raise RequestsError(endpoint) from err -def rpc_request(method, params=None, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def rpc_request( + method, params=None, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ RPC request @@ -106,8 +105,8 @@ def rpc_request(method, params=None, endpoint=_default_endpoint, timeout=_defaul try: resp = json.loads(raw_resp) - if 'error' in resp: - raise RPCError(method, endpoint, str(resp['error'])) + if "error" in resp: + raise RPCError(method, endpoint, str(resp["error"])) return resp except json.decoder.JSONDecodeError as err: raise RPCError(method, endpoint, raw_resp) from err diff --git a/pyhmy/signing.py b/pyhmy/signing.py index 9482ecf..4fddde8 100644 --- a/pyhmy/signing.py +++ b/pyhmy/signing.py @@ -1,33 +1,16 @@ import rlp -from eth_utils.curried import ( - keccak, - to_int, - hexstr_if_str, - apply_formatters_to_dict -) +from eth_utils.curried import keccak, to_int, hexstr_if_str, apply_formatters_to_dict -from rlp.sedes import ( - big_endian_int, - Binary, - binary -) +from rlp.sedes import big_endian_int, Binary, binary -from eth_account import ( - Account -) +from eth_account import Account -from eth_rlp import ( - HashableRLP -) +from eth_rlp import HashableRLP -from hexbytes import ( - HexBytes -) +from hexbytes import HexBytes -from eth_account._utils.signing import ( - sign_transaction_hash -) +from eth_account._utils.signing import sign_transaction_hash from eth_account._utils.legacy_transactions import ( Transaction as SignedEthereumTxData, @@ -35,96 +18,101 @@ from eth_account._utils.legacy_transactions import ( LEGACY_TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS, TRANSACTION_DEFAULTS, chain_id_to_v, - UNSIGNED_TRANSACTION_FIELDS + UNSIGNED_TRANSACTION_FIELDS, ) -from cytoolz import ( - dissoc, - pipe, - merge, - partial -) +from cytoolz import dissoc, pipe, merge, partial -from eth_account.datastructures import ( - SignedTransaction -) +from eth_account.datastructures import SignedTransaction -from .util import ( - chain_id_to_int, - convert_one_to_hex -) +from .util import chain_id_to_int, convert_one_to_hex HARMONY_FORMATTERS = dict( ETHEREUM_FORMATTERS, - shardID=hexstr_if_str(to_int), # additional fields for Harmony transaction - toShardID=hexstr_if_str(to_int), # which may be cross shard - ) + shardID=hexstr_if_str(to_int), # additional fields for Harmony transaction + toShardID=hexstr_if_str(to_int), # which may be cross shard +) + class UnsignedHarmonyTxData(HashableRLP): fields = ( - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gas', big_endian_int), - ('shardID', big_endian_int), - ('toShardID', big_endian_int), - ('to', Binary.fixed_length(20, allow_empty=True)), - ('value', big_endian_int), - ('data', binary), + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gas", big_endian_int), + ("shardID", big_endian_int), + ("toShardID", big_endian_int), + ("to", Binary.fixed_length(20, allow_empty=True)), + ("value", big_endian_int), + ("data", binary), ) + class SignedHarmonyTxData(HashableRLP): fields = UnsignedHarmonyTxData._meta.fields + ( - ("v", big_endian_int), # Recovery value + 27 - ("r", big_endian_int), # First 32 bytes - ("s", big_endian_int), # Next 32 bytes + ("v", big_endian_int), # Recovery value + 27 + ("r", big_endian_int), # First 32 bytes + ("s", big_endian_int), # Next 32 bytes ) -def encode_transaction(unsigned_transaction, vrs): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 - '''serialize and encode an unsigned transaction with v,r,s''' + +def encode_transaction( + unsigned_transaction, vrs +): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 + """serialize and encode an unsigned transaction with v,r,s""" (v, r, s) = vrs - chain_naive_transaction = dissoc( - unsigned_transaction.as_dict(), 'v', 'r', 's') - if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, - SignedHarmonyTxData)): + chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), "v", "r", "s") + if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, SignedHarmonyTxData)): serializer = SignedHarmonyTxData else: serializer = SignedEthereumTxData signed_transaction = serializer(v=v, r=r, s=s, **chain_naive_transaction) return rlp.encode(signed_transaction) + def serialize_transaction(filled_transaction): - '''serialize a signed/unsigned transaction''' - if 'v' in filled_transaction: - if 'shardID' in filled_transaction: + """serialize a signed/unsigned transaction""" + if "v" in filled_transaction: + if "shardID" in filled_transaction: serializer = SignedHarmonyTxData else: serializer = SignedEthereumTxData else: - if 'shardID' in filled_transaction: + if "shardID" in filled_transaction: serializer = UnsignedHarmonyTxData else: serializer = UnsignedEthereumTxData for f, _ in serializer._meta.fields: - assert f in filled_transaction, f'Could not find {f} in transaction' - return serializer.from_dict({f: filled_transaction[f] for f, _ in serializer._meta.fields}) + assert f in filled_transaction, f"Could not find {f} in transaction" + return serializer.from_dict( + {f: filled_transaction[f] for f, _ in serializer._meta.fields} + ) + def sanitize_transaction(transaction_dict, private_key): - '''remove the originating address from the dict and convert chainId to int''' - account = Account.from_key(private_key) # get account, from which you can derive public + private key - transaction_dict = transaction_dict.copy() # do not alter the original dictionary - if 'from' in transaction_dict: - transaction_dict[ 'from' ] = convert_one_to_hex( transaction_dict[ 'from' ] ) - if transaction_dict[ 'from' ] == account.address: # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 - sanitized_transaction = dissoc(transaction_dict, 'from') + """remove the originating address from the dict and convert chainId to int""" + account = Account.from_key( + private_key + ) # get account, from which you can derive public + private key + transaction_dict = transaction_dict.copy() # do not alter the original dictionary + if "from" in transaction_dict: + transaction_dict["from"] = convert_one_to_hex(transaction_dict["from"]) + if ( + transaction_dict["from"] == account.address + ): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 + sanitized_transaction = dissoc(transaction_dict, "from") else: - raise TypeError("from field must match key's %s, but it was %s" % ( + raise TypeError( + "from field must match key's %s, but it was %s" + % ( account.address, - transaction_dict['from'], - )) - if 'chainId' in transaction_dict: - transaction_dict[ 'chainId' ] = chain_id_to_int( transaction_dict[ 'chainId' ] ) + transaction_dict["from"], + ) + ) + if "chainId" in transaction_dict: + transaction_dict["chainId"] = chain_id_to_int(transaction_dict["chainId"]) return account, transaction_dict + def sign_transaction(transaction_dict, private_key) -> SignedTransaction: """ Sign a (non-staking) transaction dictionary with the specified private key @@ -171,24 +159,25 @@ def sign_transaction(transaction_dict, private_key) -> SignedTransaction: https://readthedocs.org/projects/eth-account/downloads/pdf/stable/ """ account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) - if 'to' in sanitized_transaction and sanitized_transaction[ 'to' ] is not None: - sanitized_transaction[ 'to' ] = convert_one_to_hex( sanitized_transaction[ 'to' ] ) - filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + if "to" in sanitized_transaction and sanitized_transaction["to"] is not None: + sanitized_transaction["to"] = convert_one_to_hex(sanitized_transaction["to"]) + filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 sanitized_transaction, dict, partial(merge, TRANSACTION_DEFAULTS), chain_id_to_v, - apply_formatters_to_dict(HARMONY_FORMATTERS) + apply_formatters_to_dict(HARMONY_FORMATTERS), ) unsigned_transaction = serialize_transaction(filled_transaction) transaction_hash = unsigned_transaction.hash() - if isinstance(unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData)): - chain_id = None # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 + if isinstance( + unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData) + ): + chain_id = None # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 else: chain_id = unsigned_transaction.v - (v, r, s) = sign_transaction_hash( - account._key_obj, transaction_hash, chain_id) + (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s)) signed_transaction_hash = keccak(encoded_transaction) return SignedTransaction( diff --git a/pyhmy/staking.py b/pyhmy/staking.py index 312f9d5..a70174a 100644 --- a/pyhmy/staking.py +++ b/pyhmy/staking.py @@ -1,18 +1,16 @@ -from .rpc.request import ( - rpc_request -) +from .rpc.request import rpc_request -from .exceptions import ( - InvalidRPCReplyError -) +from .exceptions import InvalidRPCReplyError -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ################## # Validator RPCs # ################## -def get_all_validator_addresses(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_all_validator_addresses( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of all created validator addresses on chain @@ -37,13 +35,16 @@ def get_all_validator_addresses(endpoint=_default_endpoint, timeout=_default_tim ------------- https://api.hmny.io/#69b93657-8d3c-4d20-9c9f-e51f08c9b3f5 """ - method = 'hmyv2_getAllValidatorAddresses' + method = "hmyv2_getAllValidatorAddresses" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_information(validator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_validator_information( + validator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get validator information for validator address @@ -112,16 +113,19 @@ def get_validator_information(validator_addr, endpoint=_default_endpoint, timeou ------------- https://api.hmny.io/#659ad999-14ca-4498-8f74-08ed347cab49 """ - method = 'hmyv2_getValidatorInformation' - params = [ - validator_addr - ] + method = "hmyv2_getValidatorInformation" + params = [validator_addr] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_elected_validator_addresses(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_elected_validator_addresses( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of elected validator addresses @@ -147,12 +151,13 @@ def get_elected_validator_addresses(endpoint=_default_endpoint, timeout=_default ------------- https://api.hmny.io/#e90a6131-d67c-4110-96ef-b283d452632d """ - method = 'hmyv2_getElectedValidatorAddresses' + method = "hmyv2_getElectedValidatorAddresses" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: """ Get validators list for a particular epoch @@ -183,16 +188,19 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152 """ - method = 'hmyv2_getValidators' - params = [ - epoch - ] + method = "hmyv2_getValidators" + params = [epoch] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_validator_keys( + epoch, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get validator BLS keys in the committee for a particular epoch @@ -218,16 +226,19 @@ def get_validator_keys(epoch, endpoint=_default_endpoint, timeout=_default_timeo ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152 """ - method = 'hmyv2_getValidatorKeys' - params = [ - epoch - ] + method = "hmyv2_getValidatorKeys" + params = [epoch] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_information_by_block_number(validator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout): + +def get_validator_information_by_block_number( + validator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout +): """ Get validator information for validator address at a block @@ -255,17 +266,19 @@ def get_validator_information_by_block_number(validator_addr, block_num, endpoin ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L319 """ - method = 'hmyv2_getValidatorInformationByBlockNumber' - params = [ - validator_addr, - block_num - ] + method = "hmyv2_getValidatorInformationByBlockNumber" + params = [validator_addr, block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_all_validator_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_all_validator_information( + page=0, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get validator information for all validators on chain @@ -291,16 +304,19 @@ def get_all_validator_information(page=0, endpoint=_default_endpoint, timeout=_d ------------- https://api.hmny.io/#df5f1631-7397-48e8-87b4-8dd873235b9c """ - method = 'hmyv2_getAllValidatorInformation' - params = [ - page - ] + method = "hmyv2_getAllValidatorInformation" + params = [page] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_self_delegation(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_validator_self_delegation( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the amount self delegated by validator @@ -326,16 +342,21 @@ def get_validator_self_delegation(address, endpoint=_default_endpoint, timeout=_ ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L352 """ - method = 'hmyv2_getValidatorSelfDelegation' - params = [ - address - ] + method = "hmyv2_getValidatorSelfDelegation" + params = [address] try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_validator_total_delegation(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_validator_total_delegation( + address, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get the total amount delegated t ovalidator (including self delegated) @@ -361,16 +382,21 @@ def get_validator_total_delegation(address, endpoint=_default_endpoint, timeout= ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L379 """ - method = 'hmyv2_getValidatorTotalDelegation' - params = [ - address - ] + method = "hmyv2_getValidatorTotalDelegation" + params = [address] try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_all_validator_information_by_block_number(block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_all_validator_information_by_block_number( + block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get validator information at block number for all validators on chain @@ -399,20 +425,22 @@ def get_all_validator_information_by_block_number(block_num, page=0, endpoint=_d ------------- https://api.hmny.io/#a229253f-ca76-4b9d-88f5-9fd96e40d583 """ - method = 'hmyv2_getAllValidatorInformationByBlockNumber' - params = [ - page, - block_num - ] + method = "hmyv2_getAllValidatorInformationByBlockNumber" + params = [page, block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ################### # Delegation RPCs # ################### -def get_all_delegation_information(page=0, endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_all_delegation_information( + page=0, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get delegation information for all delegators on chain @@ -440,16 +468,21 @@ def get_all_delegation_information(page=0, endpoint=_default_endpoint, timeout=_ ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L413 """ - method = 'hmyv2_getAllDelegationInformation' + method = "hmyv2_getAllDelegationInformation" params = [ page, ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegations_by_delegator(delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_delegations_by_delegator( + delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of delegations by a delegator @@ -482,16 +515,19 @@ def get_delegations_by_delegator(delegator_addr, endpoint=_default_endpoint, tim ------------- https://api.hmny.io/#454b032c-6072-4ecb-bf24-38b3d6d2af69 """ - method = 'hmyv2_getDelegationsByDelegator' - params = [ - delegator_addr - ] + method = "hmyv2_getDelegationsByDelegator" + params = [delegator_addr] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegations_by_delegator_by_block_number(delegator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_delegations_by_delegator_by_block_number( + delegator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of delegations by a delegator at a specific block @@ -519,18 +555,22 @@ def get_delegations_by_delegator_by_block_number(delegator_addr, block_num, endp ------------- https://api.hmny.io/#8ce13bda-e768-47b9-9dbe-193aba410b0a """ - method = 'hmyv2_getDelegationsByDelegatorByBlockNumber' - params = [ - delegator_addr, - block_num - ] + method = "hmyv2_getDelegationsByDelegatorByBlockNumber" + params = [delegator_addr, block_num] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegation_by_delegator_and_validator(delegator_addr, validator_address, - endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_delegation_by_delegator_and_validator( + delegator_addr, + validator_address, + endpoint=_default_endpoint, + timeout=_default_timeout, +) -> dict: """ Get list of delegations by a delegator at a specific block @@ -558,17 +598,19 @@ def get_delegation_by_delegator_and_validator(delegator_addr, validator_address, ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L605 """ - method = 'hmyv2_getDelegationByDelegatorAndValidator' - params = [ - delegator_addr, - validator_address - ] + method = "hmyv2_getDelegationByDelegatorAndValidator" + params = [delegator_addr, validator_address] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_available_redelegation_balance(delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> int: + +def get_available_redelegation_balance( + delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> int: """ Get amount of locked undelegated tokens @@ -594,16 +636,21 @@ def get_available_redelegation_balance(delegator_addr, endpoint=_default_endpoin ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L653 """ - method = 'hmyv2_getAvailableRedelegationBalance' - params = [ - delegator_addr - ] + method = "hmyv2_getAvailableRedelegationBalance" + params = [delegator_addr] try: - return int(rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result']) + return int( + rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] + ) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_delegations_by_validator(validator_addr, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_delegations_by_validator( + validator_addr, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of delegations to a validator @@ -629,19 +676,22 @@ def get_delegations_by_validator(validator_addr, endpoint=_default_endpoint, tim ------------- https://api.hmny.io/#2e02d8db-8fec-41d9-a672-2c9862f63f39 """ - method = 'hmyv2_getDelegationsByValidator' - params = [ - validator_addr - ] + method = "hmyv2_getDelegationsByValidator" + params = [validator_addr] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ######################## # Staking Network RPCs # ######################## -def get_current_utility_metrics(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def get_current_utility_metrics( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get current utility metrics of network @@ -669,13 +719,16 @@ def get_current_utility_metrics(endpoint=_default_endpoint, timeout=_default_tim ------------- https://api.hmny.io/#78dd2d94-9ff1-4e0c-bbac-b4eec1cdf10b """ - method = 'hmyv2_getCurrentUtilityMetrics' + method = "hmyv2_getCurrentUtilityMetrics" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_network_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_staking_network_info( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking network information @@ -704,12 +757,13 @@ def get_staking_network_info(endpoint=_default_endpoint, timeout=_default_timeou ------------- https://api.hmny.io/#4a10fce0-2aa4-4583-bdcb-81ee0800993b """ - method = 'hmyv2_getStakingNetworkInfo' + method = "hmyv2_getStakingNetworkInfo" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get voting committees for current & previous epoch @@ -757,12 +811,13 @@ def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) - ------------- https://api.hmny.io/#8eef2fc4-92db-4610-a9cd-f7b75cfbd080 """ - method = 'hmyv2_getSuperCommittees' + method = "hmyv2_getSuperCommittees" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> int: """ Get total staking by validators, only meant to be called on beaconchain @@ -787,13 +842,16 @@ def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> i ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L102 """ - method = 'hmyv2_getTotalStaking' + method = "hmyv2_getTotalStaking" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)['result']) + return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) except (KeyError, TypeError) as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_raw_median_stake_snapshot(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_raw_median_stake_snapshot( + endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get median stake & additional committee data of the current epoch @@ -830,8 +888,8 @@ def get_raw_median_stake_snapshot(endpoint=_default_endpoint, timeout=_default_t ------------- https://api.hmny.io/#bef93b3f-6763-4121-9c17-f0b0d9e5cc40 """ - method = 'hmyv2_getMedianRawStakeSnapshot' + method = "hmyv2_getMedianRawStakeSnapshot" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index afa5b52..03c96c6 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -6,29 +6,19 @@ from cytoolz import ( identity, ) -from hexbytes import ( - HexBytes -) +from hexbytes import HexBytes import rlp import math -from decimal import ( - Decimal -) +from decimal import Decimal -from eth_account.datastructures import ( - SignedTransaction -) +from eth_account.datastructures import SignedTransaction -from eth_account._utils.signing import ( - sign_transaction_hash -) +from eth_account._utils.signing import sign_transaction_hash -from eth_account._utils.legacy_transactions import ( - chain_id_to_v -) +from eth_account._utils.legacy_transactions import chain_id_to_v from eth_utils.curried import ( hexstr_if_str, @@ -37,12 +27,10 @@ from eth_utils.curried import ( apply_formatters_to_dict, to_int, apply_formatters_to_sequence, - apply_formatter_to_array + apply_formatter_to_array, ) -from .signing import ( - sanitize_transaction -) +from .signing import sanitize_transaction from .staking_structures import ( FORMATTERS, @@ -51,14 +39,15 @@ from .staking_structures import ( CreateValidator, EditValidator, DelegateOrUndelegate, - CollectRewards + CollectRewards, ) -from .util import ( - convert_one_to_hex -) +from .util import convert_one_to_hex + -def _convert_staking_percentage_to_number(value): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 +def _convert_staking_percentage_to_number( + value, +): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 """ Convert from staking percentage to integer For example, 0.1 becomes 1000000000000000000 @@ -77,38 +66,43 @@ def _convert_staking_percentage_to_number(value): # https://github.com/ha AssertionError, if data types are not as expected ValueError, if the input type is not supported """ - assert isinstance(value, (str, Decimal)), 'Only strings or decimals are supported' + assert isinstance(value, (str, Decimal)), "Only strings or decimals are supported" if isinstance(value, Decimal): value = str(value) - value1 = value; - if value[0] == '-': - raise ValueError('Negative numbers are not accepted') - if value[0] == '+': + value1 = value + if value[0] == "-": + raise ValueError("Negative numbers are not accepted") + if value[0] == "+": value1 = value[1:] if len(value1) == 0: - raise ValueError('StakingDecimal string is empty') - spaced = value1.split(' ') + raise ValueError("StakingDecimal string is empty") + spaced = value1.split(" ") if len(spaced) > 1: - raise ValueError('Bad decimal string') - splitted = value1.split('.') + raise ValueError("Bad decimal string") + splitted = value1.split(".") combined_str = splitted[0] if len(splitted) == 2: length = len(splitted[1]) if length == 0 or len(combined_str) == 0: - raise ValueError('Bad StakingDecimal length') - if splitted[1][0] == '-': - raise ValueError('Bad StakingDecimal string') + raise ValueError("Bad StakingDecimal length") + if splitted[1][0] == "-": + raise ValueError("Bad StakingDecimal string") combined_str += splitted[1] elif len(splitted) > 2: - raise ValueError('Too many periods to be a StakingDecimal string') + raise ValueError("Too many periods to be a StakingDecimal string") if length > StakingSettings.PRECISION: - raise ValueError('Too much precision, must be less than {StakingSettings.PRECISION}') + raise ValueError( + "Too much precision, must be less than {StakingSettings.PRECISION}" + ) zeroes_to_add = StakingSettings.PRECISION - length - combined_str += '0' * zeroes_to_add # This will not have any periods, so it is effectively a large integer + combined_str += ( + "0" * zeroes_to_add + ) # This will not have any periods, so it is effectively a large integer val = int(combined_str) - assert val <= StakingSettings.MAX_DECIMAL, 'Staking percentage is too large' + assert val <= StakingSettings.MAX_DECIMAL, "Staking percentage is too large" return val + def _get_account_and_transaction(transaction_dict, private_key): """ Create account from private key and sanitize the transaction @@ -132,10 +126,15 @@ def _get_account_and_transaction(transaction_dict, private_key): AssertionError, if chainId is not present in util.chain_id_to_int TypeError, if the value of 'from' key is not the same as account address """ - account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) # remove from, convert chain id (if present) to integer - sanitized_transaction['directive'] = sanitized_transaction['directive'].value # convert to value, like in TypeScript + account, sanitized_transaction = sanitize_transaction( + transaction_dict, private_key + ) # remove from, convert chain id (if present) to integer + sanitized_transaction["directive"] = sanitized_transaction[ + "directive" + ].value # convert to value, like in TypeScript return account, sanitized_transaction + def _sign_transaction_generic(account, sanitized_transaction, parent_serializer): """ Sign a generic staking transaction, given the serializer base class and account @@ -157,39 +156,54 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) rlp.exceptions.ObjectSerializationError, if data types are not as expected """ # obtain the serializers - if sanitized_transaction.get('chainId', 0) == 0: - unsigned_serializer, signed_serializer = parent_serializer.Unsigned(), parent_serializer.Signed() # unsigned, signed + if sanitized_transaction.get("chainId", 0) == 0: + unsigned_serializer, signed_serializer = ( + parent_serializer.Unsigned(), + parent_serializer.Signed(), + ) # unsigned, signed else: - unsigned_serializer, signed_serializer = parent_serializer.SignedChainId(), parent_serializer.SignedChainId() # since chain_id_to_v adds v/r/s, unsigned is not used here + unsigned_serializer, signed_serializer = ( + parent_serializer.SignedChainId(), + parent_serializer.SignedChainId(), + ) # since chain_id_to_v adds v/r/s, unsigned is not used here # fill the transaction - filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 sanitized_transaction, dict, - partial(merge, {'chainId': None}), - chain_id_to_v, # will move chain id to v and add v/r/s - apply_formatters_to_dict(FORMATTERS) + partial(merge, {"chainId": None}), + chain_id_to_v, # will move chain id to v and add v/r/s + apply_formatters_to_dict(FORMATTERS), ) # get the unsigned transaction for f, _ in unsigned_serializer._meta.fields: - assert f in filled_transaction, f'Could not find {f} in transaction' - unsigned_transaction = unsigned_serializer.from_dict(\ - {f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields}) # drop extras silently + assert f in filled_transaction, f"Could not find {f} in transaction" + unsigned_transaction = unsigned_serializer.from_dict( + {f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields} + ) # drop extras silently # sign the unsigned transaction - if 'v' in unsigned_transaction.as_dict(): + if "v" in unsigned_transaction.as_dict(): chain_id = unsigned_transaction.v else: chain_id = None transaction_hash = unsigned_transaction.hash() - (v, r, s) = sign_transaction_hash( - account._key_obj, transaction_hash, chain_id) + (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) chain_naive_transaction = dissoc( - unsigned_transaction.as_dict(), 'v', 'r', 's') # remove extra v/r/s added by chain_id_to_v + unsigned_transaction.as_dict(), "v", "r", "s" + ) # remove extra v/r/s added by chain_id_to_v # serialize it signed_transaction = signed_serializer( - v=v + (8 if chain_id is None else 0), # copied from https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 + v=v + + ( + 8 if chain_id is None else 0 + ), # copied from https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 r=r, - s=s, # in the below statement, remove everything not expected by signed_serializer - **{f: chain_naive_transaction[f] for f, _ in signed_serializer._meta.fields if f not in 'vrs'}) + s=s, # in the below statement, remove everything not expected by signed_serializer + **{ + f: chain_naive_transaction[f] + for f, _ in signed_serializer._meta.fields + if f not in "vrs" + }, + ) # encode it encoded_transaction = rlp.encode(signed_transaction) # hash it @@ -203,29 +217,34 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) v=v, ) + def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate): """ Sign a delegate or undelegate transaction See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] not in [ Directive.Delegate, Directive.Undelegate ]: - raise TypeError('Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate') + if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]: + raise TypeError( + "Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate" + ) # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg - sanitized_transaction['stakeMsg'] = \ - apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), - hexstr_if_str(to_bytes), - hexstr_if_str(to_int) - ], [ - convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')), - convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), - sanitized_transaction.pop('amount'), - ] - ) - return _sign_transaction_generic(account, sanitized_transaction, DelegateOrUndelegate) + sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + [hexstr_if_str(to_bytes), hexstr_if_str(to_bytes), hexstr_if_str(to_int)], + [ + convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")), + convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), + sanitized_transaction.pop("amount"), + ], + ) + return _sign_transaction_generic( + account, sanitized_transaction, DelegateOrUndelegate + ) + def _sign_collect_rewards(transaction_dict, private_key): """ @@ -233,109 +252,147 @@ def _sign_collect_rewards(transaction_dict, private_key): See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] != Directive.CollectRewards: - raise TypeError('Only CollectRewards is supported by _sign_collect_rewards') + if transaction_dict["directive"] != Directive.CollectRewards: + raise TypeError("Only CollectRewards is supported by _sign_collect_rewards") # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg - sanitized_transaction['stakeMsg'] = \ - [hexstr_if_str(to_bytes)(convert_one_to_hex(sanitized_transaction.pop('delegatorAddress')))] + sanitized_transaction["stakeMsg"] = [ + hexstr_if_str(to_bytes)( + convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")) + ) + ] return _sign_transaction_generic(account, sanitized_transaction, CollectRewards) + def _sign_create_validator(transaction_dict, private_key): """ Sign a create validator transaction See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] != Directive.CreateValidator: - raise TypeError('Only CreateValidator is supported by _sign_create_or_edit_validator') + if transaction_dict["directive"] != Directive.CreateValidator: + raise TypeError( + "Only CreateValidator is supported by _sign_create_or_edit_validator" + ) # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg description = [ - sanitized_transaction.pop('name'), - sanitized_transaction.pop('identity'), - sanitized_transaction.pop('website'), - sanitized_transaction.pop('security-contact'), - sanitized_transaction.pop('details'), - ] - commission = apply_formatter_to_array( hexstr_if_str(to_int), # formatter + sanitized_transaction.pop("name"), + sanitized_transaction.pop("identity"), + sanitized_transaction.pop("website"), + sanitized_transaction.pop("security-contact"), + sanitized_transaction.pop("details"), + ] + commission = apply_formatter_to_array( + hexstr_if_str(to_int), # formatter [ - _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')), - _convert_staking_percentage_to_number(sanitized_transaction.pop('max-rate')), - _convert_staking_percentage_to_number(sanitized_transaction.pop('max-change-rate')), - ] + _convert_staking_percentage_to_number(sanitized_transaction.pop("rate")), + _convert_staking_percentage_to_number( + sanitized_transaction.pop("max-rate") + ), + _convert_staking_percentage_to_number( + sanitized_transaction.pop("max-change-rate") + ), + ], ) - commission = [ [element] for element in commission ] - bls_keys = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter - sanitized_transaction.pop('bls-public-keys') + commission = [[element] for element in commission] + bls_keys = apply_formatter_to_array( + hexstr_if_str(to_bytes), # formatter + sanitized_transaction.pop("bls-public-keys"), ) - bls_key_sigs = apply_formatter_to_array( hexstr_if_str(to_bytes), # formatter - sanitized_transaction.pop('bls-key-sigs') + bls_key_sigs = apply_formatter_to_array( + hexstr_if_str(to_bytes), sanitized_transaction.pop("bls-key-sigs") # formatter ) - sanitized_transaction['stakeMsg'] = \ - apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), # address - identity, # description - identity, # commission rates - hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped - hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped - identity, # bls public keys - identity, # bls key sigs - hexstr_if_str(to_int), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) - ], [ - convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), + sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + [ + hexstr_if_str(to_bytes), # address + identity, # description + identity, # commission rates + hexstr_if_str( + to_int + ), # min self delegation (in ONE), decimals are silently dropped + hexstr_if_str( + to_int + ), # max total delegation (in ONE), decimals are silently dropped + identity, # bls public keys + identity, # bls key sigs + hexstr_if_str( + to_int + ), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) + ], + [ + convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), description, commission, - math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly - math.floor(sanitized_transaction.pop('max-total-delegation')), + math.floor( + sanitized_transaction.pop("min-self-delegation") + ), # Decimal floors it correctly + math.floor(sanitized_transaction.pop("max-total-delegation")), bls_keys, bls_key_sigs, - math.floor(sanitized_transaction.pop('amount')), - ] - ) + math.floor(sanitized_transaction.pop("amount")), + ], + ) return _sign_transaction_generic(account, sanitized_transaction, CreateValidator) + def _sign_edit_validator(transaction_dict, private_key): """ Sign an edit validator transaction See sign_staking_transaction for details """ # preliminary steps - if transaction_dict['directive'] != Directive.EditValidator: - raise TypeError('Only EditValidator is supported by _sign_create_or_edit_validator') + if transaction_dict["directive"] != Directive.EditValidator: + raise TypeError( + "Only EditValidator is supported by _sign_create_or_edit_validator" + ) # first common step - account, sanitized_transaction = _get_account_and_transaction(transaction_dict, private_key) + account, sanitized_transaction = _get_account_and_transaction( + transaction_dict, private_key + ) # encode the stakeMsg description = [ - sanitized_transaction.pop('name'), - sanitized_transaction.pop('identity'), - sanitized_transaction.pop('website'), - sanitized_transaction.pop('security-contact'), - sanitized_transaction.pop('details'), - ] - sanitized_transaction['stakeMsg'] = \ - apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), # address - identity, # description - identity, # new rate (it's in a list so can't do hexstr_if_str) - hexstr_if_str(to_int), # min self delegation (in ONE), decimals are silently dropped - hexstr_if_str(to_int), # max total delegation (in ONE), decimals are silently dropped - hexstr_if_str(to_bytes), # key to remove - hexstr_if_str(to_bytes), # key to add - ], [ - convert_one_to_hex(sanitized_transaction.pop('validatorAddress')), + sanitized_transaction.pop("name"), + sanitized_transaction.pop("identity"), + sanitized_transaction.pop("website"), + sanitized_transaction.pop("security-contact"), + sanitized_transaction.pop("details"), + ] + sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + [ + hexstr_if_str(to_bytes), # address + identity, # description + identity, # new rate (it's in a list so can't do hexstr_if_str) + hexstr_if_str( + to_int + ), # min self delegation (in ONE), decimals are silently dropped + hexstr_if_str( + to_int + ), # max total delegation (in ONE), decimals are silently dropped + hexstr_if_str(to_bytes), # key to remove + hexstr_if_str(to_bytes), # key to add + ], + [ + convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), description, - [ _convert_staking_percentage_to_number(sanitized_transaction.pop('rate')) ], - math.floor(sanitized_transaction.pop('min-self-delegation')), # Decimal floors it correctly - math.floor(sanitized_transaction.pop('max-total-delegation')), - sanitized_transaction.pop('bls-key-to-remove'), - sanitized_transaction.pop('bls-key-to-add') - ] - ) + [_convert_staking_percentage_to_number(sanitized_transaction.pop("rate"))], + math.floor( + sanitized_transaction.pop("min-self-delegation") + ), # Decimal floors it correctly + math.floor(sanitized_transaction.pop("max-total-delegation")), + sanitized_transaction.pop("bls-key-to-remove"), + sanitized_transaction.pop("bls-key-to-add"), + ], + ) return _sign_transaction_generic(account, sanitized_transaction, EditValidator) + def sign_staking_transaction(transaction_dict, private_key): """ Sign a supplied transaction_dict with the private_key @@ -404,18 +461,22 @@ def sign_staking_transaction(transaction_dict, private_key): ------------- https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts """ - assert isinstance(transaction_dict, dict), 'Only dictionaries are supported' # OrderedDict is a subclass + assert isinstance( + transaction_dict, dict + ), "Only dictionaries are supported" # OrderedDict is a subclass # chain_id missing => 'rlp: input string too long for uint64, decoding into (types.StakingTransaction)(types.txdata).GasLimit' - assert 'chainId' in transaction_dict, 'chainId missing' - assert 'directive' in transaction_dict, 'Staking transaction type not specified' - assert isinstance(transaction_dict['directive'], Directive), 'Unknown staking transaction type' - if transaction_dict['directive'] == Directive.CollectRewards: + assert "chainId" in transaction_dict, "chainId missing" + assert "directive" in transaction_dict, "Staking transaction type not specified" + assert isinstance( + transaction_dict["directive"], Directive + ), "Unknown staking transaction type" + if transaction_dict["directive"] == Directive.CollectRewards: return _sign_collect_rewards(transaction_dict, private_key) - elif transaction_dict['directive'] == Directive.Delegate: + elif transaction_dict["directive"] == Directive.Delegate: return _sign_delegate_or_undelegate(transaction_dict, private_key, True) - elif transaction_dict['directive'] == Directive.Undelegate: + elif transaction_dict["directive"] == Directive.Undelegate: return _sign_delegate_or_undelegate(transaction_dict, private_key, False) - elif transaction_dict['directive'] == Directive.CreateValidator: + elif transaction_dict["directive"] == Directive.CreateValidator: return _sign_create_validator(transaction_dict, private_key) - elif transaction_dict['directive'] == Directive.EditValidator: + elif transaction_dict["directive"] == Directive.EditValidator: return _sign_edit_validator(transaction_dict, private_key) diff --git a/pyhmy/staking_structures.py b/pyhmy/staking_structures.py index d8907f3..44f7102 100644 --- a/pyhmy/staking_structures.py +++ b/pyhmy/staking_structures.py @@ -1,219 +1,294 @@ -from enum import ( - Enum, - auto -) +from enum import Enum, auto -from rlp.sedes import ( - big_endian_int, - Binary, - CountableList, - List, - Text -) +from rlp.sedes import big_endian_int, Binary, CountableList, List, Text -from eth_rlp import ( - HashableRLP -) +from eth_rlp import HashableRLP from eth_utils.curried import ( to_int, hexstr_if_str, ) + class StakingSettings: PRECISION = 18 MAX_DECIMAL = 1000000000000000000 -class Directive(Enum): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 + +class Directive( + Enum +): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 def _generate_next_value_(name, start, count, last_values): return count + CreateValidator = auto() EditValidator = auto() Delegate = auto() Undelegate = auto() CollectRewards = auto() + FORMATTERS = { - 'directive': hexstr_if_str(to_int), # delegatorAddress is already formatted before the call - 'nonce': hexstr_if_str(to_int), - 'gasPrice': hexstr_if_str(to_int), - 'gasLimit': hexstr_if_str(to_int), - 'chainId': hexstr_if_str(to_int), + "directive": hexstr_if_str( + to_int + ), # delegatorAddress is already formatted before the call + "nonce": hexstr_if_str(to_int), + "gasPrice": hexstr_if_str(to_int), + "gasLimit": hexstr_if_str(to_int), + "chainId": hexstr_if_str(to_int), } + class CollectRewards: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', CountableList(Binary.fixed_length(20, allow_empty=True))), - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ("stakeMsg", CountableList(Binary.fixed_length(20, allow_empty=True))), + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = CollectRewards.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = CollectRewards.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = CollectRewards.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed + class DelegateOrUndelegate: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', List([Binary.fixed_length(20, allow_empty=True),Binary.fixed_length(20, allow_empty=True),big_endian_int],True)), - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ( + "stakeMsg", + List( + [ + Binary.fixed_length(20, allow_empty=True), + Binary.fixed_length(20, allow_empty=True), + big_endian_int, + ], + True, + ), + ), + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): - fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] # drop chainId + fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ + :-1 + ] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = DelegateOrUndelegate.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = DelegateOrUndelegate.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed + class CreateValidator: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', List([ # list with the following members - Binary.fixed_length(20, allow_empty=True), # validatorAddress - List([Text()]*5,True), # description is Text of 5 elements - List([List([big_endian_int],True)]*3,True), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] - big_endian_int, # min self delegation - big_endian_int, # max total delegation - CountableList(Binary.fixed_length(48, allow_empty=True)), # bls-public-keys array of unspecified length, each key of 48 - CountableList(Binary.fixed_length(96, allow_empty=True)), # bls-key-sigs array of unspecified length, each sig of 96 - big_endian_int, # amount - ], True)), # strictly these number of elements - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ( + "stakeMsg", + List( + [ # list with the following members + Binary.fixed_length( + 20, allow_empty=True + ), # validatorAddress + List( + [Text()] * 5, True + ), # description is Text of 5 elements + List( + [List([big_endian_int], True)] * 3, True + ), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] + big_endian_int, # min self delegation + big_endian_int, # max total delegation + CountableList( + Binary.fixed_length(48, allow_empty=True) + ), # bls-public-keys array of unspecified length, each key of 48 + CountableList( + Binary.fixed_length(96, allow_empty=True) + ), # bls-key-sigs array of unspecified length, each sig of 96 + big_endian_int, # amount + ], + True, + ), + ), # strictly these number of elements + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = CreateValidator.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = CreateValidator.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = CreateValidator.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed + class EditValidator: @staticmethod def UnsignedChainId(): class UnsignedChainId(HashableRLP): fields = ( - ('directive', big_endian_int), - ('stakeMsg', List([ # list with the following members - Binary.fixed_length(20, allow_empty=True), # validatorAddress - List([Text()]*5,True), # description is Text of 5 elements - List([big_endian_int],True), # new rate is in a list - big_endian_int, # min self delegation - big_endian_int, # max total delegation - Binary.fixed_length(48, allow_empty=True), # slot key to remove - Binary.fixed_length(48, allow_empty=True), # slot key to add - ], True)), # strictly these number of elements - ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gasLimit', big_endian_int), - ('chainId', big_endian_int), + ("directive", big_endian_int), + ( + "stakeMsg", + List( + [ # list with the following members + Binary.fixed_length( + 20, allow_empty=True + ), # validatorAddress + List( + [Text()] * 5, True + ), # description is Text of 5 elements + List([big_endian_int], True), # new rate is in a list + big_endian_int, # min self delegation + big_endian_int, # max total delegation + Binary.fixed_length( + 48, allow_empty=True + ), # slot key to remove + Binary.fixed_length( + 48, allow_empty=True + ), # slot key to add + ], + True, + ), + ), # strictly these number of elements + ("nonce", big_endian_int), + ("gasPrice", big_endian_int), + ("gasLimit", big_endian_int), + ("chainId", big_endian_int), ) + return UnsignedChainId @staticmethod def SignedChainId(): class SignedChainId(HashableRLP): - fields = EditValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId + fields = EditValidator.UnsignedChainId()._meta.fields[ + :-1 + ] + ( # drop chainId ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return SignedChainId @staticmethod def Unsigned(): class Unsigned(HashableRLP): fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId + return Unsigned @staticmethod def Signed(): class Signed(HashableRLP): - fields = EditValidator.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() + fields = EditValidator.Unsigned()._meta.fields[ + :-3 + ] + ( # drop last 3 for raw.pop() ("v", big_endian_int), ("r", big_endian_int), ("s", big_endian_int), ) + return Signed diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index 2e34914..b1c890c 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -1,21 +1,18 @@ -from .rpc.request import ( - rpc_request -) -from .exceptions import ( - TxConfirmationTimedoutError, - InvalidRPCReplyError -) +from .rpc.request import rpc_request +from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError import time import random -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 ######################### # Transaction Pool RPCs # ######################### -def get_pending_transactions(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_pending_transactions( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of pending transactions @@ -39,13 +36,16 @@ def get_pending_transactions(endpoint=_default_endpoint, timeout=_default_timeou ------------- https://api.hmny.io/#de6c4a12-fa42-44e8-972f-801bfde1dd18 """ - method = 'hmyv2_pendingTransactions' + method = "hmyv2_pendingTransactions" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_transaction_error_sink( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get current transactions error sink @@ -72,13 +72,16 @@ def get_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_time ------------- https://api.hmny.io/#9aedbc22-6262-44b1-8276-cd8ae19fa600 """ - method = 'hmyv2_getCurrentTransactionErrorSink' + method = "hmyv2_getCurrentTransactionErrorSink" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_pending_staking_transactions(endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def get_pending_staking_transactions( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of pending staking transactions @@ -102,14 +105,16 @@ def get_pending_staking_transactions(endpoint=_default_endpoint, timeout=_defaul ------------- https://api.hmny.io/#de0235e4-f4c9-4a69-b6d2-b77dc1ba7b12 """ - method = 'hmyv2_pendingStakingTransactions' + method = "hmyv2_pendingStakingTransactions" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_error_sink(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_staking_transaction_error_sink( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get current staking transactions error sink @@ -137,12 +142,13 @@ def get_staking_transaction_error_sink(endpoint=_default_endpoint, timeout=_defa ------------- https://api.hmny.io/#bdd00e0f-2ba0-480e-b996-2ef13f10d75a """ - method = 'hmyv2_getCurrentStakingErrorSink' + method = "hmyv2_getCurrentStakingErrorSink" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: """ Get stats of the pool, that is, number of pending and queued (non-executable) transactions @@ -169,16 +175,19 @@ def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict ------------- https://api.hmny.io/#7c2b9395-8f5e-4eb5-a687-2f1be683d83e """ - method = 'hmyv2_getPoolStats' + method = "hmyv2_getPoolStats" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + #################### # Transaction RPCs # #################### -def get_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def get_transaction_by_hash( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction by hash @@ -224,18 +233,19 @@ def get_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_defaul ------------- https://api.hmny.io/#117e84f6-a0ec-444e-abe0-455701310389 """ - method = 'hmyv2_getTransactionByHash' - params = [ - tx_hash - ] + method = "hmyv2_getTransactionByHash" + params = [tx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_by_block_hash_and_index(block_hash, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_transaction_by_block_hash_and_index( + block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction based on index in list of transactions in a block by block hash @@ -263,19 +273,19 @@ def get_transaction_by_block_hash_and_index(block_hash, tx_index, ------------- https://api.hmny.io/#7c7e8d90-4984-4ebe-bb7e-d7adec167503 """ - method = 'hmyv2_getTransactionByBlockHashAndIndex' - params = [ - block_hash, - tx_index - ] + method = "hmyv2_getTransactionByBlockHashAndIndex" + params = [block_hash, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_by_block_number_and_index(block_num, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_transaction_by_block_number_and_index( + block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction based on index in list of transactions in a block by block number @@ -303,17 +313,19 @@ def get_transaction_by_block_number_and_index(block_num, tx_index, ------------- https://api.hmny.io/#bcde8b1c-6ab9-4950-9835-3c7564e49c3e """ - method = 'hmyv2_getTransactionByBlockNumberAndIndex' - params = [ - block_num, - tx_index - ] + method = "hmyv2_getTransactionByBlockNumberAndIndex" + params = [block_num, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_transaction_receipt(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: + +def get_transaction_receipt( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get transaction receipt corresponding to tx_hash @@ -353,16 +365,19 @@ def get_transaction_receipt(tx_hash, endpoint=_default_endpoint, timeout=_defaul ------------- https://api.hmny.io/#0c2799f8-bcdc-41a4-b362-c3a6a763bb5e """ - method = 'hmyv2_getTransactionReceipt' - params = [ - tx_hash - ] + method = "hmyv2_getTransactionReceipt" + params = [tx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def send_raw_transaction( + signed_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Send signed transaction @@ -391,16 +406,19 @@ def send_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default ------------- https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0 """ - params = [ - signed_tx - ] - method = 'hmyv2_sendRawTransaction' + params = [signed_tx] + method = "hmyv2_sendRawTransaction" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def send_and_confirm_raw_transaction( + signed_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Send signed transaction and wait for it to be confirmed @@ -433,20 +451,23 @@ def send_and_confirm_raw_transaction(signed_tx, endpoint=_default_endpoint, time """ tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint) start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint) if tx_response is not None: - block_hash = tx_response.get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_response.get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": return tx_response time.sleep(random.uniform(0.2, 0.5)) raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") + ############################### # CrossShard Transaction RPCs # ############################### -def get_pending_cx_receipts(endpoint=_default_endpoint, timeout=_default_timeout) -> list: +def get_pending_cx_receipts( + endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Get list of pending cross shard transactions @@ -493,13 +514,16 @@ def get_pending_cx_receipts(endpoint=_default_endpoint, timeout=_default_timeout ------------- https://api.hmny.io/#fe60070d-97b4-458d-9365-490b44c18851 """ - method = 'hmyv2_getPendingCXReceipts' + method = "hmyv2_getPendingCXReceipts" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_cx_receipt_by_hash(cx_hash, endpoint = _default_endpoint, timeout = _default_timeout) -> dict: + +def get_cx_receipt_by_hash( + cx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get cross shard receipt by hash on the receiving shard end point @@ -534,16 +558,19 @@ def get_cx_receipt_by_hash(cx_hash, endpoint = _default_endpoint, timeout = _def ------------- https://api.hmny.io/#3d6ad045-800d-4021-aeb5-30a0fbf724fe """ - params = [ - cx_hash - ] - method = 'hmyv2_getCXReceiptByHash' + params = [cx_hash] + method = "hmyv2_getCXReceiptByHash" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def resend_cx_receipt(cx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + +def resend_cx_receipt( + cx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> bool: """ Resend the cross shard receipt to the receiving shard to re-process if the transaction did not pay out @@ -570,19 +597,22 @@ def resend_cx_receipt(cx_hash, endpoint=_default_endpoint, timeout=_default_time ------------- https://api.hmny.io/#c658b56b-d20b-480d-b71a-b0bc505d2164 """ - method = 'hmyv2_resendCx' - params = [ - cx_hash - ] + method = "hmyv2_resendCx" + params = [cx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e + ############################ # Staking Transaction RPCs # ############################ -def get_staking_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: +def get_staking_transaction_by_hash( + tx_hash, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking transaction by hash @@ -622,18 +652,19 @@ def get_staking_transaction_by_hash(tx_hash, endpoint=_default_endpoint, timeout ------------- https://api.hmny.io/#296cb4d0-bce2-48e3-bab9-64c3734edd27 """ - method = 'hmyv2_getStakingTransactionByHash' - params = [ - tx_hash - ] + method = "hmyv2_getStakingTransactionByHash" + params = [tx_hash] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_by_block_hash_and_index(block_hash, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_staking_transaction_by_block_hash_and_index( + block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking transaction by block hash and transaction index @@ -661,19 +692,19 @@ def get_staking_transaction_by_block_hash_and_index(block_hash, tx_index, ------------- https://api.hmny.io/#ba96cf61-61fe-464a-aa06-2803bb4b358f """ - method = 'hmyv2_getStakingTransactionByBlockHashAndIndex' - params = [ - block_hash, - tx_index - ] + method = "hmyv2_getStakingTransactionByBlockHashAndIndex" + params = [block_hash, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def get_staking_transaction_by_block_number_and_index(block_num, tx_index, - endpoint=_default_endpoint, timeout=_default_timeout - ) -> dict: + +def get_staking_transaction_by_block_number_and_index( + block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout +) -> dict: """ Get staking transaction by block number and transaction index @@ -701,17 +732,19 @@ def get_staking_transaction_by_block_number_and_index(block_num, tx_index, ------------- https://api.hmny.io/#fb41d717-1645-4d3e-8071-6ce8e1b65dd3 """ - method = 'hmyv2_getStakingTransactionByBlockNumberAndIndex' - params = [ - block_num, - tx_index - ] + method = "hmyv2_getStakingTransactionByBlockNumberAndIndex" + params = [block_num, tx_index] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_raw_staking_transaction(raw_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> str: + +def send_raw_staking_transaction( + raw_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> str: """ Send signed staking transaction @@ -740,16 +773,19 @@ def send_raw_staking_transaction(raw_tx, endpoint=_default_endpoint, timeout=_de ------------- https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401 """ - method = 'hmyv2_sendRawStakingTransaction' - params = [ - raw_tx - ] + method = "hmyv2_sendRawStakingTransaction" + params = [raw_tx] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)['result'] + return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ + "result" + ] except KeyError as e: raise InvalidRPCReplyError(method, endpoint) from e -def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoint, timeout=_default_timeout) -> list: + +def send_and_confirm_raw_staking_transaction( + signed_tx, endpoint=_default_endpoint, timeout=_default_timeout +) -> list: """ Send signed staking transaction and wait for it to be confirmed @@ -782,12 +818,12 @@ def send_and_confirm_raw_staking_transaction(signed_tx, endpoint=_default_endpoi """ tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint) start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint) if tx_response is not None: - block_hash = tx_response.get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_response.get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": return tx_response time.sleep(random.uniform(0.2, 0.5)) - raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") \ No newline at end of file + raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") diff --git a/pyhmy/util.py b/pyhmy/util.py index 36c350f..d287821 100644 --- a/pyhmy/util.py +++ b/pyhmy/util.py @@ -6,9 +6,7 @@ import datetime import requests -from .blockchain import ( - get_latest_header -) +from .blockchain import get_latest_header from .rpc.exceptions import ( RPCError, @@ -16,63 +14,63 @@ from .rpc.exceptions import ( RequestsTimeoutError, ) -from .account import ( - is_valid_address -) +from .account import is_valid_address -from .bech32.bech32 import ( - bech32_decode, - bech32_encode, - convertbits -) +from .bech32.bech32 import bech32_decode, bech32_encode, convertbits from eth_utils import to_checksum_address datetime_format = "%Y-%m-%d %H:%M:%S.%f" + class Typgpy(str): """ Typography constants for pretty printing. Note that an ENDC is needed to mark the end of a 'highlighted' text segment. """ - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - BOLD = '\033[1m' - UNDERLINE = '\033[4m' + + HEADER = "\033[95m" + OKBLUE = "\033[94m" + OKGREEN = "\033[92m" + WARNING = "\033[93m" + FAIL = "\033[91m" + ENDC = "\033[0m" + BOLD = "\033[1m" + UNDERLINE = "\033[4m" + def chain_id_to_int(chainId): chainIds = dict( - Default = 0, - EthMainnet = 1, - Morden = 2, - Ropsten = 3, - Rinkeby = 4, - RootstockMainnet = 30, - RootstockTestnet = 31, - Kovan = 42, - EtcMainnet = 61, - EtcTestnet = 62, - Geth = 1337, - Ganache = 0, - HmyMainnet = 1, - HmyTestnet = 2, - HmyLocal = 2, - HmyPangaea = 3, + Default=0, + EthMainnet=1, + Morden=2, + Ropsten=3, + Rinkeby=4, + RootstockMainnet=30, + RootstockTestnet=31, + Kovan=42, + EtcMainnet=61, + EtcTestnet=62, + Geth=1337, + Ganache=0, + HmyMainnet=1, + HmyTestnet=2, + HmyLocal=2, + HmyPangaea=3, ) # do not validate integer chainids, only known strings if isinstance(chainId, str): - assert chainId in chainIds, f'Chain {chainId} unknown, specify an integer chainId' + assert ( + chainId in chainIds + ), f"Chain {chainId} unknown, specify an integer chainId" return chainIds.get(chainId) elif isinstance(chainId, int): return chainId else: - raise TypeError( 'chainId must be str or int' ) + raise TypeError("chainId must be str or int") + def get_gopath(): """ @@ -87,6 +85,7 @@ def get_goversion(): """ return subprocess.check_output(["go", "version"]).decode().strip() + def convert_one_to_hex(addr): """ Given a one address, convert it to hex checksum address @@ -95,9 +94,10 @@ def convert_one_to_hex(addr): return to_checksum_address(addr) hrp, data = bech32_decode(addr) buf = convertbits(data, 5, 8, False) - address = '0x' + ''.join('{:02x}'.format(x) for x in buf) + address = "0x" + "".join("{:02x}".format(x) for x in buf) return to_checksum_address(address) + def convert_hex_to_one(addr): """ Given a hex address, convert it to a one address @@ -105,10 +105,13 @@ def convert_hex_to_one(addr): if is_valid_address(addr): return addr checksum_addr = to_checksum_address(addr) - data = bytearray.fromhex(checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr) + data = bytearray.fromhex( + checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr + ) buf = convertbits(data, 8, 5) return bech32_encode("one", buf) + def is_active_shard(endpoint, delay_tolerance=60): """ :param endpoint: The endpoint of the SHARD to check @@ -118,8 +121,10 @@ def is_active_shard(endpoint, delay_tolerance=60): try: curr_time = datetime.datetime.utcnow() latest_header = get_latest_header(endpoint=endpoint) - time_str = latest_header["timestamp"][:19] + '.0' # Fit time format - timestamp = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S.%f").replace(tzinfo=None) + time_str = latest_header["timestamp"][:19] + ".0" # Fit time format + timestamp = datetime.datetime.strptime( + time_str, "%Y-%m-%d %H:%M:%S.%f" + ).replace(tzinfo=None) time_delta = curr_time - timestamp return abs(time_delta.seconds) < delay_tolerance except (RPCError, RequestsError, RequestsTimeoutError): @@ -137,7 +142,12 @@ def get_bls_build_variables(): """ variables = {} try: - openssl_dir = subprocess.check_output(["which", "openssl"]).decode().strip().split("\n")[0] + openssl_dir = ( + subprocess.check_output(["which", "openssl"]) + .decode() + .strip() + .split("\n")[0] + ) except (IndexError, subprocess.CalledProcessError) as e: raise RuntimeError("`openssl` not found") from e hmy_path = f"{get_gopath()}/src/github.com/harmony-one" @@ -146,7 +156,9 @@ def get_bls_build_variables(): assert os.path.exists(bls_dir), f"Harmony BLS repo not found at {bls_dir}" assert os.path.exists(mcl_dir), f"Harmony MCL repo not found at {mcl_dir}" if sys.platform.startswith("darwin"): - variables["CGO_CFLAGS"] = f"-I{bls_dir}/include -I{mcl_dir}/include -I{openssl_dir}/include" + variables[ + "CGO_CFLAGS" + ] = f"-I{bls_dir}/include -I{mcl_dir}/include -I{openssl_dir}/include" variables["CGO_LDFLAGS"] = f"-L{bls_dir}/lib -L{openssl_dir}/lib" variables["LD_LIBRARY_PATH"] = f"{bls_dir}/lib:{mcl_dir}/lib:{openssl_dir}/lib" variables["DYLD_FALLBACK_LIBRARY_PATH"] = variables["LD_LIBRARY_PATH"] diff --git a/pyhmy/validator.py b/pyhmy/validator.py index ccc2b14..c2178fc 100644 --- a/pyhmy/validator.py +++ b/pyhmy/validator.py @@ -1,44 +1,27 @@ import json -from eth_account.datastructures import ( - SignedTransaction -) +from eth_account.datastructures import SignedTransaction -from decimal import ( - Decimal, - InvalidOperation -) +from decimal import Decimal, InvalidOperation -from .account import ( - get_balance, - is_valid_address -) +from .account import get_balance, is_valid_address -from .numbers import ( - convert_one_to_atto -) +from .numbers import convert_one_to_atto from .exceptions import ( InvalidValidatorError, RPCError, RequestsError, - RequestsTimeoutError + RequestsTimeoutError, ) -from .staking import ( - get_all_validator_addresses, - get_validator_information -) +from .staking import get_all_validator_addresses, get_validator_information -from .staking_structures import ( - Directive -) +from .staking_structures import Directive -from .staking_signing import ( - sign_staking_transaction -) +from .staking_signing import sign_staking_transaction -_default_endpoint = 'http://localhost:9500' +_default_endpoint = "http://localhost:9500" _default_timeout = 30 # TODO: Add unit testing @@ -49,13 +32,13 @@ class Validator: website_char_limit = 140 security_contact_char_limit = 140 details_char_limit = 280 - min_required_delegation = convert_one_to_atto(10000) # in ATTO + min_required_delegation = convert_one_to_atto(10000) # in ATTO def __init__(self, address): if not isinstance(address, str): - raise InvalidValidatorError(1, 'given ONE address was not a string') + raise InvalidValidatorError(1, "given ONE address was not a string") if not is_valid_address(address): - raise InvalidValidatorError(1, f'{address} is not valid ONE address') + raise InvalidValidatorError(1, f"{address} is not valid ONE address") self._address = address self._bls_keys = [] self._bls_key_sigs = [] @@ -84,8 +67,11 @@ class Validator: """ if check_str: if not isinstance(data, str): - raise InvalidValidatorError(3, f'Expected data to be string to avoid floating point precision issues but got {data}') - return '' if not data else str(data) + raise InvalidValidatorError( + 3, + f"Expected data to be string to avoid floating point precision issues but got {data}", + ) + return "" if not data else str(data) def __str__(self) -> str: """ @@ -98,7 +84,7 @@ class Validator: return json.dumps(info) def __repr__(self) -> str: - return f'' + return f"" def get_address(self) -> str: """ @@ -209,7 +195,9 @@ class Validator: """ name = self._sanitize_input(name) if len(name) > self.name_char_limit: - raise InvalidValidatorError(3, f'Name must be less than {self.name_char_limit} characters') + raise InvalidValidatorError( + 3, f"Name must be less than {self.name_char_limit} characters" + ) self._name = name def get_name(self) -> str: @@ -239,7 +227,9 @@ class Validator: """ identity = self._sanitize_input(identity) if len(identity) > self.identity_char_limit: - raise InvalidValidatorError(3, f'Identity must be less than {self.identity_char_limit} characters') + raise InvalidValidatorError( + 3, f"Identity must be less than {self.identity_char_limit} characters" + ) self._identity = identity def get_identity(self) -> str: @@ -269,7 +259,9 @@ class Validator: """ website = self._sanitize_input(website) if len(website) > self.website_char_limit: - raise InvalidValidatorError(3, f'Website must be less than {self.website_char_limit} characters') + raise InvalidValidatorError( + 3, f"Website must be less than {self.website_char_limit} characters" + ) self._website = website def get_website(self) -> str: @@ -299,7 +291,10 @@ class Validator: """ contact = self._sanitize_input(contact) if len(contact) > self.security_contact_char_limit: - raise InvalidValidatorError(3, f'Security contact must be less than {self.security_contact_char_limit} characters') + raise InvalidValidatorError( + 3, + f"Security contact must be less than {self.security_contact_char_limit} characters", + ) self._security_contact = contact def get_security_contact(self) -> str: @@ -329,7 +324,9 @@ class Validator: """ details = self._sanitize_input(details) if len(details) > self.details_char_limit: - raise InvalidValidatorError(3, f'Details must be less than {self.details_char_limit} characters') + raise InvalidValidatorError( + 3, f"Details must be less than {self.details_char_limit} characters" + ) self._details = details def get_details(self) -> str: @@ -361,9 +358,14 @@ class Validator: try: delegation = Decimal(delegation) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Min self delegation must be a number') from e + raise InvalidValidatorError( + 3, "Min self delegation must be a number" + ) from e if delegation < self.min_required_delegation: - raise InvalidValidatorError(3, f'Min self delegation must be greater than {self.min_required_delegation} ATTO') + raise InvalidValidatorError( + 3, + f"Min self delegation must be greater than {self.min_required_delegation} ATTO", + ) self._min_self_delegation = delegation def get_min_self_delegation(self) -> Decimal: @@ -395,13 +397,20 @@ class Validator: try: max_delegation = Decimal(max_delegation) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Max total delegation must be a number') from e + raise InvalidValidatorError( + 3, "Max total delegation must be a number" + ) from e if self._min_self_delegation: if max_delegation < self._min_self_delegation: - raise InvalidValidatorError(3, f'Max total delegation must be greater than min self delegation: ' - '{self._min_self_delegation}') + raise InvalidValidatorError( + 3, + f"Max total delegation must be greater than min self delegation: " + "{self._min_self_delegation}", + ) else: - raise InvalidValidatorError(4, 'Min self delegation must be set before max total delegation') + raise InvalidValidatorError( + 4, "Min self delegation must be set before max total delegation" + ) self._max_total_delegation = max_delegation def get_max_total_delegation(self) -> Decimal: @@ -433,19 +442,29 @@ class Validator: try: amount = Decimal(amount) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Amount must be a number') from e + raise InvalidValidatorError(3, "Amount must be a number") from e if self._min_self_delegation: if amount < self._min_self_delegation: - raise InvalidValidatorError(3, 'Amount must be greater than min self delegation: ' - f'{self._min_self_delegation}') + raise InvalidValidatorError( + 3, + "Amount must be greater than min self delegation: " + f"{self._min_self_delegation}", + ) else: - raise InvalidValidatorError(4, 'Min self delegation must be set before amount') + raise InvalidValidatorError( + 4, "Min self delegation must be set before amount" + ) if self._max_total_delegation: if amount > self._max_total_delegation: - raise InvalidValidatorError(3, 'Amount must be less than max total delegation: ' - f'{self._max_total_delegation}') + raise InvalidValidatorError( + 3, + "Amount must be less than max total delegation: " + f"{self._max_total_delegation}", + ) else: - raise InvalidValidatorError(4, 'Max total delegation must be set before amount') + raise InvalidValidatorError( + 4, "Max total delegation must be set before amount" + ) self._inital_delegation = amount def get_amount(self) -> Decimal: @@ -477,9 +496,9 @@ class Validator: try: rate = Decimal(rate) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Max rate must be a number') from e + raise InvalidValidatorError(3, "Max rate must be a number") from e if rate < 0 or rate > 1: - raise InvalidValidatorError(3, 'Max rate must be between 0 and 1') + raise InvalidValidatorError(3, "Max rate must be between 0 and 1") self._max_rate = rate def get_max_rate(self) -> Decimal: @@ -511,14 +530,21 @@ class Validator: try: rate = Decimal(rate) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Max change rate must be a number') from e + raise InvalidValidatorError(3, "Max change rate must be a number") from e if rate < 0: - raise InvalidValidatorError(3, 'Max change rate must be greater than or equal to 0') + raise InvalidValidatorError( + 3, "Max change rate must be greater than or equal to 0" + ) if self._max_rate: if rate > self._max_rate: - raise InvalidValidatorError(3, f'Max change rate must be less than or equal to max rate: {self._max_rate}') + raise InvalidValidatorError( + 3, + f"Max change rate must be less than or equal to max rate: {self._max_rate}", + ) else: - raise InvalidValidatorError(4, 'Max rate must be set before max change rate') + raise InvalidValidatorError( + 4, "Max rate must be set before max change rate" + ) self._max_change_rate = rate def get_max_change_rate(self) -> Decimal: @@ -550,14 +576,16 @@ class Validator: try: rate = Decimal(rate) except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, 'Rate must be a number') from e + raise InvalidValidatorError(3, "Rate must be a number") from e if rate < 0: - raise InvalidValidatorError(3, 'Rate must be greater than or equal to 0') + raise InvalidValidatorError(3, "Rate must be greater than or equal to 0") if self._max_rate: if rate > self._max_rate: - raise InvalidValidatorError(3, f'Rate must be less than or equal to max rate: {self._max_rate}') + raise InvalidValidatorError( + 3, f"Rate must be less than or equal to max rate: {self._max_rate}" + ) else: - raise InvalidValidatorError(4, 'Max rate must be set before rate') + raise InvalidValidatorError(4, "Max rate must be set before rate") self._rate = rate def get_rate(self) -> Decimal: @@ -571,7 +599,9 @@ class Validator: """ return self._rate - def does_validator_exist(self, endpoint=_default_endpoint, timeout=_default_timeout) -> bool: + def does_validator_exist( + self, endpoint=_default_endpoint, timeout=_default_timeout + ) -> bool: """ Check if validator exists on blockchain @@ -629,31 +659,33 @@ class Validator: If input value is invalid """ try: - self.set_name(info['name']) - self.set_identity(info['identity']) - self.set_website(info['website']) - self.set_details(info['details']) - self.set_security_contact(info['security-contact']) + self.set_name(info["name"]) + self.set_identity(info["identity"]) + self.set_website(info["website"]) + self.set_details(info["details"]) + self.set_security_contact(info["security-contact"]) - self.set_min_self_delegation(info['min-self-delegation']) - self.set_max_total_delegation(info['max-total-delegation']) - self.set_amount(info['amount']) + self.set_min_self_delegation(info["min-self-delegation"]) + self.set_max_total_delegation(info["max-total-delegation"]) + self.set_amount(info["amount"]) - self.set_max_rate(info['max-rate']) - self.set_max_change_rate(info['max-change-rate']) - self.set_rate(info['rate']) + self.set_max_rate(info["max-rate"]) + self.set_max_change_rate(info["max-change-rate"]) + self.set_rate(info["rate"]) self._bls_keys = [] - for key in info['bls-public-keys']: + for key in info["bls-public-keys"]: self.add_bls_key(key) self._bls_key_sigs = [] - for key in info['bls-key-sigs']: + for key in info["bls-key-sigs"]: self.add_bls_key_sig(key) except KeyError as e: - raise InvalidValidatorError(3, 'Info has missing key') from e + raise InvalidValidatorError(3, "Info has missing key") from e - def load_from_blockchain(self, endpoint=_default_endpoint, timeout=_default_timeout): + def load_from_blockchain( + self, endpoint=_default_endpoint, timeout=_default_timeout + ): """ Import validator information from blockchain with given address At the moment, this is unable to fetch the BLS Signature, which is not implemented @@ -673,33 +705,43 @@ class Validator: """ try: if not self.does_validator_exist(endpoint, timeout): - raise InvalidValidatorError(5, f'Validator does not exist on chain according to {endpoint}') + raise InvalidValidatorError( + 5, f"Validator does not exist on chain according to {endpoint}" + ) except (RPCError, RequestsError, RequestsTimeoutError) as e: - raise InvalidValidatorError(5, 'Error requesting validator information') from e + raise InvalidValidatorError( + 5, "Error requesting validator information" + ) from e try: validator_info = get_validator_information(self._address, endpoint, timeout) except (RPCError, RequestsError, RequestsTimeoutError) as e: - raise InvalidValidatorError(5, 'Error requesting validator information') from e + raise InvalidValidatorError( + 5, "Error requesting validator information" + ) from e # Skip additional sanity checks when importing from chain try: - info = validator_info['validator'] - self._name = info['name'] - self._identity = info['identity'] - self._website = info['website'] - self._details = info['details'] - self._security_contact = info['security-contact'] - - self._min_self_delegation = info['min-self-delegation'] - self._max_total_delegation = info['max-total-delegation'] - self._inital_delegation = self._min_self_delegation # Since validator exists, set initial delegation to 0 - - self._max_rate = Decimal(info['max-rate']) - self._max_change_rate = Decimal(info['max-change-rate']) - self._rate = Decimal(info['rate']) - self._bls_keys = info[ 'bls-public-keys' ] + info = validator_info["validator"] + self._name = info["name"] + self._identity = info["identity"] + self._website = info["website"] + self._details = info["details"] + self._security_contact = info["security-contact"] + + self._min_self_delegation = info["min-self-delegation"] + self._max_total_delegation = info["max-total-delegation"] + self._inital_delegation = ( + self._min_self_delegation + ) # Since validator exists, set initial delegation to 0 + + self._max_rate = Decimal(info["max-rate"]) + self._max_change_rate = Decimal(info["max-change-rate"]) + self._rate = Decimal(info["rate"]) + self._bls_keys = info["bls-public-keys"] except KeyError as e: - raise InvalidValidatorError(5, 'Error importing validator information from RPC result') from e + raise InvalidValidatorError( + 5, "Error importing validator information from RPC result" + ) from e def export(self) -> dict: """ @@ -724,11 +766,13 @@ class Validator: "max-rate": self._max_rate, "max-change-rate": self._max_change_rate, "bls-public-keys": self._bls_keys, - "bls-key-sigs": self._bls_key_sigs + "bls-key-sigs": self._bls_key_sigs, } return info - def sign_create_validator_transaction(self, nonce, gas_price, gas_limit, private_key, chain_id=None) -> SignedTransaction: + def sign_create_validator_transaction( + self, nonce, gas_price, gas_limit, private_key, chain_id=None + ) -> SignedTransaction: """ Create but not post a transaction to Create the Validator using private_key @@ -746,16 +790,27 @@ class Validator: https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L413 """ info = self.export().copy() - info['directive'] = Directive.CreateValidator - info['validatorAddress'] = info.pop('validator-addr') # change the key - info['nonce'] = nonce - info['gasPrice'] = gas_price - info['gasLimit'] = gas_limit + info["directive"] = Directive.CreateValidator + info["validatorAddress"] = info.pop("validator-addr") # change the key + info["nonce"] = nonce + info["gasPrice"] = gas_price + info["gasLimit"] = gas_limit if chain_id: - info['chainId'] = chain_id + info["chainId"] = chain_id return sign_staking_transaction(info, private_key) - def sign_edit_validator_transaction(self, nonce, gas_price, gas_limit, rate, bls_key_to_remove, bls_key_to_add, bls_key_to_add_sig, private_key, chain_id=None) -> SignedTransaction: + def sign_edit_validator_transaction( + self, + nonce, + gas_price, + gas_limit, + rate, + bls_key_to_remove, + bls_key_to_add, + bls_key_to_add_sig, + private_key, + chain_id=None, + ) -> SignedTransaction: """ Create but not post a transaction to Edit the Validator using private_key @@ -776,18 +831,18 @@ class Validator: self.add_bls_key(bls_key_to_add) self.remove_bls_key(bls_key_to_remove) info = self.export().copy() - info['directive'] = Directive.EditValidator - info['validatorAddress'] = info.pop('validator-addr') # change the key - info['nonce'] = nonce - info['gasPrice'] = gas_price - info['gasLimit'] = gas_limit - _ = info.pop('max-rate') # not needed - _ = info.pop('max-change-rate') # not needed - _ = info.pop('bls-public-keys') # remove this list - _ = info.pop('amount') # also unused - info['bls-key-to-remove'] = bls_key_to_remove - info['bls-key-to-add'] = bls_key_to_add - info['bls-key-to-add-sig'] = bls_key_to_add_sig + info["directive"] = Directive.EditValidator + info["validatorAddress"] = info.pop("validator-addr") # change the key + info["nonce"] = nonce + info["gasPrice"] = gas_price + info["gasLimit"] = gas_limit + _ = info.pop("max-rate") # not needed + _ = info.pop("max-change-rate") # not needed + _ = info.pop("bls-public-keys") # remove this list + _ = info.pop("amount") # also unused + info["bls-key-to-remove"] = bls_key_to_remove + info["bls-key-to-add"] = bls_key_to_add + info["bls-key-to-add-sig"] = bls_key_to_add_sig if chain_id: - info['chainId'] = chain_id + info["chainId"] = chain_id return sign_staking_transaction(info, private_key) diff --git a/tests/bech32-pyhmy/test_bech32.py b/tests/bech32-pyhmy/test_bech32.py index 2d58938..e92202f 100644 --- a/tests/bech32-pyhmy/test_bech32.py +++ b/tests/bech32-pyhmy/test_bech32.py @@ -1,10 +1,9 @@ -from pyhmy.bech32 import ( - bech32 -) +from pyhmy.bech32 import bech32 + def test_encode(): - bech32.encode('one', 5, [121, 161]) + bech32.encode("one", 5, [121, 161]) -def test_decode(): - bech32.decode('one', 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') +def test_decode(): + bech32.decode("one", "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") diff --git a/tests/logging-pyhmy/test_logging.py b/tests/logging-pyhmy/test_logging.py index 018a66e..f15796d 100644 --- a/tests/logging-pyhmy/test_logging.py +++ b/tests/logging-pyhmy/test_logging.py @@ -14,11 +14,11 @@ def test_basic_logger(): logger.debug("test debug") logger.error("test error") logger.warning("test warning") - with open(f"{os.getcwd()}/logs/pytest.log", 'r') as f: + with open(f"{os.getcwd()}/logs/pytest.log", "r") as f: log_file_contents = f.readlines() assert not log_file_contents logger.write() - with open(f"{os.getcwd()}/logs/pytest.log", 'r') as f: + with open(f"{os.getcwd()}/logs/pytest.log", "r") as f: log_file_contents = f.readlines() for line in log_file_contents: if "INFO" in line: diff --git a/tests/numbers-pyhmy/test_numbers.py b/tests/numbers-pyhmy/test_numbers.py index 02fad81..b8d277a 100644 --- a/tests/numbers-pyhmy/test_numbers.py +++ b/tests/numbers-pyhmy/test_numbers.py @@ -1,8 +1,6 @@ from decimal import Decimal -from pyhmy import ( - numbers -) +from pyhmy import numbers def test_convert_atto_to_one(): @@ -12,12 +10,13 @@ def test_convert_atto_to_one(): b = numbers.convert_atto_to_one(1e18 + 0.6) assert Decimal(1) == b - c = numbers.convert_atto_to_one('1' + ('0' * 18)) + c = numbers.convert_atto_to_one("1" + ("0" * 18)) assert Decimal(1) == c d = numbers.convert_atto_to_one(Decimal(1e18)) assert Decimal(1) == d + def test_convert_one_to_atto(): a = numbers.convert_one_to_atto(1e-18) assert Decimal(1) == a @@ -25,7 +24,7 @@ def test_convert_one_to_atto(): b = numbers.convert_one_to_atto(1.5) assert Decimal(1.5e18) == b - c = numbers.convert_one_to_atto('1') + c = numbers.convert_one_to_atto("1") assert Decimal(1e18) == c d = numbers.convert_one_to_atto(Decimal(1)) diff --git a/tests/request-pyhmy/test_request.py b/tests/request-pyhmy/test_request.py index 19acdeb..4505069 100644 --- a/tests/request-pyhmy/test_request.py +++ b/tests/request-pyhmy/test_request.py @@ -4,31 +4,27 @@ import socket import pytest import requests -from pyhmy.rpc import ( - exceptions, - request -) +from pyhmy.rpc import exceptions, request @pytest.fixture(scope="session", autouse=True) def setup(): - endpoint = 'http://localhost:9500' + endpoint = "http://localhost:9500" timeout = 30 - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" params = [] - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": method, - "params": params - } - headers = { - 'Content-Type': 'application/json' - } + payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} + headers = {"Content-Type": "application/json"} try: - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) except Exception as e: pytest.skip("can not connect to local blockchain", allow_module_level=True) @@ -36,16 +32,18 @@ def setup(): def test_request_connection_error(): # Find available port s = socket.socket() - s.bind(('localhost', 0)) + s.bind(("localhost", 0)) port = s.getsockname()[1] s.close() if port == 0: pytest.skip("could not find available port") - bad_endpoint = f'http://localhost:{port}' + bad_endpoint = f"http://localhost:{port}" bad_request = None try: - bad_request = request.rpc_request('hmyv2_getNodeMetadata', endpoint=bad_endpoint) + bad_request = request.rpc_request( + "hmyv2_getNodeMetadata", endpoint=bad_endpoint + ) except Exception as e: assert isinstance(e, exceptions.RequestsError) assert bad_request is None @@ -54,7 +52,7 @@ def test_request_connection_error(): def test_request_rpc_error(): error_request = None try: - error_request = request.rpc_request('hmyv2_getBalance') + error_request = request.rpc_request("hmyv2_getBalance") except (exceptions.RequestsTimeoutError, exceptions.RequestsError) as err: pytest.skip("can not connect to local blockchain", allow_module_level=True) except Exception as e: @@ -63,24 +61,23 @@ def test_request_rpc_error(): def test_rpc_request(): - endpoint = 'http://localhost:9500' + endpoint = "http://localhost:9500" timeout = 30 - method = 'hmyv2_getNodeMetadata' + method = "hmyv2_getNodeMetadata" params = [] - payload = { - "id": "1", - "jsonrpc": "2.0", - "method": method, - "params": params - } - headers = { - 'Content-Type': 'application/json' - } + payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} + headers = {"Content-Type": "application/json"} response = None try: - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) except: pytest.skip("can not connect to local blockchain") assert response is not None @@ -89,14 +86,14 @@ def test_rpc_request(): try: resp = json.loads(response.content) except json.decoder.JSONDecodeError as err: - pytest.skip('unable to decode response') + pytest.skip("unable to decode response") assert resp is not None rpc_response = None try: rpc_response = request.rpc_request(method, params, endpoint, timeout) except exceptions.RPCError as e: - assert 'error' in resp + assert "error" in resp if rpc_response is not None: assert rpc_response == resp diff --git a/tests/sdk-pyhmy/conftest.py b/tests/sdk-pyhmy/conftest.py index 5767b6c..d9f3af3 100644 --- a/tests/sdk-pyhmy/conftest.py +++ b/tests/sdk-pyhmy/conftest.py @@ -9,11 +9,9 @@ import requests # 1f84c95ac16e6a50f08d44c7bde7aff8742212fda6e4321fde48bf83bef266dc / one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 (genesis) # 3c86ac59f6b038f584be1c08fced78d7c71bb55d5655f81714f3cddc82144c65 / one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (transferred 503) -endpoint = 'http://localhost:9500' +endpoint = "http://localhost:9500" timeout = 30 -headers = { - 'Content-Type': 'application/json' -} +headers = {"Content-Type": "application/json"} txs = [ # same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce) "0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3", @@ -37,8 +35,9 @@ stx_hashes = [ "0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968", "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f", ] -assert len( txs ) == len( tx_hashes ), "Mismatch in tx and tx_hash count" -assert len( stxs ) == len( stx_hashes ), "Mismatch in stx and stx_hash count" +assert len(txs) == len(tx_hashes), "Mismatch in tx and tx_hash count" +assert len(stxs) == len(stx_hashes), "Mismatch in stx and stx_hash count" + @pytest.fixture(scope="session", autouse=True) def setup_blockchain(): @@ -47,38 +46,61 @@ def setup_blockchain(): metadata = _check_connection() _check_staking_epoch(metadata) - for i in range( len( txs ) ): - tx = txs[ i ] - tx_hash = tx_hashes[ i ] - _send_transaction( tx, endpoint ) - if not _wait_for_transaction_confirmed( tx_hash, endpoint ): - pytest.skip("Could not confirm initial transaction #{} on chain".format( i ), allow_module_level = True) - - for i in range( len( stxs ) ): - stx = stxs[ i ] - stx_hash = stx_hashes[ i ] - _send_staking_transaction( stx, endpoint ) - if not _wait_for_staking_transaction_confirmed( stx_hash, endpoint ): - pytest.skip("Could not confirm initial staking transaction #{} on chain".format( i ), allow_module_level = True) + for i in range(len(txs)): + tx = txs[i] + tx_hash = tx_hashes[i] + _send_transaction(tx, endpoint) + if not _wait_for_transaction_confirmed(tx_hash, endpoint): + pytest.skip( + "Could not confirm initial transaction #{} on chain".format(i), + allow_module_level=True, + ) + + for i in range(len(stxs)): + stx = stxs[i] + stx_hash = stx_hashes[i] + _send_staking_transaction(stx, endpoint) + if not _wait_for_staking_transaction_confirmed(stx_hash, endpoint): + pytest.skip( + "Could not confirm initial staking transaction #{} on chain".format(i), + allow_module_level=True, + ) + def _check_connection(): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_getNodeMetadata', - "params": [] + "method": "hmyv2_getNodeMetadata", + "params": [], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) metadata = json.loads(response.content) - if 'error' in metadata: - pytest.skip(f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}", allow_module_level=True) - if 'chain-config' not in metadata['result']: - pytest.skip("Chain config not found in hmyv2_getNodeMetadata reply", allow_module_level=True) + if "error" in metadata: + pytest.skip( + f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}", + allow_module_level=True, + ) + if "chain-config" not in metadata["result"]: + pytest.skip( + "Chain config not found in hmyv2_getNodeMetadata reply", + allow_module_level=True, + ) return metadata except Exception as e: - pytest.skip('Can not connect to local blockchain or bad hmyv2_getNodeMetadata reply', allow_module_level=True) + pytest.skip( + "Can not connect to local blockchain or bad hmyv2_getNodeMetadata reply", + allow_module_level=True, + ) + def _check_staking_epoch(metadata): latest_header = None @@ -86,105 +108,163 @@ def _check_staking_epoch(metadata): payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_latestHeader', - "params": [] + "method": "hmyv2_latestHeader", + "params": [], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) latest_header = json.loads(response.content) - if 'error' in latest_header: - pytest.skip(f"Error in hmyv2_latestHeader reply: {latest_header['error']}", allow_module_level=True) + if "error" in latest_header: + pytest.skip( + f"Error in hmyv2_latestHeader reply: {latest_header['error']}", + allow_module_level=True, + ) except Exception as e: - pytest.skip('Failed to get hmyv2_latestHeader reply', allow_module_level=True) + pytest.skip("Failed to get hmyv2_latestHeader reply", allow_module_level=True) if metadata and latest_header: - staking_epoch = metadata['result']['chain-config']['staking-epoch'] - current_epoch = latest_header['result']['epoch'] + staking_epoch = metadata["result"]["chain-config"]["staking-epoch"] + current_epoch = latest_header["result"]["epoch"] if staking_epoch > current_epoch: - pytest.skip(f'Not staking epoch: current {current_epoch}, staking {staking_epoch}', allow_module_level=True) + pytest.skip( + f"Not staking epoch: current {current_epoch}, staking {staking_epoch}", + allow_module_level=True, + ) + def _send_transaction(raw_tx, endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_sendRawTransaction', - "params": [raw_tx] + "method": "hmyv2_sendRawTransaction", + "params": [raw_tx], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) tx = json.loads(response.content) - if 'error' in tx: - pytest.skip(f"Error in hmyv2_sendRawTransaction reply: {tx['error']}", allow_module_level=True) + if "error" in tx: + pytest.skip( + f"Error in hmyv2_sendRawTransaction reply: {tx['error']}", + allow_module_level=True, + ) except Exception as e: - pytest.skip('Failed to get hmyv2_sendRawTransaction reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_sendRawTransaction reply", allow_module_level=True + ) + def _check_transaction(tx_hash, endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_getTransactionByHash', - "params": [tx_hash] + "method": "hmyv2_getTransactionByHash", + "params": [tx_hash], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) tx_data = json.loads(response.content) return tx_data except Exception as e: - pytest.skip('Failed to get hmyv2_getTransactionByHash reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_getTransactionByHash reply", allow_module_level=True + ) + -def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout = 30): +def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout=30): start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_data = _check_transaction(tx_hash, endpoint) if tx_data is not None: - block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_data["result"].get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": return True time.sleep(random.uniform(0.2, 0.5)) return False -def _send_staking_transaction(raw_tx, endpoint = endpoint): + +def _send_staking_transaction(raw_tx, endpoint=endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_sendRawStakingTransaction', - "params": [raw_tx] + "method": "hmyv2_sendRawStakingTransaction", + "params": [raw_tx], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) staking_tx = json.loads(response.content) - if 'error' in staking_tx: - pytest.skip(f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}", allow_module_level=True) + if "error" in staking_tx: + pytest.skip( + f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}", + allow_module_level=True, + ) except Exception as e: - pytest.skip('Failed to get hmyv2_sendRawStakingTransaction reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_sendRawStakingTransaction reply", + allow_module_level=True, + ) -def _check_staking_transaction(stx_hash, endpoint = endpoint): + +def _check_staking_transaction(stx_hash, endpoint=endpoint): try: payload = { "id": "1", "jsonrpc": "2.0", - "method": 'hmyv2_getStakingTransactionByHash', - "params": [stx_hash] + "method": "hmyv2_getStakingTransactionByHash", + "params": [stx_hash], } - response = requests.request('POST', endpoint, headers=headers, - data=json.dumps(payload), timeout=timeout, allow_redirects=True) + response = requests.request( + "POST", + endpoint, + headers=headers, + data=json.dumps(payload), + timeout=timeout, + allow_redirects=True, + ) stx_data = json.loads(response.content) return stx_data except Exception as e: - pytest.skip('Failed to get hmyv2_getStakingTransactionByHash reply', allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_getStakingTransactionByHash reply", + allow_module_level=True, + ) + -def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout = 30): +def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout=30): answer = False start_time = time.time() - while((time.time() - start_time) <= timeout): + while (time.time() - start_time) <= timeout: tx_data = _check_staking_transaction(tx_hash, endpoint) if tx_data is not None: - block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) - unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) + block_hash = tx_data["result"].get("blockHash", "0x00") + unique_chars = "".join(set(list(block_hash[2:]))) if unique_chars != "0": answer = True time.sleep(random.uniform(0.2, 0.5)) diff --git a/tests/sdk-pyhmy/test_account.py b/tests/sdk-pyhmy/test_account.py index 1c1aa4a..b6f0ed5 100644 --- a/tests/sdk-pyhmy/test_account.py +++ b/tests/sdk-pyhmy/test_account.py @@ -1,33 +1,32 @@ import pytest import requests -from pyhmy import ( - account -) +from pyhmy import account -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -explorer_endpoint = 'http://localhost:9700' -endpoint_shard_one = 'http://localhost:9502' -local_test_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' +explorer_endpoint = "http://localhost:9700" +endpoint_shard_one = "http://localhost:9502" +local_test_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" test_validator_address = local_test_address genesis_block_number = 0 test_block_number = 1 -fake_shard = 'http://example.com' +fake_shard = "http://example.com" + def _test_account_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response @@ -36,69 +35,102 @@ def test_get_balance(setup_blockchain): assert isinstance(balance, int) assert balance > 0 + def test_get_balance_by_block(setup_blockchain): - balance = _test_account_rpc(account.get_balance_by_block, local_test_address, genesis_block_number) + balance = _test_account_rpc( + account.get_balance_by_block, local_test_address, genesis_block_number + ) assert isinstance(balance, int) assert balance > 0 + def test_get_account_nonce(setup_blockchain): - true_nonce = _test_account_rpc(account.get_account_nonce, local_test_address, test_block_number, endpoint=endpoint_shard_one) + true_nonce = _test_account_rpc( + account.get_account_nonce, + local_test_address, + test_block_number, + endpoint=endpoint_shard_one, + ) assert isinstance(true_nonce, int) + def test_get_transaction_history(setup_blockchain): - tx_history = _test_account_rpc(account.get_transaction_history, local_test_address, endpoint=explorer_endpoint) + tx_history = _test_account_rpc( + account.get_transaction_history, local_test_address, endpoint=explorer_endpoint + ) assert isinstance(tx_history, list) assert len(tx_history) >= 0 + def test_get_staking_transaction_history(setup_blockchain): - staking_tx_history = _test_account_rpc(account.get_staking_transaction_history, test_validator_address, endpoint=explorer_endpoint) + staking_tx_history = _test_account_rpc( + account.get_staking_transaction_history, + test_validator_address, + endpoint=explorer_endpoint, + ) assert isinstance(staking_tx_history, list) assert len(staking_tx_history) > 0 + def test_get_balance_on_all_shards(setup_blockchain): balances = _test_account_rpc(account.get_balance_on_all_shards, local_test_address) assert isinstance(balances, list) assert len(balances) == 2 + def test_get_total_balance(setup_blockchain): total_balance = _test_account_rpc(account.get_total_balance, local_test_address) assert isinstance(total_balance, int) assert total_balance > 0 + def test_is_valid_address(): - assert account.is_valid_address('one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur') - assert not account.is_valid_address('one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0') + assert account.is_valid_address("one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur") + assert not account.is_valid_address("one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0") + def test_get_transaction_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_transaction_count, local_test_address, 'latest', explorer_endpoint) + tx_count = _test_account_rpc( + account.get_transaction_count, local_test_address, "latest", explorer_endpoint + ) assert isinstance(tx_count, int) assert tx_count > 0 + def test_get_transactions_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_transactions_count, local_test_address, 'ALL', explorer_endpoint) + tx_count = _test_account_rpc( + account.get_transactions_count, local_test_address, "ALL", explorer_endpoint + ) + def test_get_staking_transactions_count(setup_blockchain): - tx_count = _test_account_rpc(account.get_staking_transactions_count, local_test_address, 'ALL', explorer_endpoint) + tx_count = _test_account_rpc( + account.get_staking_transactions_count, + local_test_address, + "ALL", + explorer_endpoint, + ) assert isinstance(tx_count, int) + def test_errors(): with pytest.raises(exceptions.RPCError): - account.get_balance('', fake_shard) + account.get_balance("", fake_shard) with pytest.raises(exceptions.RPCError): - account.get_balance_by_block('', 1, fake_shard) + account.get_balance_by_block("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_account_nonce('', 1, fake_shard) + account.get_account_nonce("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transaction_count('', 1, fake_shard) + account.get_transaction_count("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transactions_count('', 1, fake_shard) + account.get_transactions_count("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transactions_count('', 'ALL', fake_shard) + account.get_transactions_count("", "ALL", fake_shard) with pytest.raises(exceptions.RPCError): - account.get_transaction_history('', endpoint=fake_shard) + account.get_transaction_history("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - account.get_staking_transaction_history('', endpoint=fake_shard) + account.get_staking_transaction_history("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - account.get_balance_on_all_shards('', endpoint=fake_shard) + account.get_balance_on_all_shards("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - account.get_total_balance('', endpoint=fake_shard) + account.get_total_balance("", endpoint=fake_shard) diff --git a/tests/sdk-pyhmy/test_blockchain.py b/tests/sdk-pyhmy/test_blockchain.py index 4d5efc8..327a97e 100644 --- a/tests/sdk-pyhmy/test_blockchain.py +++ b/tests/sdk-pyhmy/test_blockchain.py @@ -1,211 +1,264 @@ import pytest import requests -from pyhmy import ( - blockchain -) +from pyhmy import blockchain -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions test_epoch_number = 0 genesis_block_number = 0 test_block_number = 1 test_block_hash = None -fake_shard = 'http://example.com' -address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' +fake_shard = "http://example.com" +address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" + def _test_blockchain_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_node_metadata(setup_blockchain): metadata = _test_blockchain_rpc(blockchain.get_node_metadata) assert isinstance(metadata, dict) + def test_get_sharding_structure(setup_blockchain): sharding_structure = _test_blockchain_rpc(blockchain.get_sharding_structure) assert isinstance(sharding_structure, list) assert len(sharding_structure) > 0 + def test_get_leader_address(setup_blockchain): leader = _test_blockchain_rpc(blockchain.get_leader_address) assert isinstance(leader, str) - assert 'one1' in leader + assert "one1" in leader + def test_get_block_number(setup_blockchain): current_block_number = _test_blockchain_rpc(blockchain.get_block_number) assert isinstance(current_block_number, int) + def test_get_current_epoch(setup_blockchain): current_epoch = _test_blockchain_rpc(blockchain.get_current_epoch) assert isinstance(current_epoch, int) + def tset_get_gas_price(setup_blockchain): gas = _test_blockchain_rpc(blockchain.get_gas_price) assert isinstance(gas, int) + def test_get_num_peers(setup_blockchain): peers = _test_blockchain_rpc(blockchain.get_num_peers) assert isinstance(peers, int) + def test_get_latest_header(setup_blockchain): header = _test_blockchain_rpc(blockchain.get_latest_header) assert isinstance(header, dict) + def test_get_latest_chain_headers(setup_blockchain): header_pair = _test_blockchain_rpc(blockchain.get_latest_chain_headers) assert isinstance(header_pair, dict) + def test_get_block_by_number(setup_blockchain): global test_block_hash block = _test_blockchain_rpc(blockchain.get_block_by_number, test_block_number) assert isinstance(block, dict) - assert 'hash' in block.keys() - test_block_hash = block['hash'] + assert "hash" in block.keys() + test_block_hash = block["hash"] + def test_get_block_by_hash(setup_blockchain): if not test_block_hash: - pytest.skip('Failed to get reference block hash') + pytest.skip("Failed to get reference block hash") block = _test_blockchain_rpc(blockchain.get_block_by_hash, test_block_hash) assert isinstance(block, dict) + def test_get_block_transaction_count_by_number(setup_blockchain): - tx_count = _test_blockchain_rpc(blockchain.get_block_transaction_count_by_number, test_block_number) + tx_count = _test_blockchain_rpc( + blockchain.get_block_transaction_count_by_number, test_block_number + ) assert isinstance(tx_count, int) + def test_get_block_transaction_count_by_hash(setup_blockchain): if not test_block_hash: - pytest.skip('Failed to get reference block hash') - tx_count = _test_blockchain_rpc(blockchain.get_block_transaction_count_by_hash, test_block_hash) + pytest.skip("Failed to get reference block hash") + tx_count = _test_blockchain_rpc( + blockchain.get_block_transaction_count_by_hash, test_block_hash + ) assert isinstance(tx_count, int) + def test_get_blocks(setup_blockchain): - blocks = _test_blockchain_rpc(blockchain.get_blocks, genesis_block_number, test_block_number) + blocks = _test_blockchain_rpc( + blockchain.get_blocks, genesis_block_number, test_block_number + ) assert isinstance(blocks, list) assert len(blocks) == (test_block_number - genesis_block_number + 1) + def test_get_block_signers(setup_blockchain): - block_signers = _test_blockchain_rpc(blockchain.get_block_signers, test_block_number) + block_signers = _test_blockchain_rpc( + blockchain.get_block_signers, test_block_number + ) assert isinstance(block_signers, list) assert len(block_signers) > 0 + def test_get_validators(setup_blockchain): validators = _test_blockchain_rpc(blockchain.get_validators, test_epoch_number) assert isinstance(validators, dict) - assert 'validators' in validators.keys() - assert len(validators['validators']) > 0 + assert "validators" in validators.keys() + assert len(validators["validators"]) > 0 + def test_get_shard(setup_blockchain): shard = _test_blockchain_rpc(blockchain.get_shard) assert isinstance(shard, int) assert shard == 0 + def test_get_staking_epoch(setup_blockchain): staking_epoch = _test_blockchain_rpc(blockchain.get_staking_epoch) assert isinstance(staking_epoch, int) + def test_get_prestaking_epoch(setup_blockchain): prestaking_epoch = _test_blockchain_rpc(blockchain.get_prestaking_epoch) assert isinstance(prestaking_epoch, int) + def test_get_bad_blocks(setup_blockchain): # TODO: Remove skip when RPC is fixed pytest.skip("Known error with hmyv2_getCurrentBadBlocks") bad_blocks = _test_blockchain_rpc(blockchain.get_bad_blocks) assert isinstance(bad_blocks, list) + def test_get_validator_keys(setup_blockchain): keys = _test_blockchain_rpc(blockchain.get_validator_keys, test_epoch_number) assert isinstance(keys, list) assert len(keys) > 0 + def test_get_block_signers_keys(setup_blockchain): keys = _test_blockchain_rpc(blockchain.get_block_signers_keys, test_block_number) assert isinstance(keys, list) assert len(keys) > 0 + def test_chain_id(setup_blockchain): chain_id = _test_blockchain_rpc(blockchain.chain_id) assert isinstance(chain_id, int) + def test_get_peer_info(setup_blockchain): peer_info = _test_blockchain_rpc(blockchain.get_peer_info) assert isinstance(peer_info, dict) + def test_protocol_version(setup_blockchain): protocol_version = _test_blockchain_rpc(blockchain.protocol_version) assert isinstance(protocol_version, int) + def test_is_last_block(setup_blockchain): is_last_block = _test_blockchain_rpc(blockchain.is_last_block, 0) assert isinstance(is_last_block, bool) assert not is_last_block + def test_epoch_last_block(setup_blockchain): epoch_last_block = _test_blockchain_rpc(blockchain.epoch_last_block, 0) assert isinstance(epoch_last_block, int) + def test_get_circulating_supply(setup_blockchain): circulating_supply = _test_blockchain_rpc(blockchain.get_circulating_supply) assert isinstance(circulating_supply, str) + def test_get_total_supply(setup_blockchain): total_supply = _test_blockchain_rpc(blockchain.get_total_supply) assert isinstance(total_supply, str) or total_supply == None + def test_get_last_cross_links(setup_blockchain): last_cross_links = _test_blockchain_rpc(blockchain.get_last_cross_links) assert isinstance(last_cross_links, list) + def test_get_gas_price(setup_blockchain): gas_price = _test_blockchain_rpc(blockchain.get_gas_price) assert isinstance(gas_price, int) + def test_get_version(setup_blockchain): version = _test_blockchain_rpc(blockchain.get_version) assert isinstance(version, int) + def test_get_header_by_number(setup_blockchain): header_pair = _test_blockchain_rpc(blockchain.get_header_by_number, 0) assert isinstance(header_pair, dict) + def test_get_block_staking_transaction_count_by_number(setup_blockchain): - tx_count = _test_blockchain_rpc(blockchain.get_block_staking_transaction_count_by_number, test_block_number) + tx_count = _test_blockchain_rpc( + blockchain.get_block_staking_transaction_count_by_number, test_block_number + ) assert isinstance(tx_count, int) + def test_get_block_staking_transaction_count_by_hash(setup_blockchain): if not test_block_hash: - pytest.skip('Failed to get reference block hash') - tx_count = _test_blockchain_rpc(blockchain.get_block_staking_transaction_count_by_hash, test_block_hash) + pytest.skip("Failed to get reference block hash") + tx_count = _test_blockchain_rpc( + blockchain.get_block_staking_transaction_count_by_hash, test_block_hash + ) assert isinstance(tx_count, int) + def test_is_block_signer(setup_blockchain): - is_signer = _test_blockchain_rpc(blockchain.is_block_signer, test_block_number, address) + is_signer = _test_blockchain_rpc( + blockchain.is_block_signer, test_block_number, address + ) assert isinstance(is_signer, bool) + def test_get_signed_blocks(setup_blockchain): signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address) assert isinstance(signed_blocks, int) + def test_in_sync(setup_blockchain): in_sync = _test_blockchain_rpc(blockchain.in_sync) assert isinstance(in_sync, bool) + def test_beacon_in_sync(setup_blockchain): beacon_in_sync = _test_blockchain_rpc(blockchain.beacon_in_sync) assert isinstance(beacon_in_sync, bool) + def test_errors(): with pytest.raises(exceptions.RPCError): blockchain.chain_id(fake_shard) @@ -254,15 +307,15 @@ def test_errors(): with pytest.raises(exceptions.RPCError): blockchain.get_block_by_number(0, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_block_by_hash('', endpoint=fake_shard) + blockchain.get_block_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_block_transaction_count_by_number(0, fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_block_transaction_count_by_hash('', fake_shard) + blockchain.get_block_transaction_count_by_hash("", fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_block_staking_transaction_count_by_number(0, fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_block_staking_transaction_count_by_hash('', fake_shard) + blockchain.get_block_staking_transaction_count_by_hash("", fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_blocks(0, 1, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): @@ -270,9 +323,9 @@ def test_errors(): with pytest.raises(exceptions.RPCError): blockchain.get_block_signers_keys(0, fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.is_block_signer(0, '', fake_shard) + blockchain.is_block_signer(0, "", fake_shard) with pytest.raises(exceptions.RPCError): - blockchain.get_signed_blocks('', fake_shard) + blockchain.get_signed_blocks("", fake_shard) with pytest.raises(exceptions.RPCError): blockchain.get_validators(1, fake_shard) with pytest.raises(exceptions.RPCError): diff --git a/tests/sdk-pyhmy/test_contract.py b/tests/sdk-pyhmy/test_contract.py index 59dacc4..3739c5f 100644 --- a/tests/sdk-pyhmy/test_contract.py +++ b/tests/sdk-pyhmy/test_contract.py @@ -1,71 +1,80 @@ import pytest -from pyhmy import ( - contract -) +from pyhmy import contract -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -explorer_endpoint = 'http://localhost:9599' -contract_tx_hash = '0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f' +explorer_endpoint = "http://localhost:9599" +contract_tx_hash = "0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f" # deployedBytecode from json file -contract_code = '0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033' +contract_code = "0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033" contract_address = None -fake_shard = 'http://example.com' +fake_shard = "http://example.com" + def _test_contract_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - elif isinstance(e, exceptions.RPCError) and 'estimateGas returned' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + elif isinstance(e, exceptions.RPCError) and "estimateGas returned" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_contract_address_from_hash(setup_blockchain): global contract_address - contract_address = _test_contract_rpc(contract.get_contract_address_from_hash, contract_tx_hash) + contract_address = _test_contract_rpc( + contract.get_contract_address_from_hash, contract_tx_hash + ) assert isinstance(contract_address, str) + def test_call(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') - called = _test_contract_rpc(contract.call, contract_address, 'latest') - assert isinstance(called, str) and called.startswith('0x') + pytest.skip("Contract address not loaded yet") + called = _test_contract_rpc(contract.call, contract_address, "latest") + assert isinstance(called, str) and called.startswith("0x") + def test_estimate_gas(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') + pytest.skip("Contract address not loaded yet") gas = _test_contract_rpc(contract.estimate_gas, contract_address) assert isinstance(gas, int) + def test_get_code(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') - code = _test_contract_rpc(contract.get_code, contract_address, 'latest') + pytest.skip("Contract address not loaded yet") + code = _test_contract_rpc(contract.get_code, contract_address, "latest") assert code == contract_code + def test_get_storage_at(setup_blockchain): if not contract_address: - pytest.skip('Contract address not loaded yet') - storage = _test_contract_rpc(contract.get_storage_at, contract_address, '0x0', 'latest') - assert isinstance(storage, str) and storage.startswith('0x') + pytest.skip("Contract address not loaded yet") + storage = _test_contract_rpc( + contract.get_storage_at, contract_address, "0x0", "latest" + ) + assert isinstance(storage, str) and storage.startswith("0x") + def test_errors(): with pytest.raises(exceptions.RPCError): - contract.get_contract_address_from_hash('', fake_shard) + contract.get_contract_address_from_hash("", fake_shard) with pytest.raises(exceptions.RPCError): - contract.call('', '', endpoint=fake_shard) + contract.call("", "", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - contract.estimate_gas('', endpoint=fake_shard) + contract.estimate_gas("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - contract.get_code('', 'latest', endpoint=fake_shard) + contract.get_code("", "latest", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - contract.get_storage_at('', 1, 'latest', endpoint=fake_shard) + contract.get_storage_at("", 1, "latest", endpoint=fake_shard) diff --git a/tests/sdk-pyhmy/test_signing.py b/tests/sdk-pyhmy/test_signing.py index 34ac601..7709e20 100644 --- a/tests/sdk-pyhmy/test_signing.py +++ b/tests/sdk-pyhmy/test_signing.py @@ -1,6 +1,4 @@ -from pyhmy import ( - signing -) +from pyhmy import signing """ Test signature source (node.js) @@ -30,16 +28,25 @@ let signed = RLPSign(transaction, privateKey); console.log( 'Signed transaction' ) console.log(signed) """ + + def test_eth_transaction(): transaction_dict = { - 'nonce': 2, - 'gasPrice': 1, - 'gas': 100, # signing.py uses Ether, which by default calls it gas - 'to': '0x14791697260e4c9a71f18484c9f997b308e59325', - 'value': 5, + "nonce": 2, + "gasPrice": 1, + "gas": 100, # signing.py uses Ether, which by default calls it gas + "to": "0x14791697260e4c9a71f18484c9f997b308e59325", + "value": 5, } - signed_tx = signing.sign_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1' + signed_tx = signing.sign_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1" + ) + """ Test signature source (node.js) @@ -71,16 +78,24 @@ let signed = RLPSign(transaction, privateKey); console.log( 'Signed transaction' ) console.log(signed) """ + + def test_hmy_transaction(): transaction_dict = { - 'nonce': 2, - 'gasPrice': 1, - 'gas': 100, # signing.py uses Ether, which by default calls it gas - 'to': '0x14791697260e4c9a71f18484c9f997b308e59325', - 'value': 5, - 'shardID': 0, - 'toShardID': 1, - 'chainId': 'HmyMainnet' + "nonce": 2, + "gasPrice": 1, + "gas": 100, # signing.py uses Ether, which by default calls it gas + "to": "0x14791697260e4c9a71f18484c9f997b308e59325", + "value": 5, + "shardID": 0, + "toShardID": 1, + "chainId": "HmyMainnet", } - signed_tx = signing.sign_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d' + signed_tx = signing.sign_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d" + ) diff --git a/tests/sdk-pyhmy/test_staking.py b/tests/sdk-pyhmy/test_staking.py index f84375a..7fc0ed0 100644 --- a/tests/sdk-pyhmy/test_staking.py +++ b/tests/sdk-pyhmy/test_staking.py @@ -1,135 +1,191 @@ import pytest import requests -from pyhmy import ( - staking -) +from pyhmy import staking -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -explorer_endpoint = 'http://localhost:9700' -test_validator_address = 'one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3' -fake_shard = 'http://example.com' +explorer_endpoint = "http://localhost:9700" +test_validator_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" +fake_shard = "http://example.com" + def _test_staking_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_all_validator_addresses(setup_blockchain): validator_addresses = _test_staking_rpc(staking.get_all_validator_addresses) assert isinstance(validator_addresses, list) assert len(validator_addresses) > 0 assert test_validator_address in validator_addresses + def test_get_validator_information(setup_blockchain): info = _test_staking_rpc(staking.get_validator_information, test_validator_address) assert isinstance(info, dict) + def test_get_all_validator_information(setup_blockchain): all_validator_information = _test_staking_rpc(staking.get_all_validator_information) assert isinstance(all_validator_information, list) assert len(all_validator_information) > 0 + def test_get_delegations_by_delegator(setup_blockchain): - delegations = _test_staking_rpc(staking.get_delegations_by_delegator, test_validator_address) + delegations = _test_staking_rpc( + staking.get_delegations_by_delegator, test_validator_address + ) assert isinstance(delegations, list) assert len(delegations) > 0 + def test_get_delegations_by_validator(setup_blockchain): - delegations = _test_staking_rpc(staking.get_delegations_by_validator, test_validator_address) + delegations = _test_staking_rpc( + staking.get_delegations_by_validator, test_validator_address + ) assert isinstance(delegations, list) assert len(delegations) > 0 + def test_get_current_utility_metrics(setup_blockchain): metrics = _test_staking_rpc(staking.get_current_utility_metrics) assert isinstance(metrics, dict) + def test_get_staking_network_info(setup_blockchain): info = _test_staking_rpc(staking.get_staking_network_info) assert isinstance(info, dict) + def test_get_super_committees(setup_blockchain): committee = _test_staking_rpc(staking.get_super_committees) assert isinstance(committee, dict) + def test_get_raw_median_stake_snapshot(setup_blockchain): median_stake = _test_staking_rpc(staking.get_raw_median_stake_snapshot) assert isinstance(median_stake, dict) + def test_get_validator_information_by_block(setup_blockchain): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block - info = _test_staking_rpc(staking.get_validator_information_by_block_number, test_validator_address, 'latest', endpoint=explorer_endpoint) + info = _test_staking_rpc( + staking.get_validator_information_by_block_number, + test_validator_address, + "latest", + endpoint=explorer_endpoint, + ) assert isinstance(info, dict) + def test_get_validator_information_by_block(setup_blockchain): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block - info = _test_staking_rpc(staking.get_all_validator_information_by_block_number, 'latest', endpoint=explorer_endpoint) + info = _test_staking_rpc( + staking.get_all_validator_information_by_block_number, + "latest", + endpoint=explorer_endpoint, + ) assert isinstance(info, list) + def test_get_delegations_by_delegator_by_block(setup_blockchain): - delegations = _test_staking_rpc(staking.get_delegations_by_delegator_by_block_number, test_validator_address, 'latest', endpoint=explorer_endpoint) + delegations = _test_staking_rpc( + staking.get_delegations_by_delegator_by_block_number, + test_validator_address, + "latest", + endpoint=explorer_endpoint, + ) assert isinstance(delegations, list) + def test_get_elected_validator_addresses(setup_blockchain): validator_addresses = _test_staking_rpc(staking.get_elected_validator_addresses) assert isinstance(validator_addresses, list) assert len(validator_addresses) > 0 + def test_get_validators(setup_blockchain): validators = _test_staking_rpc(staking.get_validators, 2) assert isinstance(validators, dict) - assert len(validators['validators']) > 0 + assert len(validators["validators"]) > 0 + def test_get_validator_keys(setup_blockchain): validators = _test_staking_rpc(staking.get_validator_keys, 2) assert isinstance(validators, list) + def test_get_validator_self_delegation(setup_blockchain): - self_delegation = _test_staking_rpc(staking.get_validator_self_delegation, test_validator_address) + self_delegation = _test_staking_rpc( + staking.get_validator_self_delegation, test_validator_address + ) assert isinstance(self_delegation, int) assert self_delegation > 0 + def test_get_validator_total_delegation(setup_blockchain): - total_delegation = _test_staking_rpc(staking.get_validator_total_delegation, test_validator_address) + total_delegation = _test_staking_rpc( + staking.get_validator_total_delegation, test_validator_address + ) assert isinstance(total_delegation, int) assert total_delegation > 0 + def test_get_all_delegation_information(setup_blockchain): - delegation_information = _test_staking_rpc(staking.get_all_delegation_information, 0) + delegation_information = _test_staking_rpc( + staking.get_all_delegation_information, 0 + ) assert isinstance(delegation_information, list) assert len(delegation_information) > 0 + def test_get_delegation_by_delegator_and_validator(setup_blockchain): - delegation_information = _test_staking_rpc(staking.get_delegation_by_delegator_and_validator, test_validator_address, test_validator_address) + delegation_information = _test_staking_rpc( + staking.get_delegation_by_delegator_and_validator, + test_validator_address, + test_validator_address, + ) assert isinstance(delegation_information, dict) + def test_get_available_redelegation_balance(setup_blockchain): - redelgation_balance = _test_staking_rpc(staking.get_available_redelegation_balance, test_validator_address) + redelgation_balance = _test_staking_rpc( + staking.get_available_redelegation_balance, test_validator_address + ) assert isinstance(redelgation_balance, int) assert redelgation_balance == 0 + def test_get_total_staking(setup_blockchain): total_staking = _test_staking_rpc(staking.get_total_staking) assert isinstance(total_staking, int) - if staking.get_validator_information(test_validator_address, explorer_endpoint)[ 'active-status' ] == 'active': + if ( + staking.get_validator_information(test_validator_address, explorer_endpoint)[ + "active-status" + ] + == "active" + ): assert total_staking > 0 + def test_errors(): with pytest.raises(exceptions.RPCError): staking.get_all_validator_addresses(fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_information('', fake_shard) + staking.get_validator_information("", fake_shard) with pytest.raises(exceptions.RPCError): staking.get_elected_validator_addresses(fake_shard) with pytest.raises(exceptions.RPCError): @@ -137,27 +193,27 @@ def test_errors(): with pytest.raises(exceptions.RPCError): staking.get_validator_keys(1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_information_by_block_number('', 1, fake_shard) + staking.get_validator_information_by_block_number("", 1, fake_shard) with pytest.raises(exceptions.RPCError): staking.get_all_validator_information(-1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_self_delegation('', fake_shard) + staking.get_validator_self_delegation("", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_validator_total_delegation('', fake_shard) + staking.get_validator_total_delegation("", fake_shard) with pytest.raises(exceptions.RPCError): staking.get_all_validator_information_by_block_number(1, 1, fake_shard) with pytest.raises(exceptions.RPCError): staking.get_all_delegation_information(1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_delegator('', fake_shard) + staking.get_delegations_by_delegator("", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_delegator_by_block_number('', 1, fake_shard) + staking.get_delegations_by_delegator_by_block_number("", 1, fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegation_by_delegator_and_validator('', '', fake_shard) + staking.get_delegation_by_delegator_and_validator("", "", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_available_redelegation_balance('', fake_shard) + staking.get_available_redelegation_balance("", fake_shard) with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_validator('', fake_shard) + staking.get_delegations_by_validator("", fake_shard) with pytest.raises(exceptions.RPCError): staking.get_current_utility_metrics(fake_shard) with pytest.raises(exceptions.RPCError): diff --git a/tests/sdk-pyhmy/test_staking_signing.py b/tests/sdk-pyhmy/test_staking_signing.py index 9e7aefe..b947cbb 100644 --- a/tests/sdk-pyhmy/test_staking_signing.py +++ b/tests/sdk-pyhmy/test_staking_signing.py @@ -1,11 +1,6 @@ -from pyhmy import ( - staking_signing, - staking_structures -) +from pyhmy import staking_signing, staking_structures -from pyhmy.numbers import ( - convert_one_to_atto -) +from pyhmy.numbers import convert_one_to_atto # other transactions (create/edit validator) are in test_validator.py # test_delegate is the same as test_undelegate (except the directive) so it has been omitted @@ -56,17 +51,26 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_collect_rewards_chain_id(): transaction_dict = { - 'directive': staking_structures.Directive.CollectRewards, - 'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', - 'nonce': 2, - 'gasPrice': int(convert_one_to_atto(1)), - 'gasLimit': 100, - 'chainId': 1, # with chainId for coverage + "directive": staking_structures.Directive.CollectRewards, + "delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9", + "nonce": 2, + "gasPrice": int(convert_one_to_atto(1)), + "gasLimit": 100, + "chainId": 1, # with chainId for coverage } - signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099' + signed_tx = staking_signing.sign_staking_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099" + ) + """ let stakingTx @@ -87,16 +91,24 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_delegate(): transaction_dict = { - 'directive': staking_structures.Directive.Delegate, - 'delegatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', - 'validatorAddress': 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9', - 'amount': 5, - 'nonce': 2, - 'gasPrice': int(convert_one_to_atto(1)), - 'gasLimit': 100, - 'chainId': 2, + "directive": staking_structures.Directive.Delegate, + "delegatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9", + "validatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9", + "amount": 5, + "nonce": 2, + "gasPrice": int(convert_one_to_atto(1)), + "gasLimit": 100, + "chainId": 2, } - signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') - assert signed_tx.rawTransaction.hex() == '0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6' + signed_tx = staking_signing.sign_staking_transaction( + transaction_dict, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + ) + assert ( + signed_tx.rawTransaction.hex() + == "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6" + ) diff --git a/tests/sdk-pyhmy/test_transaction.py b/tests/sdk-pyhmy/test_transaction.py index 2bbcd61..3194e5d 100644 --- a/tests/sdk-pyhmy/test_transaction.py +++ b/tests/sdk-pyhmy/test_transaction.py @@ -1,157 +1,205 @@ import pytest -from pyhmy import ( - transaction -) +from pyhmy import transaction -from pyhmy.rpc import ( - exceptions -) +from pyhmy.rpc import exceptions -endpoint = 'http://localhost:9500' -endpoint_shard_one = 'http://localhost:9502' -fake_shard = 'http://example.com' +endpoint = "http://localhost:9500" +endpoint_shard_one = "http://localhost:9502" +fake_shard = "http://example.com" # previously sent txs to get and check -tx_hash = '0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43' +tx_hash = "0xc26be5776aa57438bccf196671a2d34f3f22c9c983c0f844c62b2fb90403aa43" tx_block_num = None tx_block_hash = None tx_index = None -cx_hash = '0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf' +cx_hash = "0xf73ba634cb96fc0e3e2c9d3b4c91379e223741be4a5aa56e6d6caf49c1ae75cf" -stx_hash = '0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f' +stx_hash = "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f" stx_block_num = None stx_block_hash = None stx_index = None # new txs to send and check -raw_tx = '0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0' -raw_tx_hash = '0x7ccd80f8513f76ec58b357c7a82a12a95e025d88f1444e953f90e3d86e222571' +raw_tx = "0xf86f0385174876e800825208808094c9c6d47ee5f2e3e08d7367ad1a1373ba9dd172418905b12aefafa80400008027a07a4952b90bf38723a9197179a8e6d2e9b3a86fd6da4e66a9cf09fdc59783f757a053910798b311245525bd77d6119332458c2855102e4fb9e564f6a3b710d18bb0" +raw_tx_hash = "0x7ccd80f8513f76ec58b357c7a82a12a95e025d88f1444e953f90e3d86e222571" + +raw_stx = "0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000008085174876e80082c35027a0808ea7d27adf3b1f561e8da4676814084bb75ac541b616bece87c6446e6cc54ea02f19f0b14240354bd42ad60b0c7189873c0be87044e13072b0981a837ca76f64" +raw_stx_hash = "0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629" -raw_stx = '0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f4463f1d4874b548dfbac29d91f3489056bc75e2d631000008085174876e80082c35027a0808ea7d27adf3b1f561e8da4676814084bb75ac541b616bece87c6446e6cc54ea02f19f0b14240354bd42ad60b0c7189873c0be87044e13072b0981a837ca76f64' -raw_stx_hash = '0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629' def _test_transaction_rpc(fn, *args, **kwargs): if not callable(fn): - pytest.fail(f'Invalid function: {fn}') + pytest.fail(f"Invalid function: {fn}") try: response = fn(*args, **kwargs) except Exception as e: - if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): - pytest.skip(f'{str(e)}') - pytest.fail(f'Unexpected error: {e.__class__} {e}') + if isinstance( + e, exceptions.RPCError + ) and "does not exist/is not available" in str(e): + pytest.skip(f"{str(e)}") + pytest.fail(f"Unexpected error: {e.__class__} {e}") return response + def test_get_pending_transactions(setup_blockchain): pool = _test_transaction_rpc(transaction.get_pending_transactions) assert isinstance(pool, list) + def test_get_transaction_by_hash(setup_blockchain): - tx = _test_transaction_rpc(transaction.get_transaction_by_hash, tx_hash, endpoint=endpoint) + tx = _test_transaction_rpc( + transaction.get_transaction_by_hash, tx_hash, endpoint=endpoint + ) assert tx assert isinstance(tx, dict) - assert 'blockNumber' in tx.keys() - assert 'blockHash' in tx.keys() + assert "blockNumber" in tx.keys() + assert "blockHash" in tx.keys() global tx_block_num - tx_block_num = int(tx['blockNumber']) + tx_block_num = int(tx["blockNumber"]) global tx_block_hash - tx_block_hash = tx['blockHash'] + tx_block_hash = tx["blockHash"] global tx_index - tx_index = int(tx[ 'transactionIndex' ]) + tx_index = int(tx["transactionIndex"]) + def test_get_transaction_by_block_hash_and_index(setup_blockchain): if not tx_block_hash: - pytest.skip('Failed to get reference block hash') - tx = _test_transaction_rpc(transaction.get_transaction_by_block_hash_and_index, - tx_block_hash, tx_index, endpoint=endpoint) + pytest.skip("Failed to get reference block hash") + tx = _test_transaction_rpc( + transaction.get_transaction_by_block_hash_and_index, + tx_block_hash, + tx_index, + endpoint=endpoint, + ) assert tx assert isinstance(tx, dict) + def test_get_transaction_by_block_number_and_index(setup_blockchain): if not tx_block_num: - pytest.skip('Failed to get reference block num') - tx = _test_transaction_rpc(transaction.get_transaction_by_block_number_and_index, tx_block_num, tx_index, - endpoint=endpoint) + pytest.skip("Failed to get reference block num") + tx = _test_transaction_rpc( + transaction.get_transaction_by_block_number_and_index, + tx_block_num, + tx_index, + endpoint=endpoint, + ) assert tx assert isinstance(tx, dict) + def test_get_transaction_receipt(setup_blockchain): - tx_receipt = _test_transaction_rpc(transaction.get_transaction_receipt, tx_hash, endpoint=endpoint) + tx_receipt = _test_transaction_rpc( + transaction.get_transaction_receipt, tx_hash, endpoint=endpoint + ) assert tx_receipt assert isinstance(tx_receipt, dict) + def test_get_transaction_error_sink(setup_blockchain): errors = _test_transaction_rpc(transaction.get_transaction_error_sink) assert isinstance(errors, list) + def test_send_and_confirm_raw_transaction(setup_blockchain): # Note: this test is not yet idempotent since the localnet will reject transactions which were previously finalized. - test_tx = _test_transaction_rpc(transaction.send_and_confirm_raw_transaction, - raw_tx) + test_tx = _test_transaction_rpc( + transaction.send_and_confirm_raw_transaction, raw_tx + ) assert isinstance(test_tx, dict) - assert test_tx[ 'hash' ] == raw_tx_hash + assert test_tx["hash"] == raw_tx_hash + def test_get_pending_cx_receipts(setup_blockchain): pending = _test_transaction_rpc(transaction.get_pending_cx_receipts) assert isinstance(pending, list) + def test_get_cx_receipt_by_hash(setup_blockchain): - cx = _test_transaction_rpc(transaction.get_cx_receipt_by_hash, cx_hash, endpoint_shard_one) + cx = _test_transaction_rpc( + transaction.get_cx_receipt_by_hash, cx_hash, endpoint_shard_one + ) assert cx assert isinstance(cx, dict) + def test_resend_cx_receipt(setup_blockchain): sent = _test_transaction_rpc(transaction.resend_cx_receipt, cx_hash) assert isinstance(sent, bool) assert sent + def test_get_staking_transaction_by_hash(setup_blockchain): - staking_tx = _test_transaction_rpc(transaction.get_staking_transaction_by_hash, stx_hash) + staking_tx = _test_transaction_rpc( + transaction.get_staking_transaction_by_hash, stx_hash + ) assert staking_tx assert isinstance(staking_tx, dict) - assert 'blockNumber' in staking_tx.keys() - assert 'blockHash' in staking_tx.keys() + assert "blockNumber" in staking_tx.keys() + assert "blockHash" in staking_tx.keys() global stx_block_num - stx_block_num = int(staking_tx['blockNumber']) + stx_block_num = int(staking_tx["blockNumber"]) global stx_block_hash - stx_block_hash = staking_tx['blockHash'] + stx_block_hash = staking_tx["blockHash"] global stx_index - stx_index = int(staking_tx['transactionIndex']) + stx_index = int(staking_tx["transactionIndex"]) + def test_get_transaction_by_block_hash_and_index(setup_blockchain): if not stx_block_hash: - pytest.skip('Failed to get reference block hash') - stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_hash_and_index, stx_block_hash, stx_index) + pytest.skip("Failed to get reference block hash") + stx = _test_transaction_rpc( + transaction.get_staking_transaction_by_block_hash_and_index, + stx_block_hash, + stx_index, + ) assert stx assert isinstance(stx, dict) + def test_get_transaction_by_block_number_and_index(setup_blockchain): if not stx_block_num: - pytest.skip('Failed to get reference block num') - stx = _test_transaction_rpc(transaction.get_staking_transaction_by_block_number_and_index, stx_block_num, stx_index) + pytest.skip("Failed to get reference block num") + stx = _test_transaction_rpc( + transaction.get_staking_transaction_by_block_number_and_index, + stx_block_num, + stx_index, + ) assert stx assert isinstance(stx, dict) + def test_get_staking_transaction_error_sink(setup_blockchain): errors = _test_transaction_rpc(transaction.get_staking_transaction_error_sink) assert isinstance(errors, list) + def test_send_raw_staking_transaction(setup_blockchain): - test_stx = _test_transaction_rpc(transaction.send_and_confirm_raw_staking_transaction, raw_stx, endpoint=endpoint) + test_stx = _test_transaction_rpc( + transaction.send_and_confirm_raw_staking_transaction, raw_stx, endpoint=endpoint + ) assert isinstance(test_stx, dict) - assert test_stx[ 'hash' ] == raw_stx_hash + assert test_stx["hash"] == raw_stx_hash + def test_get_pool_stats(setup_blockchain): - test_pool_stats = _test_transaction_rpc(transaction.get_pool_stats, endpoint=endpoint) + test_pool_stats = _test_transaction_rpc( + transaction.get_pool_stats, endpoint=endpoint + ) assert isinstance(test_pool_stats, dict) + def test_get_pending_staking_transactions(setup_blockchain): - pending_staking_transactions = _test_transaction_rpc(transaction.get_pending_staking_transactions, endpoint=endpoint) + pending_staking_transactions = _test_transaction_rpc( + transaction.get_pending_staking_transactions, endpoint=endpoint + ) assert isinstance(pending_staking_transactions, list) + def test_errors(): with pytest.raises(exceptions.RPCError): transaction.get_pending_transactions(fake_shard) @@ -160,30 +208,34 @@ def test_errors(): with pytest.raises(exceptions.RPCError): transaction.get_pool_stats(fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_transaction_by_hash('', endpoint=fake_shard) + transaction.get_transaction_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_transaction_by_block_hash_and_index('', 1, endpoint=fake_shard) + transaction.get_transaction_by_block_hash_and_index("", 1, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): transaction.get_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_transaction_receipt('', endpoint=fake_shard) + transaction.get_transaction_receipt("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.send_raw_transaction('', endpoint=fake_shard) + transaction.send_raw_transaction("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): transaction.get_pending_cx_receipts(fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_cx_receipt_by_hash('', endpoint=fake_shard) + transaction.get_cx_receipt_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.resend_cx_receipt('', endpoint=fake_shard) + transaction.resend_cx_receipt("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_by_hash('', endpoint=fake_shard) + transaction.get_staking_transaction_by_hash("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_by_block_hash_and_index('', 1, endpoint=fake_shard) + transaction.get_staking_transaction_by_block_hash_and_index( + "", 1, endpoint=fake_shard + ) with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard) + transaction.get_staking_transaction_by_block_number_and_index( + 1, 1, endpoint=fake_shard + ) with pytest.raises(exceptions.RPCError): transaction.get_staking_transaction_error_sink(endpoint=fake_shard) with pytest.raises(exceptions.RPCError): - transaction.send_raw_staking_transaction('', endpoint=fake_shard) + transaction.send_raw_staking_transaction("", endpoint=fake_shard) with pytest.raises(exceptions.RPCError): transaction.get_pending_staking_transactions(endpoint=fake_shard) diff --git a/tests/sdk-pyhmy/test_validator.py b/tests/sdk-pyhmy/test_validator.py index f6269e8..21f33b7 100644 --- a/tests/sdk-pyhmy/test_validator.py +++ b/tests/sdk-pyhmy/test_validator.py @@ -1,19 +1,11 @@ import pytest -from decimal import ( - Decimal -) +from decimal import Decimal -from pyhmy import ( - validator -) +from pyhmy import validator -from pyhmy.numbers import ( - convert_one_to_atto -) +from pyhmy.numbers import convert_one_to_atto -from pyhmy.exceptions import ( - InvalidValidatorError -) +from pyhmy.exceptions import InvalidValidatorError test_epoch_number = 0 genesis_block_number = 0 @@ -21,33 +13,42 @@ test_block_number = 1 test_validator_object = None test_validator_loaded = False + def test_instantiate_validator(setup_blockchain): global test_validator_object - test_validator_object = validator.Validator('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') + test_validator_object = validator.Validator( + "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) assert isinstance(test_validator_object, validator.Validator) + def test_load_validator(setup_blockchain): if not test_validator_object: - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") info = { - 'name': 'Alice', - 'identity': 'alice', - 'website': 'alice.harmony.one', - 'details': "Don't mess with me!!!", - 'security-contact': 'Bob', - 'min-self-delegation': convert_one_to_atto(10000), - 'amount': convert_one_to_atto(10001), - 'max-rate': '0.9', - 'max-change-rate': '0.05', - 'rate': '0.01', - 'bls-public-keys': ['0x30b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202'], - 'bls-key-sigs': ['0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414'], - 'max-total-delegation': convert_one_to_atto(40000) + "name": "Alice", + "identity": "alice", + "website": "alice.harmony.one", + "details": "Don't mess with me!!!", + "security-contact": "Bob", + "min-self-delegation": convert_one_to_atto(10000), + "amount": convert_one_to_atto(10001), + "max-rate": "0.9", + "max-change-rate": "0.05", + "rate": "0.01", + "bls-public-keys": [ + "0x30b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202" + ], + "bls-key-sigs": [ + "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414" + ], + "max-total-delegation": convert_one_to_atto(40000), } test_validator_object.load(info) global test_validator_loaded test_validator_loaded = True + """ TypeScript signature source (is outdated because the JS SDK has not been updated for SlotKeySigs) For now I have checked that the below transaction to localnet works @@ -75,16 +76,23 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_create_validator_sign(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") signed_hash = test_validator_object.sign_create_validator_transaction( - 2, - int(convert_one_to_atto(1)), - 100, - '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', - 2).rawTransaction.hex() - assert signed_hash == '0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd' + 2, + int(convert_one_to_atto(1)), + 100, + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + 2, + ).rawTransaction.hex() + assert ( + signed_hash + == "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd" + ) + """ Signature matched from TypeScript (is outdated because the JS SDK has not been updated for SlotKeyToAddSig) @@ -127,39 +135,46 @@ const signed = stakingTx.rlpSign('4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd console.log( 'Signed transaction' ) console.log(signed) """ + + def test_edit_validator_sign(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") signed_hash = test_validator_object.sign_edit_validator_transaction( - 2, - int(convert_one_to_atto(1)), - 100, - '0.06', - '0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612', # remove key - "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # add key - "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414", # add key sig - '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', - 2).rawTransaction.hex() - assert signed_hash == '0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995' + 2, + int(convert_one_to_atto(1)), + 100, + "0.06", + "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612", # remove key + "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", # add key + "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414", # add key sig + "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", + 2, + ).rawTransaction.hex() + assert ( + signed_hash + == "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995" + ) + def test_invalid_validator(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') + pytest.skip("Validator not instantiated yet") with pytest.raises(InvalidValidatorError): info = { - 'name': 'Alice', + "name": "Alice", } test_validator_object.load(info) with pytest.raises(InvalidValidatorError): - test_validator_object.set_name('a'*141) + test_validator_object.set_name("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_identity('a'*141) + test_validator_object.set_identity("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_website('a'*141) + test_validator_object.set_website("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_security_contact('a'*141) + test_validator_object.set_security_contact("a" * 141) with pytest.raises(InvalidValidatorError): - test_validator_object.set_details('a'*281) + test_validator_object.set_details("a" * 281) with pytest.raises(InvalidValidatorError): test_validator_object.set_min_self_delegation(1) with pytest.raises(InvalidValidatorError): @@ -167,22 +182,26 @@ def test_invalid_validator(setup_blockchain): with pytest.raises(InvalidValidatorError): test_validator_object.set_amount(1) with pytest.raises(InvalidValidatorError): - test_validator_object.set_max_rate('2.0') + test_validator_object.set_max_rate("2.0") with pytest.raises(InvalidValidatorError): - test_validator_object.set_max_change_rate('-2.0') + test_validator_object.set_max_change_rate("-2.0") with pytest.raises(InvalidValidatorError): - test_validator_object.set_rate('-2.0') + test_validator_object.set_rate("-2.0") + def test_validator_getters(setup_blockchain): if not (test_validator_object or test_validator_loaded): - pytest.skip('Validator not instantiated yet') - assert test_validator_object.get_address() == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' - assert test_validator_object.add_bls_key('5') - assert test_validator_object.remove_bls_key('5') - assert test_validator_object.get_name() == 'Alice' - assert test_validator_object.get_identity() == 'alice' - assert test_validator_object.get_website() == 'alice.harmony.one' - assert test_validator_object.get_security_contact() == 'Bob' + pytest.skip("Validator not instantiated yet") + assert ( + test_validator_object.get_address() + == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) + assert test_validator_object.add_bls_key("5") + assert test_validator_object.remove_bls_key("5") + assert test_validator_object.get_name() == "Alice" + assert test_validator_object.get_identity() == "alice" + assert test_validator_object.get_website() == "alice.harmony.one" + assert test_validator_object.get_security_contact() == "Bob" assert test_validator_object.get_details() == "Don't mess with me!!!" assert isinstance(test_validator_object.get_min_self_delegation(), Decimal) assert isinstance(test_validator_object.get_max_total_delegation(), Decimal) @@ -192,6 +211,9 @@ def test_validator_getters(setup_blockchain): assert isinstance(test_validator_object.get_rate(), Decimal) assert len(test_validator_object.get_bls_keys()) > 0 + def test_validator_load_from_blockchain(setup_blockchain): - test_validator_object2 = validator.Validator('one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3') + test_validator_object2 = validator.Validator( + "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" + ) test_validator_object2.load_from_blockchain() diff --git a/tests/util-pyhmy/test_util.py b/tests/util-pyhmy/test_util.py index e278ef2..2988c47 100644 --- a/tests/util-pyhmy/test_util.py +++ b/tests/util-pyhmy/test_util.py @@ -19,41 +19,52 @@ def setup(): def test_json_load(): - dec = util.json_load('1.1', parse_float=decimal.Decimal) + dec = util.json_load("1.1", parse_float=decimal.Decimal) assert isinstance(dec, decimal.Decimal) assert float(dec) == 1.1 - ref_dict = { - 'test': 'val', - 'arr': [ - 1, - 2, - 3 - ] - } + ref_dict = {"test": "val", "arr": [1, 2, 3]} loaded_dict = util.json_load(json.dumps(ref_dict)) assert str(ref_dict) == str(loaded_dict) + def test_chain_id_to_int(): assert util.chain_id_to_int(2) == 2 - assert util.chain_id_to_int('HmyMainnet') == 1 + assert util.chain_id_to_int("HmyMainnet") == 1 + def test_get_gopath(): assert isinstance(util.get_gopath(), str) + def test_get_goversion(): assert isinstance(util.get_goversion(), str) + def test_convert_one_to_hex(): - assert util.convert_one_to_hex('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' - assert util.convert_one_to_hex('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == '0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58' + assert ( + util.convert_one_to_hex("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58") + == "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58" + ) + assert ( + util.convert_one_to_hex("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") + == "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58" + ) + def test_convert_hex_to_one(): - assert util.convert_hex_to_one('0xebcd16e8c1d8f493ba04e99a56474122d81a9c58') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' - assert util.convert_hex_to_one('one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') == 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9' - + assert ( + util.convert_hex_to_one("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58") + == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) + assert ( + util.convert_hex_to_one("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") + == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + ) + def test_get_bls_build_variables(): assert isinstance(util.get_bls_build_variables(), dict) + def test_is_active_shard(): - assert isinstance(util.is_active_shard(''), bool) + assert isinstance(util.is_active_shard(""), bool) From 98514c7a2cb4ac1c00f9621f1cd8718f941ea504 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:21:16 +0000 Subject: [PATCH 19/23] chore(lint): resolve `pylint` complaints --- pyhmy/__init__.py | 5 +- pyhmy/_version.py | 4 +- pyhmy/account.py | 121 +++++------ pyhmy/bech32/__init__.py | 1 - pyhmy/blockchain.py | 408 ++++++++++++++++-------------------- pyhmy/cli.py | 191 +++++++++-------- pyhmy/constants.py | 18 ++ pyhmy/contract.py | 88 ++++---- pyhmy/exceptions.py | 22 +- pyhmy/logging.py | 120 +++++------ pyhmy/numbers.py | 11 +- pyhmy/rpc/exceptions.py | 17 +- pyhmy/rpc/request.py | 20 +- pyhmy/signing.py | 93 ++++---- pyhmy/staking.py | 212 +++++++++---------- pyhmy/staking_signing.py | 105 +++++----- pyhmy/staking_structures.py | 52 +++-- pyhmy/transaction.py | 185 ++++++++-------- pyhmy/util.py | 71 +++---- pyhmy/validator.py | 238 +++++++++------------ 20 files changed, 958 insertions(+), 1024 deletions(-) create mode 100644 pyhmy/constants.py diff --git a/pyhmy/__init__.py b/pyhmy/__init__.py index ebdebee..17593b8 100644 --- a/pyhmy/__init__.py +++ b/pyhmy/__init__.py @@ -1,10 +1,11 @@ +""" +`pyhmy` for interacting with the Harmony blockchain +""" import sys import warnings from ._version import __version__ -from .util import Typgpy, get_gopath, get_goversion, get_bls_build_variables, json_load - if sys.version_info.major < 3: warnings.simplefilter("always", DeprecationWarning) warnings.warn( diff --git a/pyhmy/_version.py b/pyhmy/_version.py index dc1891a..8f7cbe4 100644 --- a/pyhmy/_version.py +++ b/pyhmy/_version.py @@ -1,6 +1,4 @@ -""" -Provides pyhmy version information. -""" +"""Provides pyhmy version information.""" # This file is auto-generated! Do not edit! # Use `python -m incremental.update pyhmy` to change this file. diff --git a/pyhmy/account.py b/pyhmy/account.py index 5c53b3b..9a4f869 100644 --- a/pyhmy/account.py +++ b/pyhmy/account.py @@ -1,3 +1,6 @@ +""" +Interact with accounts on the Harmony blockchain +""" from .rpc.request import rpc_request from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError @@ -8,10 +11,7 @@ from .blockchain import get_sharding_structure from .bech32.bech32 import bech32_decode -_default_endpoint = "http://localhost:9500" -_default_timeout = 30 -_address_length = 42 - +from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT def is_valid_address(address) -> bool: """ @@ -36,9 +36,8 @@ def is_valid_address(address) -> bool: return True -def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get current account balance +def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get current account balance. Parameters ---------- @@ -70,15 +69,14 @@ def get_balance(address, endpoint=_default_endpoint, timeout=_default_timeout) - method, params=params, endpoint=endpoint, timeout=timeout )["result"] return int(balance) # v2 returns the result as it is - except TypeError as e: # check will work if rpc returns None - raise InvalidRPCReplyError(method, endpoint) from e + except TypeError as exception: # check will work if rpc returns None + raise InvalidRPCReplyError(method, endpoint) from exception def get_balance_by_block( - address, block_num, endpoint=_default_endpoint, timeout=_default_timeout + address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get account balance for address at a given block number + """Get account balance for address at a given block number. Parameters ---------- @@ -113,15 +111,14 @@ def get_balance_by_block( method, params=params, endpoint=endpoint, timeout=timeout )["result"] return int(balance) - except TypeError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except TypeError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_account_nonce( - address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout + address, block_num="latest", endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get the account nonce + """Get the account nonce. Parameters ---------- @@ -155,26 +152,24 @@ def get_account_nonce( "result" ] return int(nonce) - except TypeError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except TypeError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_nonce( - address, block_num="latest", endpoint=_default_endpoint, timeout=_default_timeout + address, block_num="latest", endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - See get_account_nonce - """ + """See get_account_nonce.""" return get_account_nonce(address, block_num, endpoint, timeout) def get_transaction_count( - address, block_num, endpoint=_default_endpoint, timeout=_default_timeout + address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get the number of transactions the given address has sent for the given block number - Legacy for apiv1. For apiv2, please use get_account_nonce/get_transactions_count/get_staking_transactions_count apis for - more granular transaction counts queries + """Get the number of transactions the given address has sent for the given + block number Legacy for apiv1. For apiv2, please use + get_account_nonce/get_transactions_count/get_staking_transactions_count + apis for more granular transaction counts queries. Parameters ---------- @@ -208,15 +203,14 @@ def get_transaction_count( "result" ] return int(nonce) - except TypeError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except TypeError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_transactions_count( - address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout + address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get the number of regular transactions from genesis of input type + """Get the number of regular transactions from genesis of input type. Parameters ---------- @@ -252,15 +246,15 @@ def get_transactions_count( method, params=params, endpoint=endpoint, timeout=timeout )["result"] return int(tx_count) - except TypeError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except TypeError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_staking_transactions_count( - address, tx_type, endpoint=_default_endpoint, timeout=_default_timeout + address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL") + """Get the number of staking transactions from genesis of input type + ("SENT", "RECEIVED", "ALL") Parameters ---------- @@ -296,22 +290,21 @@ def get_staking_transactions_count( method, params=params, endpoint=endpoint, timeout=timeout )["result"] return int(tx_count) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_transaction_history( +def get_transaction_history( # pylint: disable=too-many-arguments address, page=0, page_size=1000, include_full_tx=False, tx_type="ALL", order="ASC", - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> list: - """ - Get list of transactions sent and/or received by the account + """Get list of transactions sent and/or received by the account. Parameters ---------- @@ -369,22 +362,21 @@ def get_transaction_history( method, params=params, endpoint=endpoint, timeout=timeout ) return tx_history["result"]["transactions"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_staking_transaction_history( +def get_staking_transaction_history( # pylint: disable=too-many-arguments address, page=0, page_size=1000, include_full_tx=False, tx_type="ALL", order="ASC", - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> list: - """ - Get list of staking transactions sent by the account + """Get list of staking transactions sent by the account. Parameters ---------- @@ -411,7 +403,8 @@ def get_staking_transaction_history( ------- list of transactions if include_full_tx is True, each transaction is a dictionary with the following kets - blockHash: :obj:`str` Block hash that transaction was finalized; "0x0000000000000000000000000000000000000000000000000000000000000000" if tx is pending + blockHash: :obj:`str` Block hash that transaction was finalized or + "0x0000000000000000000000000000000000000000000000000000000000000000" if tx is pending blockNumber: :obj:`int` Block number that transaction was finalized; None if tx is pending from: :obj:`str` Wallet address timestamp: :obj:`int` Timestamp in Unix time when transaction was finalized @@ -420,7 +413,8 @@ def get_staking_transaction_history( hash: :obj:`str` Transaction hash nonce: :obj:`int` Wallet nonce for the transaction transactionIndex: :obj:`int` Index of transaction in block; None if tx is pending - type: :obj:`str` Type of staking transaction, for example, "CollectRewards", "Delegate", "Undelegate" + type: :obj:`str` Type of staking transaction + for example, "CollectRewards", "Delegate", "Undelegate" msg: :obj:`dict` Message attached to the staking transaction r: :obj:`str` First 32 bytes of the transaction signature s: :obj:`str` Next 32 bytes of the transaction signature @@ -454,15 +448,15 @@ def get_staking_transaction_history( method, params=params, endpoint=endpoint, timeout=timeout )["result"] return stx_history["staking_transactions"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_balance_on_all_shards( - address, skip_error=True, endpoint=_default_endpoint, timeout=_default_timeout + address, skip_error=True, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get current account balance in all shards & optionally report errors getting account balance for a shard + """Get current account balance in all shards & optionally report errors + getting account balance for a shard. Parameters ---------- @@ -507,10 +501,9 @@ def get_balance_on_all_shards( def get_total_balance( - address, endpoint=_default_endpoint, timeout=_default_timeout + address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get total account balance on all shards + """Get total account balance on all shards. Parameters ---------- @@ -540,5 +533,5 @@ def get_total_balance( address, skip_error=False, endpoint=endpoint, timeout=timeout ) return sum(b["balance"] for b in balances) - except TypeError as e: - raise RuntimeError from e + except TypeError as exception: + raise RuntimeError from exception diff --git a/pyhmy/bech32/__init__.py b/pyhmy/bech32/__init__.py index 8b13789..e69de29 100644 --- a/pyhmy/bech32/__init__.py +++ b/pyhmy/bech32/__init__.py @@ -1 +0,0 @@ - diff --git a/pyhmy/blockchain.py b/pyhmy/blockchain.py index 9962c14..05f43cd 100644 --- a/pyhmy/blockchain.py +++ b/pyhmy/blockchain.py @@ -1,17 +1,20 @@ +""" +Interact with the Harmony blockchain to fetch +blocks, headers, transaction pool, node status, etc. +""" +# pylint: disable=too-many-lines from .rpc.request import rpc_request from .exceptions import InvalidRPCReplyError -_default_endpoint = "http://localhost:9500" -_default_timeout = 30 +from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT ############################# # Node / network level RPCs # ############################# -def get_bad_blocks(endpoint=_default_endpoint, timeout=_default_timeout) -> list: - """ - [WIP] Get list of bad blocks in memory of specific node - Known issues with RPC not returning correctly +def get_bad_blocks(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list: + """[WIP] Get list of bad blocks in memory of specific node Known issues + with RPC not returning correctly. Parameters ---------- @@ -36,13 +39,12 @@ def get_bad_blocks(endpoint=_default_endpoint, timeout=_default_timeout) -> list method = "hmyv2_getCurrentBadBlocks" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: - """ - Chain id of the chain +def chain_id(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: + """Chain id of the chain. Parameters ---------- @@ -68,13 +70,12 @@ def chain_id(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: try: data = rpc_request(method, endpoint=endpoint, timeout=timeout) return data["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: - """ - Get config for the node +def get_node_metadata(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: + """Get config for the node. Parameters ---------- @@ -89,25 +90,8 @@ def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> d blskey: :obj:`list` of BLS keys on the node version: :obj:`str` representing the Harmony binary version network: :obj:`str` the Network name that the node is on (Mainnet or Testnet) - chain-config: :obj:`dict` with the following keys (more are added over time): - chain-id: :obj:`int` Chain ID of the network - cross-tx-epoch: :obj:`int` Epoch at which cross shard transactions were enabled - cross-link-epoch: :obj:`int` Epoch at which cross links were enabled - staking-epoch: :obj:`int` Epoch at which staking was enabled - prestaking-epoch: :obj:`int` Epoch at which staking features without election were allowed - quick-unlock-epoch: :obj:`int` Epoch at which undelegations unlocked in one epoch - eip155-epoch: :obj:`int` Epoch at with EIP155 was enabled - s3-epoch: :obj:`int` Epoch at which Mainnet V0 was launched - receipt-log-epoch: :obj:`int` Epoch at which receipt logs were enabled - eth-compatible-chain-id: :obj:`int` EVM network compatible chain ID - eth-compatible-epoch: :obj:`int` Epoch at which EVM compatibility was launched - eth-compatible-shard-0-chain-id: :obj:`int` EVM network compatible chain ID on shard 0 - five-seconds-epoch: :obj:`int` Epoch at which five second finality was enabled and block rewards adjusted to 17.5 ONE/block - istanbul-epoch: :obj:`int` Epoch at which Ethereum's Istanbul upgrade was added to Harmony - no-early-unlock-epoch: :obj:`int` Epoch at which early unlock of tokens was disabled (https://github.com/harmony-one/harmony/pull/3605) - redelegation-epoch: :obj:`int` Epoch at which redelegation was enabled (staking) - sixty-percent-epoch: :obj:`int` Epoch when internal voting power reduced from 68% to 60% - two-seconds-epoch: :obj:`int` Epoch at which two second finality was enabled and block rewards adjusted to 7 ONE/block + chain-config: :obj:`dict` with the hard fork epochs list, and `chain-id` + both as :obj:`int` is-leader: :obj:`bool` Whether the node is currently leader or not shard-id: :obj:`int` Shard that the node is on current-epoch: :obj:`int` Current epoch @@ -138,20 +122,19 @@ def get_node_metadata(endpoint=_default_endpoint, timeout=_default_timeout) -> d API Reference ------------- https://api.hmny.io/#03c39b56-8dfc-48ce-bdad-f85776dd8aec - https://github.com/harmony-one/harmony/blob/v1.10.2/internal/params/config.go#L233 for chain-config dict - https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/node/api.go#L110 for consensus dict + https://github.com/harmony-one/harmony/blob/v1.10.2/internal/params/config.go#L233 + https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/node/api.go#L110 """ method = "hmyv2_getNodeMetadata" try: metadata = rpc_request(method, endpoint=endpoint, timeout=timeout) return metadata["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: - """ - Get peer info for the node +def get_peer_info(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: + """Get peer info for the node. Parameters ---------- @@ -183,13 +166,12 @@ def get_peer_info(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: method = "hmyv2_getPeerInfo" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get the current Harmony protocol version this node supports +def protocol_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get the current Harmony protocol version this node supports. Parameters ---------- @@ -216,13 +198,12 @@ def protocol_version(endpoint=_default_endpoint, timeout=_default_timeout) -> in try: value = rpc_request(method, endpoint=endpoint, timeout=timeout) return value["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get number of peers connected to the node +def get_num_peers(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get number of peers connected to the node. Parameters ---------- @@ -250,13 +231,12 @@ def get_num_peers(endpoint=_default_endpoint, timeout=_default_timeout) -> int: return int( rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get version of the EVM network (https://chainid.network/) +def get_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get version of the EVM network (https://chainid.network/) Parameters ---------- @@ -284,13 +264,12 @@ def get_version(endpoint=_default_endpoint, timeout=_default_timeout) -> int: return int( rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 ) # this is hexadecimal - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: - """ - Whether the shard chain is in sync or syncing (not out of sync) +def in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool: + """Whether the shard chain is in sync or syncing (not out of sync) Parameters ---------- @@ -315,13 +294,12 @@ def in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: method = "hmyv2_inSync" try: return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool: - """ - Whether the beacon chain is in sync or syncing (not out of sync) +def beacon_in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool: + """Whether the beacon chain is in sync or syncing (not out of sync) Parameters ---------- @@ -346,13 +324,12 @@ def beacon_in_sync(endpoint=_default_endpoint, timeout=_default_timeout) -> bool method = "hmyv2_beaconInSync" try: return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get epoch number when blockchain switches to EPoS election +def get_staking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get epoch number when blockchain switches to EPoS election. Parameters ---------- @@ -383,13 +360,13 @@ def get_staking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i try: data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return int(data["chain-config"]["staking-epoch"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get epoch number when blockchain switches to allow staking features without election +def get_prestaking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get epoch number when blockchain switches to allow staking features + without election. Parameters ---------- @@ -420,16 +397,15 @@ def get_prestaking_epoch(endpoint=_default_endpoint, timeout=_default_timeout) - try: data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] return int(data["chain-config"]["prestaking-epoch"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception ######################## # Sharding information # ######################## -def get_shard(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get shard ID of the node +def get_shard(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get shard ID of the node. Parameters ---------- @@ -457,15 +433,14 @@ def get_shard(endpoint=_default_endpoint, timeout=_default_timeout) -> int: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"][ "shard-id" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_sharding_structure( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get network sharding structure + """Get network sharding structure. Parameters ---------- @@ -494,16 +469,15 @@ def get_sharding_structure( method = "hmyv2_getShardingStructure" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception ############################# # Current status of network # ############################# -def get_leader_address(endpoint=_default_endpoint, timeout=_default_timeout) -> str: - """ - Get current leader one address +def get_leader_address(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> str: + """Get current leader one address. Parameters ---------- @@ -529,15 +503,14 @@ def get_leader_address(endpoint=_default_endpoint, timeout=_default_timeout) -> method = "hmyv2_getLeader" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def is_last_block( - block_num, endpoint=_default_endpoint, timeout=_default_timeout + block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> bool: - """ - If the block at block_num is the last block + """If the block at block_num is the last block. Parameters ---------- @@ -571,15 +544,14 @@ def is_last_block( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def epoch_last_block( - epoch, endpoint=_default_endpoint, timeout=_default_timeout + epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Returns the number of the last block in the epoch + """Returns the number of the last block in the epoch. Parameters ---------- @@ -613,13 +585,12 @@ def epoch_last_block( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get current circulation supply of tokens in ONE +def get_circulating_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get current circulation supply of tokens in ONE. Parameters ---------- @@ -645,13 +616,12 @@ def get_circulating_supply(endpoint=_default_endpoint, timeout=_default_timeout) method = "hmyv2_getCirculatingSupply" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get total number of pre-mined tokens +def get_total_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get total number of pre-mined tokens. Parameters ---------- @@ -677,13 +647,12 @@ def get_total_supply(endpoint=_default_endpoint, timeout=_default_timeout) -> in method = "hmyv2_getTotalSupply" try: rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get current block number +def get_block_number(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get current block number. Parameters ---------- @@ -709,13 +678,12 @@ def get_block_number(endpoint=_default_endpoint, timeout=_default_timeout) -> in method = "hmyv2_blockNumber" try: return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get current epoch number +def get_current_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get current epoch number. Parameters ---------- @@ -741,13 +709,12 @@ def get_current_epoch(endpoint=_default_endpoint, timeout=_default_timeout) -> i method = "hmyv2_getEpoch" try: return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) -> list: - """ - Get last cross shard links +def get_last_cross_links(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list: + """Get last cross shard links. Parameters ---------- @@ -780,13 +747,12 @@ def get_last_cross_links(endpoint=_default_endpoint, timeout=_default_timeout) - method = "hmyv2_getLastCrossLinks" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get network gas price +def get_gas_price(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get network gas price. Parameters ---------- @@ -812,16 +778,15 @@ def get_gas_price(endpoint=_default_endpoint, timeout=_default_timeout) -> int: method = "hmyv2_gasPrice" try: return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception ############## # Block RPCs # ############## -def get_latest_header(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: - """ - Get block header of latest block +def get_latest_header(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: + """Get block header of latest block. Parameters ---------- @@ -836,14 +801,16 @@ def get_latest_header(endpoint=_default_endpoint, timeout=_default_timeout) -> d blockHash: :obj:`str` Block hash blockNumber: :obj:`int` Block number shardID: :obj:`int` Shard ID - leader: :obj:`str` Wallet address of leader that proposed this block if prestaking, otherwise sha256 hash of leader's public bls key + leader: :obj:`str` Wallet address of leader that proposed this block if prestaking + otherwise sha256 hash of leader's public bls key viewID: :obj:`int` View ID of the block epoch: :obj:`int` Epoch of block timestamp: :obj:`str` Timestamp that the block was finalized in human readable format unixtime: :obj:`int` Timestamp that the block was finalized in Unix time lastCommitSig: :obj:`str` Hex representation of aggregated signatures of the previous block - lastCommitBitmap: :obj:`str` Hex representation of aggregated signature bitmap of the previous block - crossLinks: list of dicts describing the cross shard links, each dict to have the following keys: + lastCommitBitmap: :obj:`str` + Hex representation of aggregated signature bitmap of the previous block + crossLinks:list of dicts describing the cross shard links: block-number: :obj:`int` Number of the cross link block epoch-number: :obj:`int` Epoch of the cross link block hash: :obj:`str` Hash of the cross link block @@ -863,15 +830,14 @@ def get_latest_header(endpoint=_default_endpoint, timeout=_default_timeout) -> d method = "hmyv2_latestHeader" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_header_by_number( - block_num, endpoint=_default_endpoint, timeout=_default_timeout + block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get block header of block at block_num + """Get block header of block at block_num. Parameters ---------- @@ -901,15 +867,14 @@ def get_header_by_number( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_latest_chain_headers( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get block header of latest block for beacon chain & shard chain + """Get block header of latest block for beacon chain & shard chain. Parameters ---------- @@ -921,7 +886,7 @@ def get_latest_chain_headers( Returns ------- dict with two keys: - beacon-chain-header: :obj:`dict` with the following keys, applicable to the beacon chain (cross shard links) + beacon-chain-header: :obj:`dict` with the following keys, applicable to the beacon chain shard-chain-header: :obj:`dict` with the following keys, applicable to the shard chain difficulty: legacy epoch: :obj:`int` Epoch of the block @@ -955,21 +920,20 @@ def get_latest_chain_headers( method = "hmyv2_getLatestChainHeaders" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_block_by_number( +def get_block_by_number( # pylint: disable=too-many-arguments block_num, full_tx=False, include_tx=False, include_staking_tx=False, include_signers=False, - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> dict: - """ - Get block by number + """Get block by number. Parameters ---------- @@ -1007,12 +971,14 @@ def get_block_by_number( signers: :obj:`list` List of signers (only if include_signers is set to True) size: :obj:`int` Block size in bytes stakingTransactions: :obj:`list` - if full_tx is True: List of dictionaries, each containing a staking transaction (see account.get_staking_transaction_history) + if full_tx is True: List of dictionaries, + each containing a staking transaction (see account.get_staking_transaction_history) if full_tx is False: List of staking transaction hashes stateRoot: :obj:`str` Hash of state root timestamp: :obj:`int` Unix timestamp of the block transactions: :obj:`list` - if full_tx is True: List of dictionaries, each containing a transaction (see account.get_transaction_history) + if full_tx is True: List of dictionaries, + each containing a transaction (see account.get_transaction_history) if full_tx is False: List of transaction hashes transactionsRoot: :obj:`str` Hash of transactions root uncles: :obj:`str` legacy @@ -1042,21 +1008,20 @@ def get_block_by_number( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_block_by_hash( +def get_block_by_hash( # pylint: disable=too-many-arguments block_hash, full_tx=False, include_tx=False, include_staking_tx=False, include_signers=False, - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> dict: - """ - Get block by hash + """Get block by hash. Parameters ---------- @@ -1100,15 +1065,14 @@ def get_block_by_hash( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_block_transaction_count_by_number( - block_num, endpoint=_default_endpoint, timeout=_default_timeout + block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get transaction count for specific block number + """Get transaction count for specific block number. Parameters ---------- @@ -1143,15 +1107,14 @@ def get_block_transaction_count_by_number( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_block_transaction_count_by_hash( - block_hash, endpoint=_default_endpoint, timeout=_default_timeout + block_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get transaction count for specific block hash + """Get transaction count for specific block hash. Parameters ---------- @@ -1186,15 +1149,14 @@ def get_block_transaction_count_by_hash( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_block_staking_transaction_count_by_number( - block_num, endpoint=_default_endpoint, timeout=_default_timeout + block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get staking transaction count for specific block number + """Get staking transaction count for specific block number. Parameters ---------- @@ -1229,15 +1191,14 @@ def get_block_staking_transaction_count_by_number( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_block_staking_transaction_count_by_hash( - block_hash, endpoint=_default_endpoint, timeout=_default_timeout + block_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get staking transaction count for specific block hash + """Get staking transaction count for specific block hash. Parameters ---------- @@ -1272,22 +1233,21 @@ def get_block_staking_transaction_count_by_hash( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_blocks( +def get_blocks( # pylint: disable=too-many-arguments start_block, end_block, full_tx=False, include_tx=False, include_staking_tx=False, include_signers=False, - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> list: - """ - Get list of blocks from a range + """Get list of blocks from a range. Parameters ---------- @@ -1336,15 +1296,14 @@ def get_blocks( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_block_signers( - block_num, endpoint=_default_endpoint, timeout=_default_timeout + block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of block signers for specific block number + """Get list of block signers for specific block number. Parameters ---------- @@ -1375,15 +1334,14 @@ def get_block_signers( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_block_signers_keys( - block_num, endpoint=_default_endpoint, timeout=_default_timeout + block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of block signer public bls keys for specific block number + """Get list of block signer public bls keys for specific block number. Parameters ---------- @@ -1414,15 +1372,15 @@ def get_block_signers_keys( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def is_block_signer( - block_num, address, endpoint=_default_endpoint, timeout=_default_timeout + block_num, address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> bool: - """ - Determine if the account at address is a signer for the block at block_num + """Determine if the account at address is a signer for the block at + block_num. Parameters ---------- @@ -1454,15 +1412,15 @@ def is_block_signer( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_signed_blocks( - address, endpoint=_default_endpoint, timeout=_default_timeout + address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> bool: - """ - The number of blocks a particular validator signed for last blocksPeriod (1 epoch) + """The number of blocks a particular validator signed for last blocksPeriod + (1 epoch) Parameters ---------- @@ -1494,13 +1452,12 @@ def get_signed_blocks( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> dict: - """ - Get list of validators for specific epoch number +def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: + """Get list of validators for specific epoch number. Parameters ---------- @@ -1534,15 +1491,14 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_validator_keys( - epoch, endpoint=_default_endpoint, timeout=_default_timeout + epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of validator public bls keys for specific epoch number + """Get list of validator public bls keys for specific epoch number. Parameters ---------- @@ -1573,5 +1529,5 @@ def get_validator_keys( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception diff --git a/pyhmy/cli.py b/pyhmy/cli.py index fd96890..55adae8 100644 --- a/pyhmy/cli.py +++ b/pyhmy/cli.py @@ -7,7 +7,8 @@ Example: Below is a demo of how to import, manage keys, and interact with the CLI:: >>> from pyhmy import cli >>> cli.single_call("hmy keys add test1") - '**Important** write this seed phrase in a safe place, it is the only way to recover your account if you ever forget your password + '**Important** write this seed phrase in a safe place, + it is the only way to recover your account if you ever forget your password craft ... tobacco' >>> cli.get_accounts_keystore() {'test1': 'one1aqfeed538xf7n0cfh60tjaeat7yw333pmj6sfu'} @@ -15,7 +16,7 @@ Example: >>> cli.get_accounts(check_addr) ['test1'] >>> cli.single_call("hmy keys list", timeout=2) - 'NAME \t\t ADDRESS\n\ntest1 \tone1aqfeed538xf7n0cfh60tjaeat7yw333pmj6sfu\n' + 'NAME \t\t ADDRESS\n\ntest1 \tone1aqfeed538xf7n0cfh60tjaeat7yw333pmj6sfu\n' >>> cli.get_accounts_keystore() {} @@ -40,14 +41,13 @@ For more details, reference the documentation here: TODO gitbook docs """ import subprocess -import pexpect import os import shutil import re import stat import sys -from multiprocessing import Lock from pathlib import Path +import pexpect import requests @@ -69,44 +69,61 @@ else: "libgmpxx.4.dylib", "libmcl.dylib", } -_accounts = {} # Internal accounts keystore, make sure to sync when needed. -_account_keystore_path = "~/.hmy/account-keys" # Internal path to account keystore, will match the current binary. -_binary_path = "hmy" # Internal binary path. -_arg_prefix = "__PYHMY_ARG_PREFIX__" -_keystore_cache_lock = Lock() +# Internal accounts keystore, make sure to sync when needed. +_accounts = {} +# Internal path to account keystore, will match the current binary. +ARG_PREFIX = "__PYHMY_ARG_PREFIX__" +# _keystore_cache_lock = Lock() environment = os.environ.copy() # The environment for the CLI to execute in. -# TODO: completely remove caching... we need to improve getting address better internally to REDUCE single calls.... -def _cache_and_lock_accounts_keystore(fn): - """ - Internal decorator to cache the accounts keystore and - prevent concurrent accesses with locks. - """ - cached_accounts = {} - last_mod = None +# completely remove caching... +# we need to improve getting address better internally to REDUCE single calls.... +# def _cache_and_lock_accounts_keystore(fn): +# """Internal decorator to cache the accounts keystore and prevent concurrent +# accesses with locks.""" +# cached_accounts = {} +# last_mod = None - def wrap(*args): - nonlocal last_mod - _keystore_cache_lock.acquire() - files_in_dir = str(os.listdir(_account_keystore_path)) - dir_mod_time = str(os.path.getmtime(_account_keystore_path)) - curr_mod = hash(files_in_dir + dir_mod_time + _binary_path) - if curr_mod != last_mod: - cached_accounts.clear() - cached_accounts.update(fn(*args)) - last_mod = curr_mod - accounts = cached_accounts.copy() - _keystore_cache_lock.release() - return accounts +# def wrap(*args): +# nonlocal last_mod +# _keystore_cache_lock.acquire() +# files_in_dir = str(os.listdir(ACCOUNT_KEYSTORE_PATH)) +# dir_mod_time = str(os.path.getmtime(ACCOUNT_KEYSTORE_PATH)) +# curr_mod = hash(files_in_dir + dir_mod_time + BINARY_PATH) +# if curr_mod != last_mod: +# cached_accounts.clear() +# cached_accounts.update(fn(*args)) +# last_mod = curr_mod +# accounts = cached_accounts.copy() +# _keystore_cache_lock.release() +# return accounts - return wrap +# return wrap +def account_keystore_path(value=None): + """ + Gets or sets the ACCOUNT_KEYSTORE_PATH + """ + if "value" not in account_keystore_path.__dict__: + account_keystore_path.value = "~/.hmy/account-keys" + if value: + account_keystore_path.value = value + return account_keystore_path.value -def _get_current_accounts_keystore(): +def binary_path(value=None): """ - Internal function that gets the current keystore from the CLI. + Gets or sets the BINARY_PATH + """ + if "value" not in binary_path.__dict__: + binary_path.value = "hmy" + if value: + binary_path.value = value + return binary_path.value + +def _get_current_accounts_keystore(): + """Internal function that gets the current keystore from the CLI. :returns A dictionary where the keys are the account names/aliases and the values are their 'one1...' addresses. @@ -128,32 +145,28 @@ def _get_current_accounts_keystore(): def _set_account_keystore_path(): - """ - Internal function to set the account keystore path according to the binary. - """ - global _account_keystore_path + """Internal function to set the account keystore path according to the + binary.""" response = single_call("hmy keys location").strip() if not os.path.exists(response): os.mkdir(response) - _account_keystore_path = response + account_keystore_path(response) def _sync_accounts(): - """ - Internal function that UPDATES the accounts keystore with the CLI's keystore. - """ + """Internal function that UPDATES the accounts keystore with the CLI's + keystore.""" new_keystore = _get_current_accounts_keystore() - for key in new_keystore.keys(): - if key not in _accounts.keys(): - _accounts[key] = new_keystore[key] - acc_keys_to_remove = [k for k in _accounts.keys() if k not in new_keystore.keys()] + for key, value in new_keystore.items(): + if key not in _accounts: + _accounts[key] = value + acc_keys_to_remove = [k for k in _accounts if k not in new_keystore] for key in acc_keys_to_remove: del _accounts[key] def _make_call_command(command): - """ - Internal function that processes a command String or String Arg List for + """Internal function that processes a command String or String Arg List for underlying pexpect or subprocess call. Note that single quote is not respected for strings. @@ -162,18 +175,20 @@ def _make_call_command(command): command_toks = command else: all_strings = sorted( - re.findall(r'"(.*?)"', command), key=lambda e: len(e), reverse=True + re.findall(r'"(.*?)"', command), + key=lambda e: len(e), # pylint: disable=unnecessary-lambda + reverse=True ) for i, string in enumerate(all_strings): - command = command.replace(string, f"{_arg_prefix}_{i}") + command = command.replace(string, f"{ARG_PREFIX}_{i}") command_toks_prefix = [el for el in command.split(" ") if el] command_toks = [] - for el in command_toks_prefix: - if el.startswith(f'"{_arg_prefix}_') and el.endswith(f'"'): - index = int(el.replace(f'"{_arg_prefix}_', "").replace('"', "")) + for element in command_toks_prefix: + if element.startswith(f'"{ARG_PREFIX}_') and element.endswith('"'): + index = int(element.replace(f'"{ARG_PREFIX}_', "").replace('"', "")) command_toks.append(all_strings[index]) else: - command_toks.append(el) + command_toks.append(element) if re.match(".*hmy", command_toks[0]): command_toks = command_toks[1:] return command_toks @@ -197,16 +212,16 @@ def is_valid_binary(path): path = os.path.realpath(path) os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) try: - proc = subprocess.Popen( + with subprocess.Popen( [path, "version"], env=environment, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - ) - out, err = proc.communicate() - if not err: - return False - return "harmony" in err.decode().strip().lower() + ) as proc: + _, err = proc.communicate() + if not err: + return False + return "harmony" in err.decode().strip().lower() except (OSError, subprocess.CalledProcessError, subprocess.SubprocessError): return False @@ -218,13 +233,12 @@ def set_binary(path): Note that the exposed keystore will be updated accordingly. """ - global _binary_path path = os.path.realpath(path) assert os.path.exists(path) os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) if not is_valid_binary(path): return False - _binary_path = path + binary_path(path) _set_account_keystore_path() _sync_accounts() return True @@ -234,33 +248,33 @@ def get_binary_path(): """ :return: The absolute path of the CLI binary. """ - return os.path.abspath(_binary_path) + return os.path.abspath(binary_path()) def get_version(): """ :return: The version string of the CLI binary. """ - proc = subprocess.Popen( - [_binary_path, "version"], + with subprocess.Popen( + [binary_path(), "version"], env=environment, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - ) - out, err = proc.communicate() - if not err: - raise RuntimeError( - f"Could not get version.\n" - f"\tGot exit code {proc.returncode}. Expected non-empty error message." - ) - return err.decode().strip() + ) as proc: + _, err = proc.communicate() + if not err: + raise RuntimeError( + f"Could not get version.\n" + f"\tGot exit code {proc.returncode}. Expected non-empty error message." + ) + return err.decode().strip() def get_account_keystore_path(): """ :return: The absolute path to the account keystore of the CLI binary. """ - return os.path.abspath(_account_keystore_path) + return os.path.abspath(account_keystore_path()) def check_address(address): @@ -291,8 +305,7 @@ def get_accounts(address): def remove_account(name): - """ - Note that this edits the keystore directly since there is currently no + """Note that this edits the keystore directly since there is currently no way to remove an address using the CLI. :param name: The alias of a key used in the CLI's keystore. @@ -327,7 +340,7 @@ def single_call(command, timeout=60, error_ok=False): :returns: Decoded string of response from hmy CLI call :raises: RuntimeError if bad command """ - command_toks = [_binary_path] + _make_call_command(command) + command_toks = [binary_path()] + _make_call_command(command) try: return subprocess.check_output( command_toks, env=environment, timeout=timeout @@ -350,7 +363,7 @@ def expect_call(command, timeout=60): command_toks = _make_call_command(command) try: proc = pexpect.spawn( - f"{_binary_path}", command_toks, env=environment, timeout=timeout + f"{binary_path()}", command_toks, env=environment, timeout=timeout ) proc.delaybeforesend = None except pexpect.ExceptionPexpect as err: @@ -361,9 +374,8 @@ def expect_call(command, timeout=60): def download(path="./bin/hmy", replace=True, verbose=True): - """ - Download the CLI binary to the specified path. - Related files will be saved in the same directory. + """Download the CLI binary to the specified path. Related files will be + saved in the same directory. :param path: The desired path (absolute or relative) of the saved binary. :param replace: A flag to force a replacement of the binary/file. @@ -381,8 +393,8 @@ def download(path="./bin/hmy", replace=True, verbose=True): os.makedirs(parent_dir, exist_ok=True) os.chdir(parent_dir) hmy_script_path = os.path.join(parent_dir, "hmy.sh") - with open(hmy_script_path, "w") as f: - f.write( + with open(hmy_script_path, "w", encoding='utf8') as script_file: + script_file.write( requests.get( "https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh" ).content.decode() @@ -399,11 +411,12 @@ def download(path="./bin/hmy", replace=True, verbose=True): if verbose: subprocess.call([hmy_script_path, "-d"]) else: - subprocess.call( - [hmy_script_path, "-d"], - stdout=open(os.devnull, "w"), - stderr=subprocess.STDOUT, - ) + with open(os.devnull, "w", encoding = "UTF-8") as devnull: + subprocess.call( + [hmy_script_path, "-d"], + stdout=devnull, + stderr=subprocess.STDOUT, + ) os.rename(os.path.join(parent_dir, "hmy"), path) if same_name_file: os.rename( @@ -427,8 +440,8 @@ def download(path="./bin/hmy", replace=True, verbose=True): raise RuntimeWarning( f"Could not get environment for downloaded hmy CLI at `{path}`" ) - except Exception as e: + except Exception as exception: raise RuntimeWarning( f"Could not get environment for downloaded hmy CLI at `{path}`" - ) from e + ) from exception return env diff --git a/pyhmy/constants.py b/pyhmy/constants.py new file mode 100644 index 0000000..3bd545e --- /dev/null +++ b/pyhmy/constants.py @@ -0,0 +1,18 @@ +""" +Constants +""" + +# localnet constants +DEFAULT_ENDPOINT = 'http://localhost:9500' +DEFAULT_TIMEOUT = 30 + +# staking percentage constants +PRECISION = 18 +MAX_DECIMAL = 1000000000000000000 + +NAME_CHAR_LIMIT = 140 +IDENTITY_CHAR_LIMIT = 140 +WEBSITE_CHAR_LIMIT = 140 +SECURITY_CONTACT_CHAR_LIMIT = 140 +DETAILS_CHAR_LIMIT = 280 +MIN_REQUIRED_DELEGATION = int(10000 * 1e18) diff --git a/pyhmy/contract.py b/pyhmy/contract.py index abcfa5a..b5849cc 100644 --- a/pyhmy/contract.py +++ b/pyhmy/contract.py @@ -1,34 +1,36 @@ +""" +Basic smart contract functions on Harmony +For full ABI driven interaction, use something like web3py or brownie +""" + from .rpc.request import rpc_request from .transaction import get_transaction_receipt from .exceptions import InvalidRPCReplyError - -_default_endpoint = "http://localhost:9500" -_default_timeout = 30 +from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT ######################### # Smart contract RPCs ######################### -def call( - to, +def call( # pylint: disable=too-many-arguments + to_address, block_num, from_address=None, gas=None, gas_price=None, value=None, data=None, - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> str: - """ - Execute a smart contract without saving state + """Execute a smart contract without saving state. Parameters ---------- - to: :obj:`str` + to_address: :obj:`str` Address of the smart contract block_num: :obj:`int` Block number to execute the contract for @@ -55,7 +57,7 @@ def call( Raises ------ InvalidRPCReplyError - If received unknown result from endpoint, or + If received unknown result from exceptionndpoint, or API Reference ------------- @@ -63,7 +65,7 @@ def call( """ params = [ { - "to": to, + "to": to_address, "from": from_address, "gas": gas, "gasPrice": gas_price, @@ -77,26 +79,25 @@ def call( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def estimate_gas( - to, +def estimate_gas( # pylint: disable=too-many-arguments + to_address, from_address=None, gas=None, gas_price=None, value=None, data=None, - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> int: - """ - Estimate the gas price needed for a smart contract call + """Estimate the gas price needed for a smart contract call. Parameters ---------- - to: :obj:`str` + to_address: :obj:`str` Address of the smart contract from_address: :obj:`str`, optional Wallet address @@ -121,7 +122,7 @@ def estimate_gas( Raises ------ InvalidRPCReplyError - If received unknown result from endpoint, or + If received unknown result from exceptionndpoint, or API Reference ------------- @@ -129,7 +130,7 @@ def estimate_gas( """ params = [ { - "to": to, + "to": to_address, "from": from_address, "gas": gas, "gasPrice": gas_price, @@ -145,15 +146,15 @@ def estimate_gas( ], 16, ) - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_code( - address, block_num, endpoint=_default_endpoint, timeout=_default_timeout + address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> str: - """ - Get the code stored at the given address in the state for the given block number + """Get the code stored at the given address in the state for the given + block number. Parameters ---------- @@ -174,7 +175,7 @@ def get_code( Raises ------ InvalidRPCReplyError - If received unknown result from endpoint, or + If received unknown result from exceptionndpoint, or API Reference ------------- @@ -187,15 +188,15 @@ def get_code( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_storage_at( - address, key, block_num, endpoint=_default_endpoint, timeout=_default_timeout + address, key, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> str: - """ - Get the storage from the state at the given address, the key and the block number + """Get the storage from the state at the given address, the key and the + block number. Parameters ---------- @@ -218,7 +219,7 @@ def get_storage_at( Raises ------ InvalidRPCReplyError - If received unknown result from endpoint, or + If received unknown result from exceptionndpoint, or API Reference ------------- @@ -231,16 +232,15 @@ def get_storage_at( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_contract_address_from_hash( - tx_hash, endpoint=_default_endpoint, timeout=_default_timeout + tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> str: - """ - Get address of the contract which was deployed in the transaction - represented by tx_hash + """Get address of the contract which was deployed in the transaction + represented by tx_hash. Parameters ---------- @@ -259,7 +259,7 @@ def get_contract_address_from_hash( Raises ------ InvalidRPCReplyError - If received unknown result from endpoint, or + If received unknown result from exceptionndpoint, or API Reference ------------- @@ -267,5 +267,5 @@ def get_contract_address_from_hash( """ try: return get_transaction_receipt(tx_hash, endpoint, timeout)["contractAddress"] - except KeyError as e: - raise InvalidRPCReplyError("hmyv2_getTransactionReceipt", endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError("hmyv2_getTransactionReceipt", endpoint) from exception diff --git a/pyhmy/exceptions.py b/pyhmy/exceptions.py index 91a6882..b8a5fb1 100644 --- a/pyhmy/exceptions.py +++ b/pyhmy/exceptions.py @@ -1,20 +1,18 @@ -from .rpc.exceptions import RPCError, RequestsError, RequestsTimeoutError - +""" +Exceptions used by pyhmy +""" class InvalidRPCReplyError(RuntimeError): - """ - Exception raised when RPC call returns unexpected result - Generally indicates Harmony API has been updated & pyhmy library needs to be updated as well - """ + """Exception raised when RPC call returns unexpected result Generally + indicates Harmony API has been updated & pyhmy library needs to be updated + as well.""" def __init__(self, method, endpoint): super().__init__(f"Unexpected reply for {method} from {endpoint}") class InvalidValidatorError(ValueError): - """ - Exception raised Validator does not pass sanity checks - """ + """Exception raised Validator does not pass sanity checks.""" errors = { 1: "Invalid ONE address", @@ -34,10 +32,8 @@ class InvalidValidatorError(ValueError): class TxConfirmationTimedoutError(AssertionError): - """ - Exception raised when a transaction is sent to the chain - But not confirmed during the timeout period specified - """ + """Exception raised when a transaction is sent to the chain But not + confirmed during the timeout period specified.""" def __init__(self, msg): super().__init__(f"{msg}") diff --git a/pyhmy/logging.py b/pyhmy/logging.py index 498cf70..b49e6c1 100644 --- a/pyhmy/logging.py +++ b/pyhmy/logging.py @@ -1,3 +1,7 @@ +""" +Logger for pyhmy +""" + import threading import datetime import gzip @@ -6,21 +10,18 @@ import logging import logging.handlers -class _GZipRotator: +class _GZipRotator: # pylint: disable=too-few-public-methods def __call__(self, source, dest): os.rename(source, dest) - f_in = open(dest, "rb") - f_out = gzip.open("%s.gz" % dest, "wb") - f_out.writelines(f_in) - f_out.close() - f_in.close() + with open(dest, "rb") as f_in: + with gzip.open(f"{dest}.gz", "wb") as f_out: + f_out.writelines(f_in) os.remove(dest) -class ControlledLogger: - """ - A simple logger that only writes to file when the 'write' method is called. - """ +class ControlledLogger: # pylint: disable=too-many-instance-attributes + """A simple logger that only writes to file when the 'write' method is + called.""" def __init__(self, logger_name, log_dir, backup_count=5): """ @@ -51,9 +52,7 @@ class ControlledLogger: return f"" def _clear(self): - """ - Internal method to clear the log buffer. - """ + """Internal method to clear the log buffer.""" self.info_buffer.clear() self.debug_buffer.clear() self.warning_buffer.clear() @@ -63,85 +62,74 @@ class ControlledLogger: """ :param msg: The info message to log """ - self._lock.acquire() - self.info_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" - ) - self._lock.release() + with self._lock: + self.info_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) def debug(self, msg): """ :param msg: The debug message to log """ - self._lock.acquire() - self.debug_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" - ) - self._lock.release() + with self._lock: + self.debug_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) def warning(self, msg): """ :param msg: The warning message to log """ - self._lock.acquire() - self.warning_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" - ) - self._lock.release() + with self._lock: + self.warning_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) def error(self, msg): """ :param msg: The error message to log """ - self._lock.acquire() - self.error_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" - ) - self._lock.release() + with self._lock: + self.error_buffer.append( + f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + ) def print_info(self): - """ - Prints the current info buffer but does not flush it to log file. - """ + """Prints the current info buffer but does not flush it to log file.""" print("\n".join(self.info_buffer)) def print_debug(self): - """ - Prints the current debug buffer but does not flush it to log file. - """ + """Prints the current debug buffer but does not flush it to log + file.""" print("\n".join(self.debug_buffer)) def print_warning(self): - """ - Prints the current warning buffer but does not flush it to log file. - """ + """Prints the current warning buffer but does not flush it to log + file.""" print("\n".join(self.warning_buffer)) def print_error(self): - """ - Prints the current error buffer but does not flush it to log file. - """ + """Prints the current error buffer but does not flush it to log + file.""" print("\n".join(self.error_buffer)) def write(self): - """ - Flushes ALL of the log buffers to the log file via the logger. - - Note that directly after this method call, the respective prints will print - nothing since all log messages are flushed to file. - """ - self._lock.acquire() - self.logger.setLevel(logging.DEBUG) - for line in self.debug_buffer: - self.logger.debug(line) - self.logger.setLevel(logging.WARNING) - for line in self.warning_buffer: - self.logger.warning(line) - self.logger.setLevel(logging.ERROR) - for line in self.error_buffer: - self.logger.error(line) - self.logger.setLevel(logging.INFO) - for line in self.info_buffer: - self.logger.info(line) - self._clear() - self._lock.release() + """Flushes ALL of the log buffers to the log file via the logger. + + Note that directly after this method call, the respective prints + will print nothing since all log messages are flushed to file. + """ + with self._lock: + self.logger.setLevel(logging.DEBUG) + for line in self.debug_buffer: + self.logger.debug(line) + self.logger.setLevel(logging.WARNING) + for line in self.warning_buffer: + self.logger.warning(line) + self.logger.setLevel(logging.ERROR) + for line in self.error_buffer: + self.logger.error(line) + self.logger.setLevel(logging.INFO) + for line in self.info_buffer: + self.logger.info(line) + self._clear() diff --git a/pyhmy/numbers.py b/pyhmy/numbers.py index 9d62a11..de413ef 100644 --- a/pyhmy/numbers.py +++ b/pyhmy/numbers.py @@ -1,3 +1,8 @@ +""" +Handles conversion of ONE to ATTO and vice versa +For more granular conversions, see Web3.toWei +""" + from decimal import Decimal @@ -5,8 +10,7 @@ _conversion_unit = Decimal(1e18) def convert_atto_to_one(atto) -> Decimal: - """ - Convert ATTO to ONE + """Convert ATTO to ONE. Parameters ---------- @@ -25,8 +29,7 @@ def convert_atto_to_one(atto) -> Decimal: def convert_one_to_atto(one) -> Decimal: - """ - Convert ONE to ATTO + """Convert ONE to ATTO. Parameters ---------- diff --git a/pyhmy/rpc/exceptions.py b/pyhmy/rpc/exceptions.py index a6ad3ac..fbbca44 100644 --- a/pyhmy/rpc/exceptions.py +++ b/pyhmy/rpc/exceptions.py @@ -1,10 +1,11 @@ -import requests +""" +RPC Specific Exceptions +""" +import requests 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): self.error = error @@ -12,18 +13,14 @@ class RPCError(RuntimeError): class RequestsError(requests.exceptions.RequestException): - """ - Wrapper for requests lib exceptions - """ + """Wrapper for requests lib exceptions.""" def __init__(self, endpoint): super().__init__(f"Error connecting to {endpoint}") class RequestsTimeoutError(requests.exceptions.Timeout): - """ - Wrapper for requests lib Timeout exceptions - """ + """Wrapper for requests lib Timeout exceptions.""" def __init__(self, endpoint): super().__init__(f"Error connecting to {endpoint}") diff --git a/pyhmy/rpc/request.py b/pyhmy/rpc/request.py index be1c4e8..30384cd 100644 --- a/pyhmy/rpc/request.py +++ b/pyhmy/rpc/request.py @@ -1,19 +1,19 @@ +""" +RPC wrapper around requests library +""" import json import requests from .exceptions import RequestsError, RequestsTimeoutError, RPCError - -_default_endpoint = "http://localhost:9500" -_default_timeout = 30 +from ..constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT def base_request( - method, params=None, endpoint=_default_endpoint, timeout=_default_timeout + method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> str: - """ - Basic RPC request + """Basic RPC request. Parameters --------- @@ -65,10 +65,9 @@ def base_request( def rpc_request( - method, params=None, endpoint=_default_endpoint, timeout=_default_timeout + method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - RPC request + """RPC request. Parameters --------- @@ -110,6 +109,3 @@ def rpc_request( return resp except json.decoder.JSONDecodeError as err: raise RPCError(method, endpoint, raw_resp) from err - - -# TODO: Add GET requests diff --git a/pyhmy/signing.py b/pyhmy/signing.py index 4fddde8..7e96128 100644 --- a/pyhmy/signing.py +++ b/pyhmy/signing.py @@ -1,29 +1,33 @@ +""" +Sign Harmony or Ethereum transactions +Harmony staking transaction signing is not covered by this module +""" + +# pylint: disable=protected-access, no-member + +from functools import partial +from toolz import dissoc, pipe, merge + import rlp from eth_utils.curried import keccak, to_int, hexstr_if_str, apply_formatters_to_dict from rlp.sedes import big_endian_int, Binary, binary -from eth_account import Account - from eth_rlp import HashableRLP from hexbytes import HexBytes -from eth_account._utils.signing import sign_transaction_hash - +from eth_account import Account +from eth_account.datastructures import SignedTransaction from eth_account._utils.legacy_transactions import ( Transaction as SignedEthereumTxData, UnsignedTransaction as UnsignedEthereumTxData, LEGACY_TRANSACTION_FORMATTERS as ETHEREUM_FORMATTERS, TRANSACTION_DEFAULTS, chain_id_to_v, - UNSIGNED_TRANSACTION_FIELDS, ) - -from cytoolz import dissoc, pipe, merge, partial - -from eth_account.datastructures import SignedTransaction +from eth_account._utils.signing import sign_transaction_hash from .util import chain_id_to_int, convert_one_to_hex @@ -35,6 +39,11 @@ HARMONY_FORMATTERS = dict( class UnsignedHarmonyTxData(HashableRLP): + """ + Unsigned Harmony transaction data + Includes `shardID` and `toShardID` + as the difference against Eth + """ fields = ( ("nonce", big_endian_int), ("gasPrice", big_endian_int), @@ -48,18 +57,23 @@ class UnsignedHarmonyTxData(HashableRLP): class SignedHarmonyTxData(HashableRLP): + """ + Signed Harmony transaction data + Includes `shardID` and `toShardID` + as the difference against Eth + """ fields = UnsignedHarmonyTxData._meta.fields + ( ("v", big_endian_int), # Recovery value + 27 ("r", big_endian_int), # First 32 bytes ("s", big_endian_int), # Next 32 bytes ) - +# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 def encode_transaction( unsigned_transaction, vrs -): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 - """serialize and encode an unsigned transaction with v,r,s""" - (v, r, s) = vrs +): + """serialize and encode an unsigned transaction with v,r,s.""" + (v, r, s) = vrs # pylint: disable=invalid-name chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), "v", "r", "s") if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, SignedHarmonyTxData)): serializer = SignedHarmonyTxData @@ -70,7 +84,7 @@ def encode_transaction( def serialize_transaction(filled_transaction): - """serialize a signed/unsigned transaction""" + """serialize a signed/unsigned transaction.""" if "v" in filled_transaction: if "shardID" in filled_transaction: serializer = SignedHarmonyTxData @@ -81,41 +95,40 @@ def serialize_transaction(filled_transaction): serializer = UnsignedHarmonyTxData else: serializer = UnsignedEthereumTxData - for f, _ in serializer._meta.fields: - assert f in filled_transaction, f"Could not find {f} in transaction" + for field, _ in serializer._meta.fields: + assert field in filled_transaction, f"Could not find {field} in transaction" return serializer.from_dict( - {f: filled_transaction[f] for f, _ in serializer._meta.fields} + {field: filled_transaction[field] for field, _ in serializer._meta.fields} ) +# https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 def sanitize_transaction(transaction_dict, private_key): - """remove the originating address from the dict and convert chainId to int""" - account = Account.from_key( + """remove the originating address from the dict and convert chainId to + int.""" + account = Account.from_key( # pylint: disable=no-value-for-parameter private_key - ) # get account, from which you can derive public + private key - transaction_dict = transaction_dict.copy() # do not alter the original dictionary - if "from" in transaction_dict: - transaction_dict["from"] = convert_one_to_hex(transaction_dict["from"]) + ) + sanitized_transaction = transaction_dict.copy() # do not alter the original dictionary + if "from" in sanitized_transaction: + sanitized_transaction["from"] = convert_one_to_hex(transaction_dict["from"]) if ( - transaction_dict["from"] == account.address - ): # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 - sanitized_transaction = dissoc(transaction_dict, "from") + sanitized_transaction["from"] == account.address + ): + sanitized_transaction = dissoc(sanitized_transaction, "from") else: raise TypeError( - "from field must match key's %s, but it was %s" - % ( - account.address, - transaction_dict["from"], - ) + "from field must match key's {account.address}, " + "but it was {sanitized_transaction['from']}" ) - if "chainId" in transaction_dict: - transaction_dict["chainId"] = chain_id_to_int(transaction_dict["chainId"]) - return account, transaction_dict + if "chainId" in sanitized_transaction: + sanitized_transaction["chainId"] = chain_id_to_int(sanitized_transaction["chainId"]) + return account, sanitized_transaction def sign_transaction(transaction_dict, private_key) -> SignedTransaction: - """ - Sign a (non-staking) transaction dictionary with the specified private key + """Sign a (non-staking) transaction dictionary with the specified private + key. Parameters ---------- @@ -161,7 +174,8 @@ def sign_transaction(transaction_dict, private_key) -> SignedTransaction: account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) if "to" in sanitized_transaction and sanitized_transaction["to"] is not None: sanitized_transaction["to"] = convert_one_to_hex(sanitized_transaction["to"]) - filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + filled_transaction = pipe( sanitized_transaction, dict, partial(merge, TRANSACTION_DEFAULTS), @@ -171,13 +185,14 @@ def sign_transaction(transaction_dict, private_key) -> SignedTransaction: unsigned_transaction = serialize_transaction(filled_transaction) transaction_hash = unsigned_transaction.hash() + # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 if isinstance( unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData) ): - chain_id = None # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 + chain_id = None else: chain_id = unsigned_transaction.v - (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) + (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) # pylint: disable=invalid-name encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s)) signed_transaction_hash = keccak(encoded_transaction) return SignedTransaction( diff --git a/pyhmy/staking.py b/pyhmy/staking.py index a70174a..7c54d1b 100644 --- a/pyhmy/staking.py +++ b/pyhmy/staking.py @@ -1,18 +1,20 @@ +""" +Call Harmony's staking API +""" + from .rpc.request import rpc_request from .exceptions import InvalidRPCReplyError -_default_endpoint = "http://localhost:9500" -_default_timeout = 30 +from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT ################## # Validator RPCs # ################## def get_all_validator_addresses( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of all created validator addresses on chain + """Get list of all created validator addresses on chain. Parameters ---------- @@ -38,15 +40,14 @@ def get_all_validator_addresses( method = "hmyv2_getAllValidatorAddresses" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_validator_information( - validator_addr, endpoint=_default_endpoint, timeout=_default_timeout + validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get validator information for validator address + """Get validator information for validator address. Parameters ---------- @@ -65,7 +66,8 @@ def get_validator_information( bls-public-keys: :obj:`list` List of associated public BLS keys last-epoch-in-committee: :obj:`int` Last epoch any key of the validator was elected min-self-delegation: :obj:`int` Amount that validator must delegate to self in ATTO - max-total-delegation: :obj:`int` Total amount that validator will aceept delegations until, in ATTO + max-total-delegation: :obj:`int` + Total amount that validator will aceept delegations until, in ATTO rate: :obj:`str` Current commission rate max-rate: :obj:`str` Max commission rate a validator can charge max-change-rate: :obj:`str` Maximum amount the commission rate can increase in one epoch @@ -76,8 +78,9 @@ def get_validator_information( security-contact: :obj:`str` Method to contact the validators details: :obj:`str` Validator details, displayed on the Staking Dashboard creation-height: :obj:`int` Block number in which the validator was created - delegations: :obj:`list` List of delegations, see get_delegations_by_delegator for format - metrics: :obj:`dict` BLS key earning metrics for current epoch (or None if no earnings in the current epoch) + delegations: :obj:`list` + List of delegations, see get_delegations_by_delegator for format + metrics: :obj:`dict` BLS key earning metrics for current epoch (or None) by-bls-key: :obj:`list` List of dictionaries, each with the following keys key: :obj:`dict` Dictionary with the following keys bls-public-key: :obj:`str` BLS public key @@ -90,7 +93,8 @@ def get_validator_information( earned-reward: :obj:`int` Lifetime reward key has earned total-delegation: :obj:`int` Total amount delegated to validator currently-in-committee: :obj:`bool` if key is currently elected - epos-status: :obj:`str` Currently elected, eligible to be elected next epoch, or not eligible to be elected next epoch + epos-status: :obj:`str` Currently elected, eligible to be elected next epoch, + or not eligible to be elected next epoch epos-winning-stake: :obj:`str` Total effective stake of the validator booted-status: :obj:`str` Banned status active-status: :obj:`str` Active or inactive @@ -119,15 +123,14 @@ def get_validator_information( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_elected_validator_addresses( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of elected validator addresses + """Get list of elected validator addresses. Parameters ---------- @@ -154,13 +157,12 @@ def get_elected_validator_addresses( method = "hmyv2_getElectedValidatorAddresses" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) -> list: - """ - Get validators list for a particular epoch +def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list: + """Get validators list for a particular epoch. Parameters ---------- @@ -194,15 +196,14 @@ def get_validators(epoch, endpoint=_default_endpoint, timeout=_default_timeout) return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_validator_keys( - epoch, endpoint=_default_endpoint, timeout=_default_timeout + epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get validator BLS keys in the committee for a particular epoch + """Get validator BLS keys in the committee for a particular epoch. Parameters ---------- @@ -232,15 +233,14 @@ def get_validator_keys( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_validator_information_by_block_number( - validator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout + validator_addr, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ): - """ - Get validator information for validator address at a block + """Get validator information for validator address at a block. Parameters ---------- @@ -272,15 +272,14 @@ def get_validator_information_by_block_number( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_all_validator_information( - page=0, endpoint=_default_endpoint, timeout=_default_timeout + page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get validator information for all validators on chain + """Get validator information for all validators on chain. Parameters ---------- @@ -310,15 +309,14 @@ def get_all_validator_information( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_validator_self_delegation( - address, endpoint=_default_endpoint, timeout=_default_timeout + address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get the amount self delegated by validator + """Get the amount self delegated by validator. Parameters ---------- @@ -350,15 +348,14 @@ def get_validator_self_delegation( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_validator_total_delegation( - address, endpoint=_default_endpoint, timeout=_default_timeout + address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get the total amount delegated t ovalidator (including self delegated) + """Get the total amount delegated t ovalidator (including self delegated) Parameters ---------- @@ -390,15 +387,14 @@ def get_validator_total_delegation( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_all_validator_information_by_block_number( - block_num, page=0, endpoint=_default_endpoint, timeout=_default_timeout + block_num, page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get validator information at block number for all validators on chain + """Get validator information at block number for all validators on chain. Parameters ---------- @@ -431,18 +427,17 @@ def get_all_validator_information_by_block_number( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception ################### # Delegation RPCs # ################### def get_all_delegation_information( - page=0, endpoint=_default_endpoint, timeout=_default_timeout + page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get delegation information for all delegators on chain + """Get delegation information for all delegators on chain. Parameters ---------- @@ -476,15 +471,14 @@ def get_all_delegation_information( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_delegations_by_delegator( - delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout + delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of delegations by a delegator + """Get list of delegations by a delegator. Parameters ---------- @@ -521,15 +515,14 @@ def get_delegations_by_delegator( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_delegations_by_delegator_by_block_number( - delegator_addr, block_num, endpoint=_default_endpoint, timeout=_default_timeout + delegator_addr, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of delegations by a delegator at a specific block + """Get list of delegations by a delegator at a specific block. Parameters ---------- @@ -561,18 +554,17 @@ def get_delegations_by_delegator_by_block_number( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_delegation_by_delegator_and_validator( delegator_addr, validator_address, - endpoint=_default_endpoint, - timeout=_default_timeout, + endpoint=DEFAULT_ENDPOINT, + timeout=DEFAULT_TIMEOUT, ) -> dict: - """ - Get list of delegations by a delegator at a specific block + """Get list of delegations by a delegator at a specific block. Parameters ---------- @@ -587,7 +579,8 @@ def get_delegation_by_delegator_and_validator( Returns ------- - one delegation (or None if such delegation doesn't exist), see get_delegations_by_delegator for fields + one delegation (or None if such delegation doesn't exist) + see get_delegations_by_delegator for fields Raises ------ @@ -604,15 +597,14 @@ def get_delegation_by_delegator_and_validator( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_available_redelegation_balance( - delegator_addr, endpoint=_default_endpoint, timeout=_default_timeout + delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> int: - """ - Get amount of locked undelegated tokens + """Get amount of locked undelegated tokens. Parameters ---------- @@ -644,15 +636,14 @@ def get_available_redelegation_balance( "result" ] ) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_delegations_by_validator( - validator_addr, endpoint=_default_endpoint, timeout=_default_timeout + validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of delegations to a validator + """Get list of delegations to a validator. Parameters ---------- @@ -682,18 +673,17 @@ def get_delegations_by_validator( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception ######################## # Staking Network RPCs # ######################## def get_current_utility_metrics( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get current utility metrics of network + """Get current utility metrics of network. Parameters ---------- @@ -722,15 +712,14 @@ def get_current_utility_metrics( method = "hmyv2_getCurrentUtilityMetrics" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_staking_network_info( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get staking network information + """Get staking network information. Parameters ---------- @@ -760,13 +749,12 @@ def get_staking_network_info( method = "hmyv2_getStakingNetworkInfo" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: - """ - Get voting committees for current & previous epoch +def get_super_committees(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: + """Get voting committees for current & previous epoch. Parameters ---------- @@ -814,13 +802,12 @@ def get_super_committees(endpoint=_default_endpoint, timeout=_default_timeout) - method = "hmyv2_getSuperCommittees" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> int: - """ - Get total staking by validators, only meant to be called on beaconchain +def get_total_staking(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: + """Get total staking by validators, only meant to be called on beaconchain. Parameters ---------- @@ -845,15 +832,14 @@ def get_total_staking(endpoint=_default_endpoint, timeout=_default_timeout) -> i method = "hmyv2_getTotalStaking" try: return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as e: - raise InvalidRPCReplyError(method, endpoint) from e + except (KeyError, TypeError) as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_raw_median_stake_snapshot( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get median stake & additional committee data of the current epoch + """Get median stake & additional committee data of the current epoch. Parameters ---------- @@ -891,5 +877,5 @@ def get_raw_median_stake_snapshot( method = "hmyv2_getMedianRawStakeSnapshot" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index 03c96c6..bffbcf8 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -1,7 +1,15 @@ -from cytoolz import ( +""" +Sign Harmony staking transactions +""" + +import math + +from decimal import Decimal + +from functools import partial +from toolz import ( pipe, dissoc, - partial, merge, identity, ) @@ -10,10 +18,6 @@ from hexbytes import HexBytes import rlp -import math - -from decimal import Decimal - from eth_account.datastructures import SignedTransaction from eth_account._utils.signing import sign_transaction_hash @@ -30,11 +34,12 @@ from eth_utils.curried import ( apply_formatter_to_array, ) +from .constants import PRECISION, MAX_DECIMAL + from .signing import sanitize_transaction from .staking_structures import ( FORMATTERS, - StakingSettings, Directive, CreateValidator, EditValidator, @@ -45,12 +50,13 @@ from .staking_structures import ( from .util import convert_one_to_hex +# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 def _convert_staking_percentage_to_number( value, -): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 - """ - Convert from staking percentage to integer - For example, 0.1 becomes 1000000000000000000 +): + """Convert from staking percentage to integer For example, 0.1 becomes + 1000000000000000000. Since Python floats are problematic with precision, + this function is used as a workaround. Parameters --------- @@ -90,24 +96,23 @@ def _convert_staking_percentage_to_number( combined_str += splitted[1] elif len(splitted) > 2: raise ValueError("Too many periods to be a StakingDecimal string") - if length > StakingSettings.PRECISION: + if length > PRECISION: raise ValueError( - "Too much precision, must be less than {StakingSettings.PRECISION}" + "Too much precision, must be less than {PRECISION}" ) - zeroes_to_add = StakingSettings.PRECISION - length + zeroes_to_add = PRECISION - length combined_str += ( "0" * zeroes_to_add ) # This will not have any periods, so it is effectively a large integer val = int(combined_str) - assert val <= StakingSettings.MAX_DECIMAL, "Staking percentage is too large" + assert val <= MAX_DECIMAL, "Staking percentage is too large" return val def _get_account_and_transaction(transaction_dict, private_key): - """ - Create account from private key and sanitize the transaction - Sanitization involves removal of 'from' key - And conversion of chainId key from str to int (if present) + """Create account from private key and sanitize the transaction + Sanitization involves removal of 'from' key And conversion of chainId key + from str to int (if present) Parameters ---------- @@ -134,10 +139,10 @@ def _get_account_and_transaction(transaction_dict, private_key): ].value # convert to value, like in TypeScript return account, sanitized_transaction - +# pylint: disable=too-many-locals,protected-access,invalid-name def _sign_transaction_generic(account, sanitized_transaction, parent_serializer): - """ - Sign a generic staking transaction, given the serializer base class and account + """Sign a generic staking transaction, given the serializer base class and + account. Paramters --------- @@ -167,7 +172,8 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) parent_serializer.SignedChainId(), ) # since chain_id_to_v adds v/r/s, unsigned is not used here # fill the transaction - filled_transaction = pipe( # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 + filled_transaction = pipe( sanitized_transaction, dict, partial(merge, {"chainId": None}), @@ -175,8 +181,8 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) apply_formatters_to_dict(FORMATTERS), ) # get the unsigned transaction - for f, _ in unsigned_serializer._meta.fields: - assert f in filled_transaction, f"Could not find {f} in transaction" + for field, _ in unsigned_serializer._meta.fields: + assert field in filled_transaction, f"Could not find {field} in transaction" unsigned_transaction = unsigned_serializer.from_dict( {f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields} ) # drop extras silently @@ -191,11 +197,12 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) unsigned_transaction.as_dict(), "v", "r", "s" ) # remove extra v/r/s added by chain_id_to_v # serialize it + # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 signed_transaction = signed_serializer( v=v + ( 8 if chain_id is None else 0 - ), # copied from https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 + ), r=r, s=s, # in the below statement, remove everything not expected by signed_serializer **{ @@ -218,11 +225,9 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) ) -def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate): - """ - Sign a delegate or undelegate transaction - See sign_staking_transaction for details - """ +def _sign_delegate_or_undelegate(transaction_dict, private_key): + """Sign a delegate or undelegate transaction See sign_staking_transaction + for details.""" # preliminary steps if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]: raise TypeError( @@ -247,10 +252,8 @@ def _sign_delegate_or_undelegate(transaction_dict, private_key, delegate): def _sign_collect_rewards(transaction_dict, private_key): - """ - Sign a collect rewards transaction - See sign_staking_transaction for details - """ + """Sign a collect rewards transaction See sign_staking_transaction for + details.""" # preliminary steps if transaction_dict["directive"] != Directive.CollectRewards: raise TypeError("Only CollectRewards is supported by _sign_collect_rewards") @@ -268,10 +271,8 @@ def _sign_collect_rewards(transaction_dict, private_key): def _sign_create_validator(transaction_dict, private_key): - """ - Sign a create validator transaction - See sign_staking_transaction for details - """ + """Sign a create validator transaction See sign_staking_transaction for + details.""" # preliminary steps if transaction_dict["directive"] != Directive.CreateValidator: raise TypeError( @@ -343,10 +344,8 @@ def _sign_create_validator(transaction_dict, private_key): def _sign_edit_validator(transaction_dict, private_key): - """ - Sign an edit validator transaction - See sign_staking_transaction for details - """ + """Sign an edit validator transaction See sign_staking_transaction for + details.""" # preliminary steps if transaction_dict["directive"] != Directive.EditValidator: raise TypeError( @@ -377,6 +376,7 @@ def _sign_edit_validator(transaction_dict, private_key): ), # max total delegation (in ONE), decimals are silently dropped hexstr_if_str(to_bytes), # key to remove hexstr_if_str(to_bytes), # key to add + hexstr_if_str(to_bytes), # key to add sig ], [ convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), @@ -388,14 +388,14 @@ def _sign_edit_validator(transaction_dict, private_key): math.floor(sanitized_transaction.pop("max-total-delegation")), sanitized_transaction.pop("bls-key-to-remove"), sanitized_transaction.pop("bls-key-to-add"), + sanitized_transaction.pop("bls-key-to-add-sig"), ], ) return _sign_transaction_generic(account, sanitized_transaction, EditValidator) def sign_staking_transaction(transaction_dict, private_key): - """ - Sign a supplied transaction_dict with the private_key + """Sign a supplied transaction_dict with the private_key. Parameters ---------- @@ -464,7 +464,7 @@ def sign_staking_transaction(transaction_dict, private_key): assert isinstance( transaction_dict, dict ), "Only dictionaries are supported" # OrderedDict is a subclass - # chain_id missing => 'rlp: input string too long for uint64, decoding into (types.StakingTransaction)(types.txdata).GasLimit' + # chain_id missing => results in rlp decoding error for GasLimit assert "chainId" in transaction_dict, "chainId missing" assert "directive" in transaction_dict, "Staking transaction type not specified" assert isinstance( @@ -472,11 +472,12 @@ def sign_staking_transaction(transaction_dict, private_key): ), "Unknown staking transaction type" if transaction_dict["directive"] == Directive.CollectRewards: return _sign_collect_rewards(transaction_dict, private_key) - elif transaction_dict["directive"] == Directive.Delegate: - return _sign_delegate_or_undelegate(transaction_dict, private_key, True) - elif transaction_dict["directive"] == Directive.Undelegate: - return _sign_delegate_or_undelegate(transaction_dict, private_key, False) - elif transaction_dict["directive"] == Directive.CreateValidator: + if transaction_dict["directive"] == Directive.Delegate: + return _sign_delegate_or_undelegate(transaction_dict, private_key) + if transaction_dict["directive"] == Directive.Undelegate: + return _sign_delegate_or_undelegate(transaction_dict, private_key) + if transaction_dict["directive"] == Directive.CreateValidator: return _sign_create_validator(transaction_dict, private_key) - elif transaction_dict["directive"] == Directive.EditValidator: + if transaction_dict["directive"] == Directive.EditValidator: return _sign_edit_validator(transaction_dict, private_key) + raise ValueError('Unknown staking transaction type') diff --git a/pyhmy/staking_structures.py b/pyhmy/staking_structures.py index 44f7102..ced6a0b 100644 --- a/pyhmy/staking_structures.py +++ b/pyhmy/staking_structures.py @@ -1,3 +1,9 @@ +""" +Helper module for signing Harmony staking transactions +""" +# disable most of the Lint here +# pylint: disable=protected-access,no-member,invalid-name,missing-class-docstring,missing-function-docstring + from enum import Enum, auto from rlp.sedes import big_endian_int, Binary, CountableList, List, Text @@ -9,16 +15,11 @@ from eth_utils.curried import ( hexstr_if_str, ) - -class StakingSettings: - PRECISION = 18 - MAX_DECIMAL = 1000000000000000000 - - +# https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 class Directive( Enum -): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 - def _generate_next_value_(name, start, count, last_values): +): + def _generate_next_value_(name, start, count, last_values): # pylint: disable=no-self-argument return count CreateValidator = auto() @@ -38,7 +39,6 @@ FORMATTERS = { "chainId": hexstr_if_str(to_int), } - class CollectRewards: @staticmethod def UnsignedChainId(): @@ -159,23 +159,28 @@ class CreateValidator: "stakeMsg", List( [ # list with the following members + # validatorAddress Binary.fixed_length( 20, allow_empty=True - ), # validatorAddress + ), + # description is Text of 5 elements List( [Text()] * 5, True - ), # description is Text of 5 elements + ), + # commission rate is made up of 3 integers in an array List( [List([big_endian_int], True)] * 3, True - ), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] + ), big_endian_int, # min self delegation big_endian_int, # max total delegation + # bls-public-keys array of unspecified length, each key of 48 CountableList( Binary.fixed_length(48, allow_empty=True) - ), # bls-public-keys array of unspecified length, each key of 48 + ), + # bls-key-sigs array of unspecified length, each sig of 96 CountableList( Binary.fixed_length(96, allow_empty=True) - ), # bls-key-sigs array of unspecified length, each sig of 96 + ), big_endian_int, # amount ], True, @@ -233,21 +238,30 @@ class EditValidator: "stakeMsg", List( [ # list with the following members + # validatorAddress Binary.fixed_length( 20, allow_empty=True - ), # validatorAddress + ), + # description is Text of 5 elements List( [Text()] * 5, True - ), # description is Text of 5 elements - List([big_endian_int], True), # new rate is in a list + ), + # new rate is in a list + List([big_endian_int], True), big_endian_int, # min self delegation big_endian_int, # max total delegation + # slot key to remove Binary.fixed_length( 48, allow_empty=True - ), # slot key to remove + ), + # slot key to add Binary.fixed_length( 48, allow_empty=True - ), # slot key to add + ), + # slot key to add sig + Binary.fixed_length( + 96, allow_empty=True + ), ], True, ), diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index b1c890c..97eb407 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -1,20 +1,21 @@ -from .rpc.request import rpc_request -from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError +""" +Interact with Harmony's transaction RPC API +""" + import time import random - -_default_endpoint = "http://localhost:9500" -_default_timeout = 30 +from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT +from .rpc.request import rpc_request +from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError ######################### # Transaction Pool RPCs # ######################### def get_pending_transactions( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of pending transactions + """Get list of pending transactions. Parameters ---------- @@ -39,15 +40,14 @@ def get_pending_transactions( method = "hmyv2_pendingTransactions" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_transaction_error_sink( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get current transactions error sink + """Get current transactions error sink. Parameters ---------- @@ -75,15 +75,14 @@ def get_transaction_error_sink( method = "hmyv2_getCurrentTransactionErrorSink" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_pending_staking_transactions( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of pending staking transactions + """Get list of pending staking transactions. Parameters ---------- @@ -108,15 +107,14 @@ def get_pending_staking_transactions( method = "hmyv2_pendingStakingTransactions" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_staking_transaction_error_sink( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get current staking transactions error sink + """Get current staking transactions error sink. Parameters ---------- @@ -145,13 +143,13 @@ def get_staking_transaction_error_sink( method = "hmyv2_getCurrentStakingErrorSink" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception -def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict: - """ - Get stats of the pool, that is, number of pending and queued (non-executable) transactions +def get_pool_stats(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: + """Get stats of the pool, that is, number of pending and queued (non- + executable) transactions. Parameters ---------- @@ -178,18 +176,17 @@ def get_pool_stats(endpoint=_default_endpoint, timeout=_default_timeout) -> dict method = "hmyv2_getPoolStats" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception #################### # Transaction RPCs # #################### def get_transaction_by_hash( - tx_hash, endpoint=_default_endpoint, timeout=_default_timeout + tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get transaction by hash + """Get transaction by hash. Parameters ---------- @@ -239,15 +236,15 @@ def get_transaction_by_hash( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_transaction_by_block_hash_and_index( - block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout + block_hash, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get transaction based on index in list of transactions in a block by block hash + """Get transaction based on index in list of transactions in a block by + block hash. Parameters ---------- @@ -279,15 +276,15 @@ def get_transaction_by_block_hash_and_index( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_transaction_by_block_number_and_index( - block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout + block_num, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get transaction based on index in list of transactions in a block by block number + """Get transaction based on index in list of transactions in a block by + block number. Parameters ---------- @@ -319,15 +316,14 @@ def get_transaction_by_block_number_and_index( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_transaction_receipt( - tx_hash, endpoint=_default_endpoint, timeout=_default_timeout + tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get transaction receipt corresponding to tx_hash + """Get transaction receipt corresponding to tx_hash. Parameters ---------- @@ -348,7 +344,9 @@ def get_transaction_receipt( from: :obj:`str` Sender wallet address gasUsed: :obj:`int` Gas used for the transaction logs: :obj:`list` List of logs, each being a dict with keys as follows: - address, blockHash, blockNumber, data, logIndex, removed, topics, transactionHash, transactionIndex + address, blockHash, blockNumber + data, logIndex, removed + topics, transactionHash, transactionIndex logsBloom :obj:`str` Bloom logs shardID :obj:`int` Shard ID status :obj:`int` Status of transaction (0: pending, 1: success) @@ -371,15 +369,14 @@ def get_transaction_receipt( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def send_raw_transaction( - signed_tx, endpoint=_default_endpoint, timeout=_default_timeout + signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> str: - """ - Send signed transaction + """Send signed transaction. Parameters ---------- @@ -412,15 +409,14 @@ def send_raw_transaction( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def send_and_confirm_raw_transaction( - signed_tx, endpoint=_default_endpoint, timeout=_default_timeout + signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Send signed transaction and wait for it to be confirmed + """Send signed transaction and wait for it to be confirmed. Parameters ---------- @@ -466,10 +462,9 @@ def send_and_confirm_raw_transaction( # CrossShard Transaction RPCs # ############################### def get_pending_cx_receipts( - endpoint=_default_endpoint, timeout=_default_timeout + endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Get list of pending cross shard transactions + """Get list of pending cross shard transactions. Parameters ---------- @@ -483,7 +478,7 @@ def get_pending_cx_receipts( list of CX receipts, each a dict with the following keys commitBitmap: :obj:`str` Hex represenation of aggregated signature bitmap commitSig: :obj:`str` Hex representation of aggregated signature - receipts: :obj:`list` list of dictionaries, each representing a cross shard transaction receipt + receipts: :obj:`list` list of dictionaries, each a cross shard transaction receipt amount: :obj:`int` Amount in ATTO from: :obj:`str` From address to: :obj:`str` From address @@ -517,15 +512,14 @@ def get_pending_cx_receipts( method = "hmyv2_getPendingCXReceipts" try: return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_cx_receipt_by_hash( - cx_hash, endpoint=_default_endpoint, timeout=_default_timeout + cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get cross shard receipt by hash on the receiving shard end point + """Get cross shard receipt by hash on the receiving shard end point. Parameters ---------- @@ -564,15 +558,15 @@ def get_cx_receipt_by_hash( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def resend_cx_receipt( - cx_hash, endpoint=_default_endpoint, timeout=_default_timeout + cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> bool: - """ - Resend the cross shard receipt to the receiving shard to re-process if the transaction did not pay out + """Resend the cross shard receipt to the receiving shard to re-process if + the transaction did not pay out. Parameters ---------- @@ -603,18 +597,17 @@ def resend_cx_receipt( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception ############################ # Staking Transaction RPCs # ############################ def get_staking_transaction_by_hash( - tx_hash, endpoint=_default_endpoint, timeout=_default_timeout + tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get staking transaction by hash + """Get staking transaction by hash. Parameters ---------- @@ -658,15 +651,14 @@ def get_staking_transaction_by_hash( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_staking_transaction_by_block_hash_and_index( - block_hash, tx_index, endpoint=_default_endpoint, timeout=_default_timeout + block_hash, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get staking transaction by block hash and transaction index + """Get staking transaction by block hash and transaction index. Parameters ---------- @@ -698,15 +690,14 @@ def get_staking_transaction_by_block_hash_and_index( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def get_staking_transaction_by_block_number_and_index( - block_num, tx_index, endpoint=_default_endpoint, timeout=_default_timeout + block_num, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> dict: - """ - Get staking transaction by block number and transaction index + """Get staking transaction by block number and transaction index. Parameters ---------- @@ -738,15 +729,14 @@ def get_staking_transaction_by_block_number_and_index( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def send_raw_staking_transaction( - raw_tx, endpoint=_default_endpoint, timeout=_default_timeout + raw_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> str: - """ - Send signed staking transaction + """Send signed staking transaction. Parameters ---------- @@ -779,15 +769,14 @@ def send_raw_staking_transaction( return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ "result" ] - except KeyError as e: - raise InvalidRPCReplyError(method, endpoint) from e + except KeyError as exception: + raise InvalidRPCReplyError(method, endpoint) from exception def send_and_confirm_raw_staking_transaction( - signed_tx, endpoint=_default_endpoint, timeout=_default_timeout + signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> list: - """ - Send signed staking transaction and wait for it to be confirmed + """Send signed staking transaction and wait for it to be confirmed. Parameters ---------- diff --git a/pyhmy/util.py b/pyhmy/util.py index d287821..e055b95 100644 --- a/pyhmy/util.py +++ b/pyhmy/util.py @@ -1,10 +1,15 @@ +""" +Basic pyhmy utils like is_shard_active +ONE address format conversion +Chain id (str) to int conversion +""" import json import subprocess import os import sys import datetime -import requests +from eth_utils import to_checksum_address from .blockchain import get_latest_header @@ -18,16 +23,11 @@ from .account import is_valid_address from .bech32.bech32 import bech32_decode, bech32_encode, convertbits -from eth_utils import to_checksum_address - -datetime_format = "%Y-%m-%d %H:%M:%S.%f" - - class Typgpy(str): - """ - Typography constants for pretty printing. + """Typography constants for pretty printing. - Note that an ENDC is needed to mark the end of a 'highlighted' text segment. + Note that an ENDC is needed to mark the end of a 'highlighted' text + segment. """ HEADER = "\033[95m" @@ -40,8 +40,14 @@ class Typgpy(str): UNDERLINE = "\033[4m" -def chain_id_to_int(chainId): - chainIds = dict( +def chain_id_to_int(chain_id): + """ + If chain_id is a string, converts it to int. + If chain_id is an int, returns the int. + + Else raises TypeError + """ + chain_ids = dict( Default=0, EthMainnet=1, Morden=2, @@ -61,15 +67,14 @@ def chain_id_to_int(chainId): ) # do not validate integer chainids, only known strings - if isinstance(chainId, str): + if isinstance(chain_id, str): assert ( - chainId in chainIds - ), f"Chain {chainId} unknown, specify an integer chainId" - return chainIds.get(chainId) - elif isinstance(chainId, int): - return chainId - else: - raise TypeError("chainId must be str or int") + chain_id in chain_ids + ), f"Chain {chain_id} unknown, specify an integer chainId" + return chain_ids.get(chain_id) + if isinstance(chain_id, int): + return chain_id + raise TypeError("chainId must be str or int") def get_gopath(): @@ -87,29 +92,25 @@ def get_goversion(): def convert_one_to_hex(addr): - """ - Given a one address, convert it to hex checksum address - """ + """Given a one address, convert it to hex checksum address.""" if not is_valid_address(addr): return to_checksum_address(addr) - hrp, data = bech32_decode(addr) + _, data = bech32_decode(addr) buf = convertbits(data, 5, 8, False) - address = "0x" + "".join("{:02x}".format(x) for x in buf) - return to_checksum_address(address) + address = "0x" + "".join(f"{x:02x}" for x in buf) + return str(to_checksum_address(address)) def convert_hex_to_one(addr): - """ - Given a hex address, convert it to a one address - """ + """Given a hex address, convert it to a one address.""" if is_valid_address(addr): return addr - checksum_addr = to_checksum_address(addr) + checksum_addr = str(to_checksum_address(addr)) data = bytearray.fromhex( checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr ) buf = convertbits(data, 8, 5) - return bech32_encode("one", buf) + return str(bech32_encode("one", buf)) def is_active_shard(endpoint, delay_tolerance=60): @@ -146,10 +147,10 @@ def get_bls_build_variables(): subprocess.check_output(["which", "openssl"]) .decode() .strip() - .split("\n")[0] + .split("\n", maxsplit=1)[0] ) - except (IndexError, subprocess.CalledProcessError) as e: - raise RuntimeError("`openssl` not found") from e + except (IndexError, subprocess.CalledProcessError) as exception: + raise RuntimeError("`openssl` not found") from exception hmy_path = f"{get_gopath()}/src/github.com/harmony-one" bls_dir = f"{hmy_path}/bls" mcl_dir = f"{hmy_path}/mcl" @@ -179,6 +180,6 @@ def json_load(string, **kwargs): """ try: return json.loads(string, **kwargs) - except Exception as e: + except Exception as exception: print(f"{Typgpy.FAIL}Could not parse input: '{string}'{Typgpy.ENDC}") - raise e from e + raise exception diff --git a/pyhmy/validator.py b/pyhmy/validator.py index c2178fc..1b77093 100644 --- a/pyhmy/validator.py +++ b/pyhmy/validator.py @@ -1,15 +1,28 @@ +""" +Load validator information from Harmony blockchain +Create and edit validators +""" import json +from decimal import Decimal, InvalidOperation from eth_account.datastructures import SignedTransaction -from decimal import Decimal, InvalidOperation - -from .account import get_balance, is_valid_address +from .account import is_valid_address + +from .constants import ( + DEFAULT_ENDPOINT, + DEFAULT_TIMEOUT, + NAME_CHAR_LIMIT, + IDENTITY_CHAR_LIMIT, + WEBSITE_CHAR_LIMIT, + SECURITY_CONTACT_CHAR_LIMIT, + DETAILS_CHAR_LIMIT, + MIN_REQUIRED_DELEGATION, +) -from .numbers import convert_one_to_atto +from .exceptions import InvalidValidatorError -from .exceptions import ( - InvalidValidatorError, +from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError, @@ -21,18 +34,10 @@ from .staking_structures import Directive from .staking_signing import sign_staking_transaction -_default_endpoint = "http://localhost:9500" -_default_timeout = 30 - -# TODO: Add unit testing -class Validator: - - name_char_limit = 140 - identity_char_limit = 140 - website_char_limit = 140 - security_contact_char_limit = 140 - details_char_limit = 280 - min_required_delegation = convert_one_to_atto(10000) # in ATTO +class Validator: # pylint: disable=too-many-instance-attributes, too-many-public-methods + """ + Harmony validator + """ def __init__(self, address): if not isinstance(address, str): @@ -58,8 +63,7 @@ class Validator: self._max_rate = None def _sanitize_input(self, data, check_str=False) -> str: - """ - If data is None, return '' else return data + """If data is None, return '' else return data. Raises ------ @@ -69,14 +73,13 @@ class Validator: if not isinstance(data, str): raise InvalidValidatorError( 3, - f"Expected data to be string to avoid floating point precision issues but got {data}", + "Expected data to be string " + f"to avoid floating point precision issues but got {data}", ) return "" if not data else str(data) def __str__(self) -> str: - """ - Returns JSON string representation of Validator fields - """ + """Returns JSON string representation of Validator fields.""" info = self.export() for key, value in info.items(): if isinstance(value, Decimal): @@ -87,8 +90,7 @@ class Validator: return f"" def get_address(self) -> str: - """ - Get validator address + """Get validator address. Returns ------- @@ -98,8 +100,7 @@ class Validator: return self._address def add_bls_key(self, key) -> bool: - """ - Add BLS public key to validator BLS keys if not already in list + """Add BLS public key to validator BLS keys if not already in list. Returns ------- @@ -113,8 +114,7 @@ class Validator: return False def remove_bls_key(self, key) -> bool: - """ - Remove BLS public key from validator BLS keys if exists + """Remove BLS public key from validator BLS keys if exists. Returns ------- @@ -128,8 +128,7 @@ class Validator: return False def get_bls_keys(self) -> list: - """ - Get list of validator BLS keys + """Get list of validator BLS keys. Returns ------- @@ -139,8 +138,7 @@ class Validator: return self._bls_keys def add_bls_key_sig(self, key) -> bool: - """ - Add BLS public key to validator BLS keys if not already in list + """Add BLS public key to validator BLS keys if not already in list. Returns ------- @@ -154,8 +152,7 @@ class Validator: return False def remove_bls_key_sig(self, key) -> bool: - """ - Remove BLS public key from validator BLS keys if exists + """Remove BLS public key from validator BLS keys if exists. Returns ------- @@ -169,8 +166,7 @@ class Validator: return False def get_bls_key_sigs(self) -> list: - """ - Get list of validator BLS keys + """Get list of validator BLS keys. Returns ------- @@ -180,8 +176,7 @@ class Validator: return self._bls_key_sigs def set_name(self, name): - """ - Set validator name + """Set validator name. Parameters ---------- @@ -194,15 +189,14 @@ class Validator: If input is invalid """ name = self._sanitize_input(name) - if len(name) > self.name_char_limit: + if len(name) > NAME_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Name must be less than {self.name_char_limit} characters" + 3, f"Name must be less than {NAME_CHAR_LIMIT} characters" ) self._name = name def get_name(self) -> str: - """ - Get validator name + """Get validator name. Returns ------- @@ -212,8 +206,7 @@ class Validator: return self._name def set_identity(self, identity): - """ - Set validator identity + """Set validator identity. Parameters ---------- @@ -226,15 +219,14 @@ class Validator: If input is invalid """ identity = self._sanitize_input(identity) - if len(identity) > self.identity_char_limit: + if len(identity) > IDENTITY_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Identity must be less than {self.identity_char_limit} characters" + 3, f"Identity must be less than {IDENTITY_CHAR_LIMIT} characters" ) self._identity = identity def get_identity(self) -> str: - """ - Get validator identity + """Get validator identity. Returns ------- @@ -244,8 +236,7 @@ class Validator: return self._identity def set_website(self, website): - """ - Set validator website + """Set validator website. Parameters ---------- @@ -258,15 +249,14 @@ class Validator: If input is invalid """ website = self._sanitize_input(website) - if len(website) > self.website_char_limit: + if len(website) > WEBSITE_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Website must be less than {self.website_char_limit} characters" + 3, f"Website must be less than {WEBSITE_CHAR_LIMIT} characters" ) self._website = website def get_website(self) -> str: - """ - Get validator website + """Get validator website. Returns ------- @@ -276,8 +266,7 @@ class Validator: return self._website def set_security_contact(self, contact): - """ - Set validator security contact + """Set validator security contact. Parameters ---------- @@ -290,16 +279,15 @@ class Validator: If input is invalid """ contact = self._sanitize_input(contact) - if len(contact) > self.security_contact_char_limit: + if len(contact) > SECURITY_CONTACT_CHAR_LIMIT: raise InvalidValidatorError( 3, - f"Security contact must be less than {self.security_contact_char_limit} characters", + f"Security contact must be less than {SECURITY_CONTACT_CHAR_LIMIT} characters", ) self._security_contact = contact def get_security_contact(self) -> str: - """ - Get validator security contact + """Get validator security contact. Returns ------- @@ -309,8 +297,7 @@ class Validator: return self._security_contact def set_details(self, details): - """ - Set validator details + """Set validator details. Parameters ---------- @@ -323,15 +310,14 @@ class Validator: If input is invalid """ details = self._sanitize_input(details) - if len(details) > self.details_char_limit: + if len(details) > DETAILS_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Details must be less than {self.details_char_limit} characters" + 3, f"Details must be less than {DETAILS_CHAR_LIMIT} characters" ) self._details = details def get_details(self) -> str: - """ - Get validator details + """Get validator details. Returns ------- @@ -341,8 +327,7 @@ class Validator: return self._details def set_min_self_delegation(self, delegation): - """ - Set validator min self delegation + """Set validator min self delegation. Parameters ---------- @@ -357,20 +342,19 @@ class Validator: delegation = self._sanitize_input(delegation) try: delegation = Decimal(delegation) - except (TypeError, InvalidOperation) as e: + except (TypeError, InvalidOperation) as exception: raise InvalidValidatorError( 3, "Min self delegation must be a number" - ) from e - if delegation < self.min_required_delegation: + ) from exception + if delegation < MIN_REQUIRED_DELEGATION: raise InvalidValidatorError( 3, - f"Min self delegation must be greater than {self.min_required_delegation} ATTO", + f"Min self delegation must be greater than {MIN_REQUIRED_DELEGATION} ATTO", ) self._min_self_delegation = delegation def get_min_self_delegation(self) -> Decimal: - """ - Get validator min self delegation + """Get validator min self delegation. Returns ------- @@ -380,8 +364,7 @@ class Validator: return self._min_self_delegation def set_max_total_delegation(self, max_delegation): - """ - Set validator max total delegation + """Set validator max total delegation. Parameters ---------- @@ -396,16 +379,16 @@ class Validator: max_delegation = self._sanitize_input(max_delegation) try: max_delegation = Decimal(max_delegation) - except (TypeError, InvalidOperation) as e: + except (TypeError, InvalidOperation) as exception: raise InvalidValidatorError( 3, "Max total delegation must be a number" - ) from e + ) from exception if self._min_self_delegation: if max_delegation < self._min_self_delegation: raise InvalidValidatorError( 3, - f"Max total delegation must be greater than min self delegation: " - "{self._min_self_delegation}", + "Max total delegation must be greater than min self delegation: " + f"{self._min_self_delegation}", ) else: raise InvalidValidatorError( @@ -414,8 +397,7 @@ class Validator: self._max_total_delegation = max_delegation def get_max_total_delegation(self) -> Decimal: - """ - Get validator max total delegation + """Get validator max total delegation. Returns ------- @@ -425,8 +407,7 @@ class Validator: return self._max_total_delegation def set_amount(self, amount): - """ - Set validator initial delegation amount + """Set validator initial delegation amount. Parameters ---------- @@ -441,8 +422,8 @@ class Validator: amount = self._sanitize_input(amount) try: amount = Decimal(amount) - except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, "Amount must be a number") from e + except (TypeError, InvalidOperation) as exception: + raise InvalidValidatorError(3, "Amount must be a number") from exception if self._min_self_delegation: if amount < self._min_self_delegation: raise InvalidValidatorError( @@ -468,8 +449,7 @@ class Validator: self._inital_delegation = amount def get_amount(self) -> Decimal: - """ - Get validator initial delegation amount + """Get validator initial delegation amount. Returns ------- @@ -479,8 +459,7 @@ class Validator: return self._inital_delegation def set_max_rate(self, rate): - """ - Set validator max commission rate + """Set validator max commission rate. Parameters ---------- @@ -495,15 +474,14 @@ class Validator: rate = self._sanitize_input(rate, True) try: rate = Decimal(rate) - except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, "Max rate must be a number") from e + except (TypeError, InvalidOperation) as exception: + raise InvalidValidatorError(3, "Max rate must be a number") from exception if rate < 0 or rate > 1: raise InvalidValidatorError(3, "Max rate must be between 0 and 1") self._max_rate = rate def get_max_rate(self) -> Decimal: - """ - Get validator max commission rate + """Get validator max commission rate. Returns ------- @@ -513,8 +491,7 @@ class Validator: return self._max_rate def set_max_change_rate(self, rate): - """ - Set validator max commission change rate + """Set validator max commission change rate. Parameters ---------- @@ -529,8 +506,8 @@ class Validator: rate = self._sanitize_input(rate, True) try: rate = Decimal(rate) - except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, "Max change rate must be a number") from e + except (TypeError, InvalidOperation) as exception: + raise InvalidValidatorError(3, "Max change rate must be a number") from exception if rate < 0: raise InvalidValidatorError( 3, "Max change rate must be greater than or equal to 0" @@ -548,8 +525,7 @@ class Validator: self._max_change_rate = rate def get_max_change_rate(self) -> Decimal: - """ - Get validator max commission change rate + """Get validator max commission change rate. Returns ------- @@ -559,8 +535,7 @@ class Validator: return self._max_change_rate def set_rate(self, rate): - """ - Set validator commission rate + """Set validator commission rate. Parameters ---------- @@ -575,8 +550,8 @@ class Validator: rate = self._sanitize_input(rate, True) try: rate = Decimal(rate) - except (TypeError, InvalidOperation) as e: - raise InvalidValidatorError(3, "Rate must be a number") from e + except (TypeError, InvalidOperation) as exception: + raise InvalidValidatorError(3, "Rate must be a number") from exception if rate < 0: raise InvalidValidatorError(3, "Rate must be greater than or equal to 0") if self._max_rate: @@ -589,8 +564,7 @@ class Validator: self._rate = rate def get_rate(self) -> Decimal: - """ - Get validator commission rate + """Get validator commission rate. Returns ------- @@ -600,10 +574,9 @@ class Validator: return self._rate def does_validator_exist( - self, endpoint=_default_endpoint, timeout=_default_timeout + self, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ) -> bool: - """ - Check if validator exists on blockchain + """Check if validator exists on blockchain. Parameters ---------- @@ -628,8 +601,7 @@ class Validator: return False def load(self, info): - """ - Import validator information + """Import validator information. Parameters ---------- @@ -680,16 +652,15 @@ class Validator: self._bls_key_sigs = [] for key in info["bls-key-sigs"]: self.add_bls_key_sig(key) - except KeyError as e: - raise InvalidValidatorError(3, "Info has missing key") from e + except KeyError as exception: + raise InvalidValidatorError(3, "Info has missing key") from exception def load_from_blockchain( - self, endpoint=_default_endpoint, timeout=_default_timeout + self, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT ): - """ - Import validator information from blockchain with given address - At the moment, this is unable to fetch the BLS Signature, which is not implemented - in the Node API + """Import validator information from blockchain with given address At + the moment, this is unable to fetch the BLS Signature, which is not + implemented in the Node API. Parameters ---------- @@ -708,16 +679,16 @@ class Validator: raise InvalidValidatorError( 5, f"Validator does not exist on chain according to {endpoint}" ) - except (RPCError, RequestsError, RequestsTimeoutError) as e: + except (RPCError, RequestsError, RequestsTimeoutError) as exception: raise InvalidValidatorError( 5, "Error requesting validator information" - ) from e + ) from exception try: validator_info = get_validator_information(self._address, endpoint, timeout) - except (RPCError, RequestsError, RequestsTimeoutError) as e: + except (RPCError, RequestsError, RequestsTimeoutError) as exception: raise InvalidValidatorError( 5, "Error requesting validator information" - ) from e + ) from exception # Skip additional sanity checks when importing from chain try: @@ -738,14 +709,13 @@ class Validator: self._max_change_rate = Decimal(info["max-change-rate"]) self._rate = Decimal(info["rate"]) self._bls_keys = info["bls-public-keys"] - except KeyError as e: + except KeyError as exception: raise InvalidValidatorError( 5, "Error importing validator information from RPC result" - ) from e + ) from exception def export(self) -> dict: - """ - Export validator information as dict + """Export validator information as dict. Returns ------- @@ -770,11 +740,11 @@ class Validator: } return info - def sign_create_validator_transaction( + def sign_create_validator_transaction( # pylint: disable=too-many-arguments self, nonce, gas_price, gas_limit, private_key, chain_id=None ) -> SignedTransaction: - """ - Create but not post a transaction to Create the Validator using private_key + """Create but not post a transaction to Create the Validator using + private_key. Returns ------- @@ -799,7 +769,7 @@ class Validator: info["chainId"] = chain_id return sign_staking_transaction(info, private_key) - def sign_edit_validator_transaction( + def sign_edit_validator_transaction( # pylint: disable=too-many-arguments self, nonce, gas_price, @@ -811,8 +781,8 @@ class Validator: private_key, chain_id=None, ) -> SignedTransaction: - """ - Create but not post a transaction to Edit the Validator using private_key + """Create but not post a transaction to Edit the Validator using + private_key. Returns ------- From ca6a6ae1a803abd04eef3c68bcdbe00bde897062 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:31:50 +0000 Subject: [PATCH 20/23] chore(yapf): run yapf --- .style.yapf | 404 ++++++++++++++++++ pyhmy/__init__.py | 14 +- pyhmy/_version.py | 4 +- pyhmy/account.py | 185 +++++--- pyhmy/bech32/bech32.py | 116 +++-- pyhmy/blockchain.py | 541 ++++++++++++++++-------- pyhmy/cli.py | 240 ++++++----- pyhmy/constants.py | 2 +- pyhmy/contract.py | 72 +++- pyhmy/exceptions.py | 23 +- pyhmy/logging.py | 98 +++-- pyhmy/numbers.py | 19 +- pyhmy/rpc/exceptions.py | 24 +- pyhmy/rpc/request.py | 45 +- pyhmy/signing.py | 132 ++++-- pyhmy/staking.py | 323 +++++++++----- pyhmy/staking_signing.py | 323 +++++++------- pyhmy/staking_structures.py | 103 +++-- pyhmy/transaction.py | 290 ++++++++----- pyhmy/util.py | 136 +++--- pyhmy/validator.py | 388 +++++++++-------- tests/bech32-pyhmy/test_bech32.py | 4 +- tests/cli-pyhmy/test_cli.py | 57 +-- tests/logging-pyhmy/test_logging.py | 20 +- tests/numbers-pyhmy/test_numbers.py | 32 +- tests/request-pyhmy/test_request.py | 75 ++-- tests/sdk-pyhmy/conftest.py | 180 ++++---- tests/sdk-pyhmy/test_account.py | 145 ++++--- tests/sdk-pyhmy/test_blockchain.py | 457 ++++++++++---------- tests/sdk-pyhmy/test_contract.py | 84 ++-- tests/sdk-pyhmy/test_signing.py | 9 +- tests/sdk-pyhmy/test_staking.py | 272 ++++++------ tests/sdk-pyhmy/test_staking_signing.py | 12 +- tests/sdk-pyhmy/test_transaction.py | 235 +++++----- tests/sdk-pyhmy/test_validator.py | 132 +++--- tests/util-pyhmy/test_util.py | 43 +- 36 files changed, 3228 insertions(+), 2011 deletions(-) create mode 100644 .style.yapf diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000..108d85c --- /dev/null +++ b/.style.yapf @@ -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 + diff --git a/pyhmy/__init__.py b/pyhmy/__init__.py index 17593b8..12ddecc 100644 --- a/pyhmy/__init__.py +++ b/pyhmy/__init__.py @@ -7,13 +7,17 @@ import warnings from ._version import __version__ if sys.version_info.major < 3: - warnings.simplefilter("always", DeprecationWarning) + warnings.simplefilter( "always", DeprecationWarning ) warnings.warn( - DeprecationWarning("`pyhmy` does not support Python 2. Please use Python 3.") + DeprecationWarning( + "`pyhmy` does not support Python 2. Please use Python 3." + ) ) warnings.resetwarnings() -if sys.platform.startswith("win32") or sys.platform.startswith("cygwin"): - warnings.simplefilter("always", ImportWarning) - warnings.warn(ImportWarning("`pyhmy` does not work on Windows or Cygwin.")) +if sys.platform.startswith( "win32" ) or sys.platform.startswith( "cygwin" ): + warnings.simplefilter( "always", ImportWarning ) + warnings.warn( + ImportWarning( "`pyhmy` does not work on Windows or Cygwin." ) + ) warnings.resetwarnings() diff --git a/pyhmy/_version.py b/pyhmy/_version.py index 8f7cbe4..0287d4b 100644 --- a/pyhmy/_version.py +++ b/pyhmy/_version.py @@ -5,5 +5,5 @@ from incremental import Version -__version__ = Version("pyhmy", 20, 5, 20) -__all__ = ["__version__"] +__version__ = Version( "pyhmy", 20, 5, 20 ) +__all__ = [ "__version__" ] diff --git a/pyhmy/account.py b/pyhmy/account.py index 9a4f869..95f2ca8 100644 --- a/pyhmy/account.py +++ b/pyhmy/account.py @@ -13,7 +13,8 @@ from .bech32.bech32 import bech32_decode from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT -def is_valid_address(address) -> bool: + +def is_valid_address( address ) -> bool: """ Check if given string is valid one address NOTE: This function is NOT thread safe due to the C function used by the bech32 library. @@ -28,15 +29,19 @@ def is_valid_address(address) -> bool: bool Is valid address """ - if not address.startswith("one1"): + if not address.startswith( "one1" ): return False - hrp, _ = bech32_decode(address) + hrp, _ = bech32_decode( address ) if not hrp: return False return True -def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_balance( + address, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get current account balance. Parameters @@ -63,18 +68,24 @@ def get_balance(address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> https://api.hmny.io/#da8901d2-d237-4c3b-9d7d-10af9def05c4 """ method = "hmyv2_getBalance" - params = [address] + params = [ address ] try: balance = rpc_request( - method, params=params, endpoint=endpoint, timeout=timeout - )["result"] - return int(balance) # v2 returns the result as it is + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] + return int( balance ) # v2 returns the result as it is except TypeError as exception: # check will work if rpc returns None - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_balance_by_block( - address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get account balance for address at a given block number. @@ -105,18 +116,24 @@ def get_balance_by_block( https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/blockchain.go#L92 """ method = "hmyv2_getBalanceByBlockNumber" - params = [address, block_num] + params = [ address, block_num ] try: balance = rpc_request( - method, params=params, endpoint=endpoint, timeout=timeout - )["result"] - return int(balance) + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] + return int( balance ) except TypeError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_account_nonce( - address, block_num="latest", endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + block_num = "latest", + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get the account nonce. @@ -146,25 +163,34 @@ def get_account_nonce( https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L51 """ method = "hmyv2_getAccountNonce" - params = [address, block_num] + params = [ address, block_num ] try: - nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] - return int(nonce) + nonce = rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] + return int( nonce ) except TypeError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_nonce( - address, block_num="latest", endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + block_num = "latest", + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """See get_account_nonce.""" - return get_account_nonce(address, block_num, endpoint, timeout) + return get_account_nonce( address, block_num, endpoint, timeout ) def get_transaction_count( - address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get the number of transactions the given address has sent for the given block number Legacy for apiv1. For apiv2, please use @@ -197,18 +223,24 @@ def get_transaction_count( https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L69 """ method = "hmyv2_getTransactionCount" - params = [address, block_num] + params = [ address, block_num ] try: - nonce = rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] - return int(nonce) + nonce = rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] + return int( nonce ) except TypeError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_transactions_count( - address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + tx_type, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get the number of regular transactions from genesis of input type. @@ -240,18 +272,24 @@ def get_transactions_count( https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L114 """ method = "hmyv2_getTransactionsCount" - params = [address, tx_type] + params = [ address, tx_type ] try: tx_count = rpc_request( - method, params=params, endpoint=endpoint, timeout=timeout - )["result"] - return int(tx_count) + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] + return int( tx_count ) except TypeError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_staking_transactions_count( - address, tx_type, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + tx_type, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get the number of staking transactions from genesis of input type ("SENT", "RECEIVED", "ALL") @@ -284,14 +322,17 @@ def get_staking_transactions_count( https://github.com/harmony-one/harmony/blob/9f320436ff30d9babd957bc5f2e15a1818c86584/rpc/transaction.go#L134 """ method = "hmyv2_getStakingTransactionsCount" - params = [address, tx_type] + params = [ address, tx_type ] try: tx_count = rpc_request( - method, params=params, endpoint=endpoint, timeout=timeout - )["result"] - return int(tx_count) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] + return int( tx_count ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_transaction_history( # pylint: disable=too-many-arguments @@ -359,11 +400,14 @@ def get_transaction_history( # pylint: disable=too-many-arguments method = "hmyv2_getTransactionsHistory" try: tx_history = rpc_request( - method, params=params, endpoint=endpoint, timeout=timeout + method, + params = params, + endpoint = endpoint, + timeout = timeout ) - return tx_history["result"]["transactions"] + return tx_history[ "result" ][ "transactions" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_staking_transaction_history( # pylint: disable=too-many-arguments @@ -445,15 +489,21 @@ def get_staking_transaction_history( # pylint: disable=too-many-arguments method = "hmyv2_getStakingTransactionsHistory" try: stx_history = rpc_request( - method, params=params, endpoint=endpoint, timeout=timeout - )["result"] - return stx_history["staking_transactions"] + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] + return stx_history[ "staking_transactions" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_balance_on_all_shards( - address, skip_error=True, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + skip_error = True, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get current account balance in all shards & optionally report errors getting account balance for a shard. @@ -483,25 +533,37 @@ def get_balance_on_all_shards( ] """ balances = [] - sharding_structure = get_sharding_structure(endpoint=endpoint, timeout=timeout) + sharding_structure = get_sharding_structure( + endpoint = endpoint, + timeout = timeout + ) for shard in sharding_structure: try: balances.append( { - "shard": shard["shardID"], + "shard": shard[ "shardID" ], "balance": get_balance( - address, endpoint=shard["http"], timeout=timeout + address, + endpoint = shard[ "http" ], + timeout = timeout ), } ) - except (KeyError, RPCError, RequestsError, RequestsTimeoutError): + except ( KeyError, RPCError, RequestsError, RequestsTimeoutError ): if not skip_error: - balances.append({"shard": shard["shardID"], "balance": None}) + balances.append( + { + "shard": shard[ "shardID" ], + "balance": None + } + ) return balances def get_total_balance( - address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get total account balance on all shards. @@ -530,8 +592,11 @@ def get_total_balance( """ try: balances = get_balance_on_all_shards( - address, skip_error=False, endpoint=endpoint, timeout=timeout + address, + skip_error = False, + endpoint = endpoint, + timeout = timeout ) - return sum(b["balance"] for b in balances) + return sum( b[ "balance" ] for b in balances ) except TypeError as exception: raise RuntimeError from exception diff --git a/pyhmy/bech32/bech32.py b/pyhmy/bech32/bech32.py index 212705d..0a2f196 100644 --- a/pyhmy/bech32/bech32.py +++ b/pyhmy/bech32/bech32.py @@ -17,108 +17,106 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. - """Reference implementation for Bech32 and segwit addresses.""" - CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" -def bech32_polymod(values): +def bech32_polymod( values ): """Internal function that computes the Bech32 checksum.""" - generator = [0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3] + generator = [ 0x3B6A57B2, 0x26508E6D, 0x1EA119FA, 0x3D4233DD, 0x2A1462B3 ] chk = 1 for value in values: top = chk >> 25 - chk = (chk & 0x1FFFFFF) << 5 ^ value - for i in range(5): - chk ^= generator[i] if ((top >> i) & 1) else 0 + chk = ( chk & 0x1FFFFFF ) << 5 ^ value + for i in range( 5 ): + chk ^= generator[ i ] if ( ( top >> i ) & 1 ) else 0 return chk -def bech32_hrp_expand(hrp): +def bech32_hrp_expand( hrp ): """Expand the HRP into values for checksum computation.""" - return [ord(x) >> 5 for x in hrp] + [0] + [ord(x) & 31 for x in hrp] + return [ ord( x ) >> 5 for x in hrp ] + [ 0 + ] + [ ord( x ) & 31 for x in hrp ] -def bech32_verify_checksum(hrp, data): +def bech32_verify_checksum( hrp, data ): """Verify a checksum given HRP and converted data characters.""" - return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1 + return bech32_polymod( bech32_hrp_expand( hrp ) + data ) == 1 -def bech32_create_checksum(hrp, data): +def bech32_create_checksum( hrp, data ): """Compute the checksum values given HRP and data.""" - values = bech32_hrp_expand(hrp) + data - polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1 - return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)] + values = bech32_hrp_expand( hrp ) + data + polymod = bech32_polymod( values + [ 0, 0, 0, 0, 0, 0 ] ) ^ 1 + return [ ( polymod >> 5 * ( 5 - i ) ) & 31 for i in range( 6 ) ] -def bech32_encode(hrp, data): +def bech32_encode( hrp, data ): """Compute a Bech32 string given HRP and data values.""" - combined = data + bech32_create_checksum(hrp, data) - return hrp + "1" + "".join([CHARSET[d] for d in combined]) + combined = data + bech32_create_checksum( hrp, data ) + return hrp + "1" + "".join( [ CHARSET[ d ] for d in combined ] ) -def bech32_decode(bech): +def bech32_decode( bech ): """Validate a Bech32 string, and determine HRP and data.""" - if (any(ord(x) < 33 or ord(x) > 126 for x in bech)) or ( - bech.lower() != bech and bech.upper() != bech - ): - return (None, None) + if ( any( ord( x ) < 33 or ord( x ) > 126 for x in bech + ) ) or ( bech.lower() != bech and bech.upper() != bech ): + return ( None, None ) bech = bech.lower() - pos = bech.rfind("1") - if pos < 1 or pos + 7 > len(bech) or len(bech) > 90: - return (None, None) - if not all(x in CHARSET for x in bech[pos + 1 :]): - return (None, None) - hrp = bech[:pos] - data = [CHARSET.find(x) for x in bech[pos + 1 :]] - if not bech32_verify_checksum(hrp, data): - return (None, None) - return (hrp, data[:-6]) - - -def convertbits(data, frombits, tobits, pad=True): + pos = bech.rfind( "1" ) + if pos < 1 or pos + 7 > len( bech ) or len( bech ) > 90: + return ( None, None ) + if not all( x in CHARSET for x in bech[ pos + 1 : ] ): + return ( None, None ) + hrp = bech[ : pos ] + data = [ CHARSET.find( x ) for x in bech[ pos + 1 : ] ] + if not bech32_verify_checksum( hrp, data ): + return ( None, None ) + return ( hrp, data[ :-6 ] ) + + +def convertbits( data, frombits, tobits, pad = True ): """General power-of-2 base conversion.""" acc = 0 bits = 0 ret = [] - maxv = (1 << tobits) - 1 - max_acc = (1 << (frombits + tobits - 1)) - 1 + maxv = ( 1 << tobits ) - 1 + max_acc = ( 1 << ( frombits + tobits - 1 ) ) - 1 for value in data: - if value < 0 or (value >> frombits): + if value < 0 or ( value >> frombits ): return None - acc = ((acc << frombits) | value) & max_acc + acc = ( ( acc << frombits ) | value ) & max_acc bits += frombits while bits >= tobits: bits -= tobits - ret.append((acc >> bits) & maxv) + ret.append( ( acc >> bits ) & maxv ) if pad: if bits: - ret.append((acc << (tobits - bits)) & maxv) - elif bits >= frombits or ((acc << (tobits - bits)) & maxv): + ret.append( ( acc << ( tobits - bits ) ) & maxv ) + elif bits >= frombits or ( ( acc << ( tobits - bits ) ) & maxv ): return None return ret -def decode(hrp, addr): +def decode( hrp, addr ): """Decode a segwit address.""" - hrpgot, data = bech32_decode(addr) + hrpgot, data = bech32_decode( addr ) if hrpgot != hrp: - return (None, None) - decoded = convertbits(data[1:], 5, 8, False) - if decoded is None or len(decoded) < 2 or len(decoded) > 40: - return (None, None) - if data[0] > 16: - return (None, None) - if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32: - return (None, None) - return (data[0], decoded) - - -def encode(hrp, witver, witprog): + return ( None, None ) + decoded = convertbits( data[ 1 : ], 5, 8, False ) + if decoded is None or len( decoded ) < 2 or len( decoded ) > 40: + return ( None, None ) + if data[ 0 ] > 16: + return ( None, None ) + if data[ 0 ] == 0 and len( decoded ) != 20 and len( decoded ) != 32: + return ( None, None ) + return ( data[ 0 ], decoded ) + + +def encode( hrp, witver, witprog ): """Encode a segwit address.""" - ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5)) - if decode(hrp, ret) == (None, None): + ret = bech32_encode( hrp, [ witver ] + convertbits( witprog, 8, 5 ) ) + if decode( hrp, ret ) == ( None, None ): return None return ret diff --git a/pyhmy/blockchain.py b/pyhmy/blockchain.py index 05f43cd..a989be1 100644 --- a/pyhmy/blockchain.py +++ b/pyhmy/blockchain.py @@ -9,10 +9,14 @@ from .exceptions import InvalidRPCReplyError from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT + ############################# # Node / network level RPCs # ############################# -def get_bad_blocks(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list: +def get_bad_blocks( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> list: """[WIP] Get list of bad blocks in memory of specific node Known issues with RPC not returning correctly. @@ -38,12 +42,14 @@ def get_bad_blocks(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list: """ method = "hmyv2_getCurrentBadBlocks" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def chain_id(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: +def chain_id( endpoint = DEFAULT_ENDPOINT, timeout = DEFAULT_TIMEOUT ) -> dict: """Chain id of the chain. Parameters @@ -68,13 +74,16 @@ def chain_id(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: """ method = "hmyv2_chainId" try: - data = rpc_request(method, endpoint=endpoint, timeout=timeout) - return data["result"] + data = rpc_request( method, endpoint = endpoint, timeout = timeout ) + return data[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_node_metadata(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: +def get_node_metadata( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> dict: """Get config for the node. Parameters @@ -127,13 +136,16 @@ def get_node_metadata(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dic """ method = "hmyv2_getNodeMetadata" try: - metadata = rpc_request(method, endpoint=endpoint, timeout=timeout) - return metadata["result"] + metadata = rpc_request( method, endpoint = endpoint, timeout = timeout ) + return metadata[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_peer_info(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: +def get_peer_info( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> dict: """Get peer info for the node. Parameters @@ -165,12 +177,17 @@ def get_peer_info(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: """ method = "hmyv2_getPeerInfo" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def protocol_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def protocol_version( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get the current Harmony protocol version this node supports. Parameters @@ -196,13 +213,16 @@ def protocol_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: """ method = "hmyv2_protocolVersion" try: - value = rpc_request(method, endpoint=endpoint, timeout=timeout) - return value["result"] + value = rpc_request( method, endpoint = endpoint, timeout = timeout ) + return value[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_num_peers(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_num_peers( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get number of peers connected to the node. Parameters @@ -229,13 +249,19 @@ def get_num_peers(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: method = "net_peerCount" try: # Number of peers represented as a hex string return int( - rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ], + 16 ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_version( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get version of the EVM network (https://chainid.network/) Parameters @@ -262,13 +288,16 @@ def get_version(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: method = "net_version" try: return int( - rpc_request(method, endpoint=endpoint, timeout=timeout)["result"], 16 + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ], + 16 ) # this is hexadecimal - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool: +def in_sync( endpoint = DEFAULT_ENDPOINT, timeout = DEFAULT_TIMEOUT ) -> bool: """Whether the shard chain is in sync or syncing (not out of sync) Parameters @@ -293,12 +322,19 @@ def in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool: """ method = "hmyv2_inSync" try: - return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + return bool( + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def beacon_in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool: +def beacon_in_sync( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> bool: """Whether the beacon chain is in sync or syncing (not out of sync) Parameters @@ -323,12 +359,19 @@ def beacon_in_sync(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> bool: """ method = "hmyv2_beaconInSync" try: - return bool(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + return bool( + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_staking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_staking_epoch( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get epoch number when blockchain switches to EPoS election. Parameters @@ -358,13 +401,18 @@ def get_staking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int """ method = "hmyv2_getNodeMetadata" try: - data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - return int(data["chain-config"]["staking-epoch"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + data = rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + return int( data[ "chain-config" ][ "staking-epoch" ] ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_prestaking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_prestaking_epoch( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get epoch number when blockchain switches to allow staking features without election. @@ -395,16 +443,18 @@ def get_prestaking_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> """ method = "hmyv2_getNodeMetadata" try: - data = rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] - return int(data["chain-config"]["prestaking-epoch"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + data = rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + return int( data[ "chain-config" ][ "prestaking-epoch" ] ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception ######################## # Sharding information # ######################## -def get_shard(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_shard( endpoint = DEFAULT_ENDPOINT, timeout = DEFAULT_TIMEOUT ) -> int: """Get shard ID of the node. Parameters @@ -430,15 +480,16 @@ def get_shard(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: """ method = "hmyv2_getNodeMetadata" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"][ - "shard-id" - ] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ][ "shard-id" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_sharding_structure( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get network sharding structure. @@ -468,15 +519,20 @@ def get_sharding_structure( """ method = "hmyv2_getShardingStructure" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception ############################# # Current status of network # ############################# -def get_leader_address(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> str: +def get_leader_address( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> str: """Get current leader one address. Parameters @@ -502,13 +558,17 @@ def get_leader_address(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> st """ method = "hmyv2_getLeader" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def is_last_block( - block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> bool: """If the block at block_num is the last block. @@ -534,22 +594,25 @@ def is_last_block( ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L286 """ - params = [ - block_num, - ] + params = [ block_num, ] method = "hmyv2_isLastBlock" try: return bool( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def epoch_last_block( - epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + epoch, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Returns the number of the last block in the epoch. @@ -575,21 +638,25 @@ def epoch_last_block( ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L294 """ - params = [ - epoch, - ] + params = [ epoch, ] method = "hmyv2_epochLastBlock" try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_circulating_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_circulating_supply( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get current circulation supply of tokens in ONE. Parameters @@ -615,12 +682,17 @@ def get_circulating_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) - """ method = "hmyv2_getCirculatingSupply" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_total_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_total_supply( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get total number of pre-mined tokens. Parameters @@ -646,12 +718,17 @@ def get_total_supply(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: """ method = "hmyv2_getTotalSupply" try: - rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_block_number(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_block_number( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get current block number. Parameters @@ -677,12 +754,19 @@ def get_block_number(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: """ method = "hmyv2_blockNumber" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + return int( + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_current_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_current_epoch( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get current epoch number. Parameters @@ -708,12 +792,19 @@ def get_current_epoch(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int """ method = "hmyv2_getEpoch" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + return int( + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_last_cross_links(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list: +def get_last_cross_links( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> list: """Get last cross shard links. Parameters @@ -746,12 +837,17 @@ def get_last_cross_links(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> """ method = "hmyv2_getLastCrossLinks" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_gas_price(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_gas_price( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get network gas price. Parameters @@ -777,15 +873,22 @@ def get_gas_price(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: """ method = "hmyv2_gasPrice" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + return int( + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception ############## # Block RPCs # ############## -def get_latest_header(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: +def get_latest_header( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> dict: """Get block header of latest block. Parameters @@ -829,13 +932,17 @@ def get_latest_header(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dic """ method = "hmyv2_latestHeader" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_header_by_number( - block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get block header of block at block_num. @@ -862,17 +969,21 @@ def get_header_by_number( https://api.hmny.io/#01148e4f-72bb-426d-a123-718a161eaec0 """ method = "hmyv2_getHeaderByNumber" - params = [block_num] + params = [ block_num ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_latest_chain_headers( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get block header of latest block for beacon chain & shard chain. @@ -919,9 +1030,11 @@ def get_latest_chain_headers( """ method = "hmyv2_getLatestChainHeaders" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_by_number( # pylint: disable=too-many-arguments @@ -1005,11 +1118,14 @@ def get_block_by_number( # pylint: disable=too-many-arguments ] method = "hmyv2_getBlockByNumber" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_by_hash( # pylint: disable=too-many-arguments @@ -1062,15 +1178,20 @@ def get_block_by_hash( # pylint: disable=too-many-arguments ] method = "hmyv2_getBlockByHash" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_transaction_count_by_number( - block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get transaction count for specific block number. @@ -1099,20 +1220,25 @@ def get_block_transaction_count_by_number( ------------- https://api.hmny.io/#26c5adfb-d757-4595-9eb7-c6efef63df32 """ - params = [block_num] + params = [ block_num ] method = "hmyv2_getBlockTransactionCountByNumber" try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_transaction_count_by_hash( - block_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get transaction count for specific block hash. @@ -1141,20 +1267,25 @@ def get_block_transaction_count_by_hash( ------------- https://api.hmny.io/#66c68844-0208-49bb-a83b-08722bc113eb """ - params = [block_hash] + params = [ block_hash ] method = "hmyv2_getBlockTransactionCountByHash" try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_staking_transaction_count_by_number( - block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get staking transaction count for specific block number. @@ -1183,20 +1314,25 @@ def get_block_staking_transaction_count_by_number( ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L494 """ - params = [block_num] + params = [ block_num ] method = "hmyv2_getBlockStakingTransactionCountByNumber" try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_staking_transaction_count_by_hash( - block_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get staking transaction count for specific block hash. @@ -1225,16 +1361,19 @@ def get_block_staking_transaction_count_by_hash( ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/transaction.go#L523 """ - params = [block_hash] + params = [ block_hash ] method = "hmyv2_getBlockStakingTransactionCountByHash" try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_blocks( # pylint: disable=too-many-arguments @@ -1293,15 +1432,20 @@ def get_blocks( # pylint: disable=too-many-arguments ] method = "hmyv2_getBlocks" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_signers( - block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of block signers for specific block number. @@ -1328,18 +1472,23 @@ def get_block_signers( ------------- https://api.hmny.io/#1e4b5f41-9db6-4dea-92fb-4408db78e622 """ - params = [block_num] + params = [ block_num ] method = "hmyv2_getBlockSigners" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_block_signers_keys( - block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of block signer public bls keys for specific block number. @@ -1366,18 +1515,24 @@ def get_block_signers_keys( ------------- https://api.hmny.io/#9f9c8298-1a4e-4901-beac-f34b59ed02f1 """ - params = [block_num] + params = [ block_num ] method = "hmyv2_getBlockSignerKeys" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def is_block_signer( - block_num, address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + address, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> bool: """Determine if the account at address is a signer for the block at block_num. @@ -1406,18 +1561,23 @@ def is_block_signer( ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L368 """ - params = [block_num, address] + params = [ block_num, address ] method = "hmyv2_isBlockSigner" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_signed_blocks( - address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> bool: """The number of blocks a particular validator signed for last blocksPeriod (1 epoch) @@ -1444,19 +1604,26 @@ def get_signed_blocks( ------------- https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/blockchain.go#L406 """ - params = [address] + params = [ address ] method = "hmyv2_getSignedBlocks" try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: +def get_validators( + epoch, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> dict: """Get list of validators for specific epoch number. Parameters @@ -1485,18 +1652,23 @@ def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> ------------- https://api.hmny.io/#4dfe91ad-71fa-4c7d-83f3-d1c86a804da5 """ - params = [epoch] + params = [ epoch ] method = "hmyv2_getValidators" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_validator_keys( - epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + epoch, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of validator public bls keys for specific epoch number. @@ -1523,11 +1695,14 @@ def get_validator_keys( ------------- https://api.hmny.io/#1439b580-fa3c-4d44-a79d-303390997a8c """ - params = [epoch] + params = [ epoch ] method = "hmyv2_getValidatorKeys" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception diff --git a/pyhmy/cli.py b/pyhmy/cli.py index 55adae8..79bda37 100644 --- a/pyhmy/cli.py +++ b/pyhmy/cli.py @@ -53,7 +53,7 @@ import requests from .util import get_bls_build_variables, get_gopath -if sys.platform.startswith("linux"): +if sys.platform.startswith( "linux" ): _libs = { "libbls384_256.so", "libcrypto.so.10", @@ -77,7 +77,6 @@ ARG_PREFIX = "__PYHMY_ARG_PREFIX__" environment = os.environ.copy() # The environment for the CLI to execute in. - # completely remove caching... # we need to improve getting address better internally to REDUCE single calls.... # def _cache_and_lock_accounts_keystore(fn): @@ -102,7 +101,8 @@ environment = os.environ.copy() # The environment for the CLI to execute in. # return wrap -def account_keystore_path(value=None): + +def account_keystore_path( value = None ): """ Gets or sets the ACCOUNT_KEYSTORE_PATH """ @@ -112,7 +112,8 @@ def account_keystore_path(value=None): account_keystore_path.value = value return account_keystore_path.value -def binary_path(value=None): + +def binary_path( value = None ): """ Gets or sets the BINARY_PATH """ @@ -122,6 +123,7 @@ def binary_path(value=None): binary_path.value = value return binary_path.value + def _get_current_accounts_keystore(): """Internal function that gets the current keystore from the CLI. @@ -129,28 +131,32 @@ def _get_current_accounts_keystore(): values are their 'one1...' addresses. """ curr_addresses = {} - response = single_call("hmy keys list") - lines = response.split("\n") - if "NAME" not in lines[0] or "ADDRESS" not in lines[0]: - raise ValueError("Name or Address not found on first line of key list") - if lines[1] != "": - raise ValueError("Unknown format: No blank line between label and data") - for line in lines[2:]: - columns = line.split("\t") - if len(columns) != 2: + response = single_call( "hmy keys list" ) + lines = response.split( "\n" ) + if "NAME" not in lines[ 0 ] or "ADDRESS" not in lines[ 0 ]: + raise ValueError( + "Name or Address not found on first line of key list" + ) + if lines[ 1 ] != "": + raise ValueError( + "Unknown format: No blank line between label and data" + ) + for line in lines[ 2 : ]: + columns = line.split( "\t" ) + if len( columns ) != 2: break # Done iterating through all of the addresses. name, address = columns - curr_addresses[name.strip()] = address + curr_addresses[ name.strip() ] = address return curr_addresses def _set_account_keystore_path(): """Internal function to set the account keystore path according to the binary.""" - response = single_call("hmy keys location").strip() - if not os.path.exists(response): - os.mkdir(response) - account_keystore_path(response) + response = single_call( "hmy keys location" ).strip() + if not os.path.exists( response ): + os.mkdir( response ) + account_keystore_path( response ) def _sync_accounts(): @@ -159,19 +165,19 @@ def _sync_accounts(): new_keystore = _get_current_accounts_keystore() for key, value in new_keystore.items(): if key not in _accounts: - _accounts[key] = value - acc_keys_to_remove = [k for k in _accounts if k not in new_keystore] + _accounts[ key ] = value + acc_keys_to_remove = [ k for k in _accounts if k not in new_keystore ] for key in acc_keys_to_remove: - del _accounts[key] + del _accounts[ key ] -def _make_call_command(command): +def _make_call_command( command ): """Internal function that processes a command String or String Arg List for underlying pexpect or subprocess call. Note that single quote is not respected for strings. """ - if isinstance(command, list): + if isinstance( command, list ): command_toks = command else: all_strings = sorted( @@ -179,18 +185,23 @@ def _make_call_command(command): key=lambda e: len(e), # pylint: disable=unnecessary-lambda reverse=True ) - for i, string in enumerate(all_strings): - command = command.replace(string, f"{ARG_PREFIX}_{i}") - command_toks_prefix = [el for el in command.split(" ") if el] + for i, string in enumerate( all_strings ): + command = command.replace( string, f"{ARG_PREFIX}_{i}" ) + command_toks_prefix = [ el for el in command.split( " " ) if el ] command_toks = [] for element in command_toks_prefix: - if element.startswith(f'"{ARG_PREFIX}_') and element.endswith('"'): - index = int(element.replace(f'"{ARG_PREFIX}_', "").replace('"', "")) - command_toks.append(all_strings[index]) + if element.startswith( f'"{ARG_PREFIX}_' + ) and element.endswith( '"' ): + index = int( + element.replace( f'"{ARG_PREFIX}_', + "" ).replace( '"', + "" ) + ) + command_toks.append( all_strings[ index ] ) else: - command_toks.append(element) - if re.match(".*hmy", command_toks[0]): - command_toks = command_toks[1:] + command_toks.append( element ) + if re.match( ".*hmy", command_toks[ 0 ] ): + command_toks = command_toks[ 1 : ] return command_toks @@ -204,41 +215,46 @@ def get_accounts_keystore(): return _accounts -def is_valid_binary(path): +def is_valid_binary( path ): """ :param path: Path to the Harmony CLI binary (absolute or relative). :return: If the file at the path is a CLI binary. """ - path = os.path.realpath(path) - os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) + path = os.path.realpath( path ) + os.chmod( path, os.stat( path ).st_mode | stat.S_IEXEC ) try: with subprocess.Popen( - [path, "version"], - env=environment, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + [ path, + "version" ], + env = environment, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, ) as proc: _, err = proc.communicate() if not err: return False return "harmony" in err.decode().strip().lower() - except (OSError, subprocess.CalledProcessError, subprocess.SubprocessError): + except ( + OSError, + subprocess.CalledProcessError, + subprocess.SubprocessError + ): return False -def set_binary(path): +def set_binary( path ): """ :param path: The path of the CLI binary to use. :returns If the binary has been set. Note that the exposed keystore will be updated accordingly. """ - path = os.path.realpath(path) - assert os.path.exists(path) - os.chmod(path, os.stat(path).st_mode | stat.S_IEXEC) - if not is_valid_binary(path): + path = os.path.realpath( path ) + assert os.path.exists( path ) + os.chmod( path, os.stat( path ).st_mode | stat.S_IEXEC ) + if not is_valid_binary( path ): return False - binary_path(path) + binary_path( path ) _set_account_keystore_path() _sync_accounts() return True @@ -248,7 +264,7 @@ def get_binary_path(): """ :return: The absolute path of the CLI binary. """ - return os.path.abspath(binary_path()) + return os.path.abspath( binary_path() ) def get_version(): @@ -256,10 +272,11 @@ def get_version(): :return: The version string of the CLI binary. """ with subprocess.Popen( - [binary_path(), "version"], - env=environment, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, + [ binary_path(), + "version" ], + env = environment, + stdout = subprocess.PIPE, + stderr = subprocess.PIPE, ) as proc: _, err = proc.communicate() if not err: @@ -274,10 +291,10 @@ def get_account_keystore_path(): """ :return: The absolute path to the account keystore of the CLI binary. """ - return os.path.abspath(account_keystore_path()) + return os.path.abspath( account_keystore_path() ) -def check_address(address): +def check_address( address ): """ :param address: A 'one1...' address. :return: Boolean of if the address is in the CLI's keystore. @@ -285,15 +302,15 @@ def check_address(address): return address in get_accounts_keystore().values() -def get_address(name): +def get_address( name ): """ :param name: The alias of a key used in the CLI's keystore. :return: The associated 'one1...' address. """ - return get_accounts_keystore().get(name, None) + return get_accounts_keystore().get( name, None ) -def get_accounts(address): +def get_accounts( address ): """ :param address: The 'one1...' address :return: A list of account names associated with the param @@ -301,38 +318,42 @@ def get_accounts(address): Note that a list of account names is needed because 1 address can have multiple names within the CLI's keystore. """ - return [acc for acc, addr in get_accounts_keystore().items() if address == addr] + return [ + acc for acc, + addr in get_accounts_keystore().items() if address == addr + ] -def remove_account(name): +def remove_account( name ): """Note that this edits the keystore directly since there is currently no way to remove an address using the CLI. :param name: The alias of a key used in the CLI's keystore. :raises RuntimeError: If it failed to remove an account. """ - if not get_address(name): + if not get_address( name ): return keystore_path = f"{get_account_keystore_path()}/{name}" try: - shutil.rmtree(keystore_path) - except (shutil.Error, FileNotFoundError) as err: + shutil.rmtree( keystore_path ) + except ( shutil.Error, FileNotFoundError ) as err: raise RuntimeError( - f"Failed to delete dir: {keystore_path}\n" f"\tException: {err}" + f"Failed to delete dir: {keystore_path}\n" + f"\tException: {err}" ) from err _sync_accounts() -def remove_address(address): +def remove_address( address ): """ :param address: The 'one1...' address to be removed. """ - for name in get_accounts(address): - remove_account(name) + for name in get_accounts( address ): + remove_account( name ) _sync_accounts() -def single_call(command, timeout=60, error_ok=False): +def single_call( command, timeout = 60, error_ok = False ): """ :param command: String or String Arg List of command to execute on CLI. :param timeout: Optional timeout in seconds @@ -340,40 +361,47 @@ def single_call(command, timeout=60, error_ok=False): :returns: Decoded string of response from hmy CLI call :raises: RuntimeError if bad command """ - command_toks = [binary_path()] + _make_call_command(command) + command_toks = [ binary_path() ] + _make_call_command( command ) try: return subprocess.check_output( - command_toks, env=environment, timeout=timeout + command_toks, + env = environment, + timeout = timeout ).decode() except subprocess.CalledProcessError as err: if not error_ok: raise RuntimeError( - f"Bad CLI args: `{command}`\n " f"\tException: {err}" + f"Bad CLI args: `{command}`\n " + f"\tException: {err}" ) from err return err.output.decode() -def expect_call(command, timeout=60): +def expect_call( command, timeout = 60 ): """ :param command: String or String Arg List of command to execute on CLI. :param timeout: Optional timeout in seconds :returns: A pexpect child program :raises: RuntimeError if bad command """ - command_toks = _make_call_command(command) + command_toks = _make_call_command( command ) try: proc = pexpect.spawn( - f"{binary_path()}", command_toks, env=environment, timeout=timeout + f"{binary_path()}", + command_toks, + env = environment, + timeout = timeout ) proc.delaybeforesend = None except pexpect.ExceptionPexpect as err: raise RuntimeError( - f"Bad CLI args: `{command}`\n " f"\tException: {err}" + f"Bad CLI args: `{command}`\n " + f"\tException: {err}" ) from err return proc -def download(path="./bin/hmy", replace=True, verbose=True): +def download( path = "./bin/hmy", replace = True, verbose = True ): """Download the CLI binary to the specified path. Related files will be saved in the same directory. @@ -382,60 +410,74 @@ def download(path="./bin/hmy", replace=True, verbose=True): :param verbose: A flag to enable a report message once the binary is downloaded. :returns the environment to run the saved CLI binary. """ - path = os.path.realpath(path) - parent_dir = Path(path).parent + path = os.path.realpath( path ) + parent_dir = Path( path ).parent assert not os.path.isdir( path ), f"path `{path}` must specify a file, not a directory." - if not os.path.exists(path) or replace: + if not os.path.exists( path ) or replace: old_cwd = os.getcwd() - os.makedirs(parent_dir, exist_ok=True) - os.chdir(parent_dir) - hmy_script_path = os.path.join(parent_dir, "hmy.sh") - with open(hmy_script_path, "w", encoding='utf8') as script_file: + os.makedirs( parent_dir, exist_ok = True ) + os.chdir( parent_dir ) + hmy_script_path = os.path.join( parent_dir, "hmy.sh" ) + with open( hmy_script_path, "w", encoding = 'utf8' ) as script_file: script_file.write( requests.get( "https://raw.githubusercontent.com/harmony-one/go-sdk/master/scripts/hmy.sh" ).content.decode() ) - os.chmod(hmy_script_path, os.stat(hmy_script_path).st_mode | stat.S_IEXEC) + os.chmod( + hmy_script_path, + os.stat( hmy_script_path ).st_mode | stat.S_IEXEC + ) same_name_file = False if ( - os.path.exists(os.path.join(parent_dir, "hmy")) and Path(path).name != "hmy" + os.path.exists( os.path.join( parent_dir, + "hmy" ) ) and + Path( path ).name != "hmy" ): # Save same name file. same_name_file = True os.rename( - os.path.join(parent_dir, "hmy"), os.path.join(parent_dir, ".hmy_tmp") + os.path.join( parent_dir, + "hmy" ), + os.path.join( parent_dir, + ".hmy_tmp" ) ) if verbose: - subprocess.call([hmy_script_path, "-d"]) + subprocess.call( [ hmy_script_path, "-d" ] ) else: - with open(os.devnull, "w", encoding = "UTF-8") as devnull: + with open( os.devnull, "w", encoding = "UTF-8" ) as devnull: subprocess.call( - [hmy_script_path, "-d"], - stdout=devnull, - stderr=subprocess.STDOUT, + [ hmy_script_path, + "-d" ], + stdout = devnull, + stderr = subprocess.STDOUT, ) - os.rename(os.path.join(parent_dir, "hmy"), path) + os.rename( os.path.join( parent_dir, "hmy" ), path ) if same_name_file: os.rename( - os.path.join(parent_dir, ".hmy_tmp"), os.path.join(parent_dir, "hmy") + os.path.join( parent_dir, + ".hmy_tmp" ), + os.path.join( parent_dir, + "hmy" ) ) if verbose: - print(f"Saved harmony binary to: `{path}`") - os.chdir(old_cwd) + print( f"Saved harmony binary to: `{path}`" ) + os.chdir( old_cwd ) env = os.environ.copy() - if sys.platform.startswith("darwin"): # Dynamic linking for darwin + if sys.platform.startswith( "darwin" ): # Dynamic linking for darwin try: - files_in_parent_dir = set(os.listdir(parent_dir)) - if files_in_parent_dir.intersection(_libs) == _libs: - env["DYLD_FALLBACK_LIBRARY_PATH"] = parent_dir + files_in_parent_dir = set( os.listdir( parent_dir ) ) + if files_in_parent_dir.intersection( _libs ) == _libs: + env[ "DYLD_FALLBACK_LIBRARY_PATH" ] = parent_dir elif os.path.exists( f"{get_gopath()}/src/github.com/harmony-one/bls" - ) and os.path.exists(f"{get_gopath()}/src/github.com/harmony-one/mcl"): - env.update(get_bls_build_variables()) + ) and os.path.exists( + f"{get_gopath()}/src/github.com/harmony-one/mcl" + ): + env.update( get_bls_build_variables() ) else: raise RuntimeWarning( f"Could not get environment for downloaded hmy CLI at `{path}`" diff --git a/pyhmy/constants.py b/pyhmy/constants.py index 3bd545e..f4bc7ea 100644 --- a/pyhmy/constants.py +++ b/pyhmy/constants.py @@ -15,4 +15,4 @@ IDENTITY_CHAR_LIMIT = 140 WEBSITE_CHAR_LIMIT = 140 SECURITY_CONTACT_CHAR_LIMIT = 140 DETAILS_CHAR_LIMIT = 280 -MIN_REQUIRED_DELEGATION = int(10000 * 1e18) +MIN_REQUIRED_DELEGATION = int( 10000 * 1e18 ) diff --git a/pyhmy/contract.py b/pyhmy/contract.py index b5849cc..781c8b3 100644 --- a/pyhmy/contract.py +++ b/pyhmy/contract.py @@ -76,11 +76,14 @@ def call( # pylint: disable=too-many-arguments ] method = "hmyv2_call" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def estimate_gas( # pylint: disable=too-many-arguments @@ -141,17 +144,23 @@ def estimate_gas( # pylint: disable=too-many-arguments method = "hmyv2_estimateGas" try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ], + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ], 16, ) except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_code( - address, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> str: """Get the code stored at the given address in the state for the given block number. @@ -182,18 +191,25 @@ def get_code( https://api.hmny.io/?version=latest#e13e9d78-9322-4dc8-8917-f2e721a8e556 https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L59 """ - params = [address, block_num] + params = [ address, block_num ] method = "hmyv2_getCode" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_storage_at( - address, key, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + key, + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> str: """Get the storage from the state at the given address, the key and the block number. @@ -226,18 +242,23 @@ def get_storage_at( https://api.hmny.io/?version=latest#fa8ac8bd-952d-4149-968c-857ca76da43f https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/contract.go#L84 """ - params = [address, key, block_num] + params = [ address, key, block_num ] method = "hmyv2_getStorageAt" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_contract_address_from_hash( - tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + tx_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> str: """Get address of the contract which was deployed in the transaction represented by tx_hash. @@ -266,6 +287,11 @@ def get_contract_address_from_hash( https://github.com/harmony-one/harmony-test/blob/master/localnet/rpc_tests/test_contract.py#L36 """ try: - return get_transaction_receipt(tx_hash, endpoint, timeout)["contractAddress"] + return get_transaction_receipt( tx_hash, + endpoint, + timeout )[ "contractAddress" ] except KeyError as exception: - raise InvalidRPCReplyError("hmyv2_getTransactionReceipt", endpoint) from exception + raise InvalidRPCReplyError( + "hmyv2_getTransactionReceipt", + endpoint + ) from exception diff --git a/pyhmy/exceptions.py b/pyhmy/exceptions.py index b8a5fb1..37edf55 100644 --- a/pyhmy/exceptions.py +++ b/pyhmy/exceptions.py @@ -2,16 +2,16 @@ Exceptions used by pyhmy """ -class InvalidRPCReplyError(RuntimeError): + +class InvalidRPCReplyError( RuntimeError ): """Exception raised when RPC call returns unexpected result Generally indicates Harmony API has been updated & pyhmy library needs to be updated as well.""" - - def __init__(self, method, endpoint): - super().__init__(f"Unexpected reply for {method} from {endpoint}") + def __init__( self, method, endpoint ): + super().__init__( f"Unexpected reply for {method} from {endpoint}" ) -class InvalidValidatorError(ValueError): +class InvalidValidatorError( ValueError ): """Exception raised Validator does not pass sanity checks.""" errors = { @@ -22,18 +22,17 @@ class InvalidValidatorError(ValueError): 5: "Unable to import validator information from blockchain", } - def __init__(self, err_code, msg): + def __init__( self, err_code, msg ): self.code = err_code self.msg = msg - super().__init__(msg) + super().__init__( msg ) - def __str__(self): + def __str__( self ): return f"[Errno {self.code}] {self.errors[self.code]}: {self.msg}" -class TxConfirmationTimedoutError(AssertionError): +class TxConfirmationTimedoutError( AssertionError ): """Exception raised when a transaction is sent to the chain But not confirmed during the timeout period specified.""" - - def __init__(self, msg): - super().__init__(f"{msg}") + def __init__( self, msg ): + super().__init__( f"{msg}" ) diff --git a/pyhmy/logging.py b/pyhmy/logging.py index b49e6c1..e9f29de 100644 --- a/pyhmy/logging.py +++ b/pyhmy/logging.py @@ -10,37 +10,41 @@ import logging import logging.handlers -class _GZipRotator: # pylint: disable=too-few-public-methods - def __call__(self, source, dest): - os.rename(source, dest) - with open(dest, "rb") as f_in: - with gzip.open(f"{dest}.gz", "wb") as f_out: - f_out.writelines(f_in) - os.remove(dest) +class _GZipRotator: # pylint: disable=too-few-public-methods + def __call__( self, source, dest ): + os.rename( source, dest ) + with open( dest, "rb" ) as f_in: + with gzip.open( f"{dest}.gz", "wb" ) as f_out: + f_out.writelines( f_in ) + os.remove( dest ) -class ControlledLogger: # pylint: disable=too-many-instance-attributes +class ControlledLogger: # pylint: disable=too-many-instance-attributes """A simple logger that only writes to file when the 'write' method is called.""" - - def __init__(self, logger_name, log_dir, backup_count=5): + def __init__( self, logger_name, log_dir, backup_count = 5 ): """ :param logger_name: The name of the logger and logfile :param log_dir: The directory in which to save this log file (can be abs or relative). """ - if log_dir.endswith("/"): - log_dir = log_dir[:-1] - log_dir = os.path.realpath(log_dir) - os.makedirs(log_dir, exist_ok=True) + if log_dir.endswith( "/" ): + log_dir = log_dir[ :-1 ] + log_dir = os.path.realpath( log_dir ) + os.makedirs( log_dir, exist_ok = True ) handler = logging.handlers.TimedRotatingFileHandler( - f"{log_dir}/{logger_name}.log", "midnight", 1, backupCount=backup_count + f"{log_dir}/{logger_name}.log", + "midnight", + 1, + backupCount = backup_count + ) + handler.setFormatter( + logging.Formatter( "%(levelname)s - %(message)s" ) ) - handler.setFormatter(logging.Formatter("%(levelname)s - %(message)s")) handler.rotator = _GZipRotator() self.filename = handler.baseFilename - self.logger = logging.getLogger(logger_name) - self.logger.addHandler(handler) + self.logger = logging.getLogger( logger_name ) + self.logger.addHandler( handler ) self._lock = threading.Lock() self.filepath = f"{log_dir}/{logger_name}.log" self.info_buffer = [] @@ -48,88 +52,92 @@ class ControlledLogger: # pylint: disable=too-many-instance-attributes self.warning_buffer = [] self.error_buffer = [] - def __repr__(self): + def __repr__( self ): return f"" - def _clear(self): + def _clear( self ): """Internal method to clear the log buffer.""" self.info_buffer.clear() self.debug_buffer.clear() self.warning_buffer.clear() self.error_buffer.clear() - def info(self, msg): + def info( self, msg ): """ :param msg: The info message to log """ with self._lock: self.info_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + f"[{threading.get_ident()}] " + f"{datetime.datetime.utcnow()} : {msg}" ) - def debug(self, msg): + def debug( self, msg ): """ :param msg: The debug message to log """ with self._lock: self.debug_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + f"[{threading.get_ident()}] " + f"{datetime.datetime.utcnow()} : {msg}" ) - def warning(self, msg): + def warning( self, msg ): """ :param msg: The warning message to log """ with self._lock: self.warning_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + f"[{threading.get_ident()}] " + f"{datetime.datetime.utcnow()} : {msg}" ) - def error(self, msg): + def error( self, msg ): """ :param msg: The error message to log """ with self._lock: self.error_buffer.append( - f"[{threading.get_ident()}] " f"{datetime.datetime.utcnow()} : {msg}" + f"[{threading.get_ident()}] " + f"{datetime.datetime.utcnow()} : {msg}" ) - def print_info(self): + def print_info( self ): """Prints the current info buffer but does not flush it to log file.""" - print("\n".join(self.info_buffer)) + print( "\n".join( self.info_buffer ) ) - def print_debug(self): + def print_debug( self ): """Prints the current debug buffer but does not flush it to log file.""" - print("\n".join(self.debug_buffer)) + print( "\n".join( self.debug_buffer ) ) - def print_warning(self): + def print_warning( self ): """Prints the current warning buffer but does not flush it to log file.""" - print("\n".join(self.warning_buffer)) + print( "\n".join( self.warning_buffer ) ) - def print_error(self): + def print_error( self ): """Prints the current error buffer but does not flush it to log file.""" - print("\n".join(self.error_buffer)) + print( "\n".join( self.error_buffer ) ) - def write(self): + def write( self ): """Flushes ALL of the log buffers to the log file via the logger. Note that directly after this method call, the respective prints will print nothing since all log messages are flushed to file. """ with self._lock: - self.logger.setLevel(logging.DEBUG) + self.logger.setLevel( logging.DEBUG ) for line in self.debug_buffer: - self.logger.debug(line) - self.logger.setLevel(logging.WARNING) + self.logger.debug( line ) + self.logger.setLevel( logging.WARNING ) for line in self.warning_buffer: - self.logger.warning(line) - self.logger.setLevel(logging.ERROR) + self.logger.warning( line ) + self.logger.setLevel( logging.ERROR ) for line in self.error_buffer: - self.logger.error(line) - self.logger.setLevel(logging.INFO) + self.logger.error( line ) + self.logger.setLevel( logging.INFO ) for line in self.info_buffer: - self.logger.info(line) + self.logger.info( line ) self._clear() diff --git a/pyhmy/numbers.py b/pyhmy/numbers.py index de413ef..d1577d7 100644 --- a/pyhmy/numbers.py +++ b/pyhmy/numbers.py @@ -5,11 +5,10 @@ For more granular conversions, see Web3.toWei from decimal import Decimal +_conversion_unit = Decimal( 1e18 ) -_conversion_unit = Decimal(1e18) - -def convert_atto_to_one(atto) -> Decimal: +def convert_atto_to_one( atto ) -> Decimal: """Convert ATTO to ONE. Parameters @@ -23,12 +22,12 @@ def convert_atto_to_one(atto) -> Decimal: decimal Converted value in ONE """ - if isinstance(atto, float): - atto = int(atto) - return Decimal(atto) / _conversion_unit + if isinstance( atto, float ): + atto = int( atto ) + return Decimal( atto ) / _conversion_unit -def convert_one_to_atto(one) -> Decimal: +def convert_one_to_atto( one ) -> Decimal: """Convert ONE to ATTO. Parameters @@ -41,6 +40,6 @@ def convert_one_to_atto(one) -> Decimal: decimal Converted value in ATTO """ - if isinstance(one, float): - one = str(one) - return Decimal(one) * _conversion_unit + if isinstance( one, float ): + one = str( one ) + return Decimal( one ) * _conversion_unit diff --git a/pyhmy/rpc/exceptions.py b/pyhmy/rpc/exceptions.py index fbbca44..a5b08a5 100644 --- a/pyhmy/rpc/exceptions.py +++ b/pyhmy/rpc/exceptions.py @@ -4,23 +4,23 @@ RPC Specific Exceptions import requests -class RPCError(RuntimeError): - """Exception raised when RPC call returns an error.""" - def __init__(self, method, endpoint, error): +class RPCError( RuntimeError ): + """Exception raised when RPC call returns an error.""" + def __init__( self, method, endpoint, error ): self.error = error - super().__init__(f"Error in reply from {endpoint}: {method} returned {error}") + super().__init__( + f"Error in reply from {endpoint}: {method} returned {error}" + ) -class RequestsError(requests.exceptions.RequestException): +class RequestsError( requests.exceptions.RequestException ): """Wrapper for requests lib exceptions.""" + def __init__( self, endpoint ): + super().__init__( f"Error connecting to {endpoint}" ) - def __init__(self, endpoint): - super().__init__(f"Error connecting to {endpoint}") - -class RequestsTimeoutError(requests.exceptions.Timeout): +class RequestsTimeoutError( requests.exceptions.Timeout ): """Wrapper for requests lib Timeout exceptions.""" - - def __init__(self, endpoint): - super().__init__(f"Error connecting to {endpoint}") + def __init__( self, endpoint ): + super().__init__( f"Error connecting to {endpoint}" ) diff --git a/pyhmy/rpc/request.py b/pyhmy/rpc/request.py index 30384cd..a86de58 100644 --- a/pyhmy/rpc/request.py +++ b/pyhmy/rpc/request.py @@ -11,7 +11,10 @@ from ..constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT def base_request( - method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + method, + params = None, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> str: """Basic RPC request. @@ -42,30 +45,40 @@ def base_request( """ if params is None: params = [] - elif not isinstance(params, list): - raise TypeError(f"invalid type {params.__class__}") + elif not isinstance( params, list ): + raise TypeError( f"invalid type {params.__class__}" ) try: - payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} - headers = {"Content-Type": "application/json"} + payload = { + "id": "1", + "jsonrpc": "2.0", + "method": method, + "params": params + } + headers = { + "Content-Type": "application/json" + } resp = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) return resp.content except requests.exceptions.Timeout as err: - raise RequestsTimeoutError(endpoint) from err + raise RequestsTimeoutError( endpoint ) from err except requests.exceptions.RequestException as err: - raise RequestsError(endpoint) from err + raise RequestsError( endpoint ) from err def rpc_request( - method, params=None, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + method, + params = None, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """RPC request. @@ -100,12 +113,12 @@ def rpc_request( -------- base_request """ - raw_resp = base_request(method, params, endpoint, timeout) + raw_resp = base_request( method, params, endpoint, timeout ) try: - resp = json.loads(raw_resp) + resp = json.loads( raw_resp ) if "error" in resp: - raise RPCError(method, endpoint, str(resp["error"])) + raise RPCError( method, endpoint, str( resp[ "error" ] ) ) return resp except json.decoder.JSONDecodeError as err: - raise RPCError(method, endpoint, raw_resp) from err + raise RPCError( method, endpoint, raw_resp ) from err diff --git a/pyhmy/signing.py b/pyhmy/signing.py index 7e96128..693a494 100644 --- a/pyhmy/signing.py +++ b/pyhmy/signing.py @@ -38,25 +38,34 @@ HARMONY_FORMATTERS = dict( ) -class UnsignedHarmonyTxData(HashableRLP): +class UnsignedHarmonyTxData( HashableRLP ): """ Unsigned Harmony transaction data Includes `shardID` and `toShardID` as the difference against Eth """ fields = ( - ("nonce", big_endian_int), - ("gasPrice", big_endian_int), - ("gas", big_endian_int), - ("shardID", big_endian_int), - ("toShardID", big_endian_int), - ("to", Binary.fixed_length(20, allow_empty=True)), - ("value", big_endian_int), - ("data", binary), + ( "nonce", + big_endian_int ), + ( "gasPrice", + big_endian_int ), + ( "gas", + big_endian_int ), + ( "shardID", + big_endian_int ), + ( "toShardID", + big_endian_int ), + ( "to", + Binary.fixed_length( 20, + allow_empty = True ) ), + ( "value", + big_endian_int ), + ( "data", + binary ), ) -class SignedHarmonyTxData(HashableRLP): +class SignedHarmonyTxData( HashableRLP ): """ Signed Harmony transaction data Includes `shardID` and `toShardID` @@ -68,22 +77,35 @@ class SignedHarmonyTxData(HashableRLP): ("s", big_endian_int), # Next 32 bytes ) + # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L55 -def encode_transaction( - unsigned_transaction, vrs -): +def encode_transaction( unsigned_transaction, vrs ): """serialize and encode an unsigned transaction with v,r,s.""" - (v, r, s) = vrs # pylint: disable=invalid-name - chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), "v", "r", "s") - if isinstance(unsigned_transaction, (UnsignedHarmonyTxData, SignedHarmonyTxData)): + ( v, r, s ) = vrs # pylint: disable=invalid-name + chain_naive_transaction = dissoc( + unsigned_transaction.as_dict(), + "v", + "r", + "s" + ) + if isinstance( + unsigned_transaction, + ( UnsignedHarmonyTxData, + SignedHarmonyTxData ) + ): serializer = SignedHarmonyTxData else: serializer = SignedEthereumTxData - signed_transaction = serializer(v=v, r=r, s=s, **chain_naive_transaction) - return rlp.encode(signed_transaction) + signed_transaction = serializer( + v = v, + r = r, + s = s, + **chain_naive_transaction + ) + return rlp.encode( signed_transaction ) -def serialize_transaction(filled_transaction): +def serialize_transaction( filled_transaction ): """serialize a signed/unsigned transaction.""" if "v" in filled_transaction: if "shardID" in filled_transaction: @@ -98,35 +120,42 @@ def serialize_transaction(filled_transaction): for field, _ in serializer._meta.fields: assert field in filled_transaction, f"Could not find {field} in transaction" return serializer.from_dict( - {field: filled_transaction[field] for field, _ in serializer._meta.fields} + { + field: filled_transaction[ field ] + for field, + _ in serializer._meta.fields + } ) # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/account.py#L650 -def sanitize_transaction(transaction_dict, private_key): +def sanitize_transaction( transaction_dict, private_key ): """remove the originating address from the dict and convert chainId to int.""" account = Account.from_key( # pylint: disable=no-value-for-parameter private_key ) - sanitized_transaction = transaction_dict.copy() # do not alter the original dictionary + sanitized_transaction = transaction_dict.copy( + ) # do not alter the original dictionary if "from" in sanitized_transaction: - sanitized_transaction["from"] = convert_one_to_hex(transaction_dict["from"]) - if ( - sanitized_transaction["from"] == account.address - ): - sanitized_transaction = dissoc(sanitized_transaction, "from") + sanitized_transaction[ "from" ] = convert_one_to_hex( + transaction_dict[ "from" ] + ) + if sanitized_transaction[ "from" ] == account.address: + sanitized_transaction = dissoc( sanitized_transaction, "from" ) else: raise TypeError( "from field must match key's {account.address}, " "but it was {sanitized_transaction['from']}" ) if "chainId" in sanitized_transaction: - sanitized_transaction["chainId"] = chain_id_to_int(sanitized_transaction["chainId"]) + sanitized_transaction[ "chainId" ] = chain_id_to_int( + sanitized_transaction[ "chainId" ] + ) return account, sanitized_transaction -def sign_transaction(transaction_dict, private_key) -> SignedTransaction: +def sign_transaction( transaction_dict, private_key ) -> SignedTransaction: """Sign a (non-staking) transaction dictionary with the specified private key. @@ -172,33 +201,50 @@ def sign_transaction(transaction_dict, private_key) -> SignedTransaction: https://readthedocs.org/projects/eth-account/downloads/pdf/stable/ """ account, sanitized_transaction = sanitize_transaction(transaction_dict, private_key) - if "to" in sanitized_transaction and sanitized_transaction["to"] is not None: - sanitized_transaction["to"] = convert_one_to_hex(sanitized_transaction["to"]) + if "to" in sanitized_transaction and sanitized_transaction[ "to" + ] is not None: + sanitized_transaction[ "to" ] = convert_one_to_hex( + sanitized_transaction[ "to" ] + ) # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 filled_transaction = pipe( sanitized_transaction, dict, - partial(merge, TRANSACTION_DEFAULTS), + partial( merge, + TRANSACTION_DEFAULTS ), chain_id_to_v, - apply_formatters_to_dict(HARMONY_FORMATTERS), + apply_formatters_to_dict( HARMONY_FORMATTERS ), ) - unsigned_transaction = serialize_transaction(filled_transaction) + unsigned_transaction = serialize_transaction( filled_transaction ) transaction_hash = unsigned_transaction.hash() # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/signing.py#L26 if isinstance( - unsigned_transaction, (UnsignedEthereumTxData, UnsignedHarmonyTxData) + unsigned_transaction, + ( UnsignedEthereumTxData, + UnsignedHarmonyTxData ) ): chain_id = None else: chain_id = unsigned_transaction.v - (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) # pylint: disable=invalid-name - encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s)) - signed_transaction_hash = keccak(encoded_transaction) + ( v, # pylint: disable=invalid-name + r, # pylint: disable=invalid-name + s ) = sign_transaction_hash( # pylint: disable=invalid-name + account._key_obj, + transaction_hash, + chain_id + ) + encoded_transaction = encode_transaction( + unsigned_transaction, + vrs = ( v, + r, + s ) + ) + signed_transaction_hash = keccak( encoded_transaction ) return SignedTransaction( - rawTransaction=HexBytes(encoded_transaction), - hash=HexBytes(signed_transaction_hash), - r=r, - s=s, - v=v, + rawTransaction = HexBytes( encoded_transaction ), + hash = HexBytes( signed_transaction_hash ), + r = r, + s = s, + v = v, ) diff --git a/pyhmy/staking.py b/pyhmy/staking.py index 7c54d1b..3864182 100644 --- a/pyhmy/staking.py +++ b/pyhmy/staking.py @@ -8,11 +8,13 @@ from .exceptions import InvalidRPCReplyError from .constants import DEFAULT_ENDPOINT, DEFAULT_TIMEOUT + ################## # Validator RPCs # ################## def get_all_validator_addresses( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of all created validator addresses on chain. @@ -39,13 +41,17 @@ def get_all_validator_addresses( """ method = "hmyv2_getAllValidatorAddresses" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_validator_information( - validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + validator_addr, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get validator information for validator address. @@ -118,17 +124,21 @@ def get_validator_information( https://api.hmny.io/#659ad999-14ca-4498-8f74-08ed347cab49 """ method = "hmyv2_getValidatorInformation" - params = [validator_addr] + params = [ validator_addr ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_elected_validator_addresses( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of elected validator addresses. @@ -156,12 +166,18 @@ def get_elected_validator_addresses( """ method = "hmyv2_getElectedValidatorAddresses" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> list: +def get_validators( + epoch, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> list: """Get validators list for a particular epoch. Parameters @@ -191,17 +207,22 @@ def get_validators(epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152 """ method = "hmyv2_getValidators" - params = [epoch] + params = [ epoch ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_validator_keys( - epoch, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + epoch, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get validator BLS keys in the committee for a particular epoch. @@ -228,17 +249,23 @@ def get_validator_keys( https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L152 """ method = "hmyv2_getValidatorKeys" - params = [epoch] + params = [ epoch ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_validator_information_by_block_number( - validator_addr, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + validator_addr, + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ): """Get validator information for validator address at a block. @@ -267,17 +294,22 @@ def get_validator_information_by_block_number( https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L319 """ method = "hmyv2_getValidatorInformationByBlockNumber" - params = [validator_addr, block_num] + params = [ validator_addr, block_num ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_all_validator_information( - page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + page = 0, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get validator information for all validators on chain. @@ -304,17 +336,22 @@ def get_all_validator_information( https://api.hmny.io/#df5f1631-7397-48e8-87b4-8dd873235b9c """ method = "hmyv2_getAllValidatorInformation" - params = [page] + params = [ page ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_validator_self_delegation( - address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get the amount self delegated by validator. @@ -341,19 +378,24 @@ def get_validator_self_delegation( https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L352 """ method = "hmyv2_getValidatorSelfDelegation" - params = [address] + params = [ address ] try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_validator_total_delegation( - address, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + address, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get the total amount delegated t ovalidator (including self delegated) @@ -380,19 +422,25 @@ def get_validator_total_delegation( https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L379 """ method = "hmyv2_getValidatorTotalDelegation" - params = [address] + params = [ address ] try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_all_validator_information_by_block_number( - block_num, page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + page = 0, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get validator information at block number for all validators on chain. @@ -422,20 +470,25 @@ def get_all_validator_information_by_block_number( https://api.hmny.io/#a229253f-ca76-4b9d-88f5-9fd96e40d583 """ method = "hmyv2_getAllValidatorInformationByBlockNumber" - params = [page, block_num] + params = [ page, block_num ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception ################### # Delegation RPCs # ################### def get_all_delegation_information( - page=0, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + page = 0, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get delegation information for all delegators on chain. @@ -464,19 +517,22 @@ def get_all_delegation_information( https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L413 """ method = "hmyv2_getAllDelegationInformation" - params = [ - page, - ] + params = [ page, ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_delegations_by_delegator( - delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + delegator_addr, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of delegations by a delegator. @@ -510,17 +566,23 @@ def get_delegations_by_delegator( https://api.hmny.io/#454b032c-6072-4ecb-bf24-38b3d6d2af69 """ method = "hmyv2_getDelegationsByDelegator" - params = [delegator_addr] + params = [ delegator_addr ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_delegations_by_delegator_by_block_number( - delegator_addr, block_num, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + delegator_addr, + block_num, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of delegations by a delegator at a specific block. @@ -549,20 +611,23 @@ def get_delegations_by_delegator_by_block_number( https://api.hmny.io/#8ce13bda-e768-47b9-9dbe-193aba410b0a """ method = "hmyv2_getDelegationsByDelegatorByBlockNumber" - params = [delegator_addr, block_num] + params = [ delegator_addr, block_num ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_delegation_by_delegator_and_validator( delegator_addr, validator_address, - endpoint=DEFAULT_ENDPOINT, - timeout=DEFAULT_TIMEOUT, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT, ) -> dict: """Get list of delegations by a delegator at a specific block. @@ -592,17 +657,22 @@ def get_delegation_by_delegator_and_validator( https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L605 """ method = "hmyv2_getDelegationByDelegatorAndValidator" - params = [delegator_addr, validator_address] + params = [ delegator_addr, validator_address ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_available_redelegation_balance( - delegator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + delegator_addr, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> int: """Get amount of locked undelegated tokens. @@ -629,19 +699,24 @@ def get_available_redelegation_balance( https://github.com/harmony-one/harmony/blob/1a8494c069dc3f708fdf690456713a2411465199/rpc/staking.go#L653 """ method = "hmyv2_getAvailableRedelegationBalance" - params = [delegator_addr] + params = [ delegator_addr ] try: return int( - rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] ) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_delegations_by_validator( - validator_addr, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + validator_addr, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of delegations to a validator. @@ -668,20 +743,24 @@ def get_delegations_by_validator( https://api.hmny.io/#2e02d8db-8fec-41d9-a672-2c9862f63f39 """ method = "hmyv2_getDelegationsByValidator" - params = [validator_addr] + params = [ validator_addr ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception ######################## # Staking Network RPCs # ######################## def get_current_utility_metrics( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get current utility metrics of network. @@ -711,13 +790,16 @@ def get_current_utility_metrics( """ method = "hmyv2_getCurrentUtilityMetrics" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_staking_network_info( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get staking network information. @@ -748,12 +830,17 @@ def get_staking_network_info( """ method = "hmyv2_getStakingNetworkInfo" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_super_committees(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: +def get_super_committees( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> dict: """Get voting committees for current & previous epoch. Parameters @@ -801,12 +888,17 @@ def get_super_committees(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> """ method = "hmyv2_getSuperCommittees" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_total_staking(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int: +def get_total_staking( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> int: """Get total staking by validators, only meant to be called on beaconchain. Parameters @@ -831,13 +923,18 @@ def get_total_staking(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> int """ method = "hmyv2_getTotalStaking" try: - return int(rpc_request(method, endpoint=endpoint, timeout=timeout)["result"]) - except (KeyError, TypeError) as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + return int( + rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] + ) + except ( KeyError, TypeError ) as exception: + raise InvalidRPCReplyError( method, endpoint ) from exception def get_raw_median_stake_snapshot( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get median stake & additional committee data of the current epoch. @@ -876,6 +973,8 @@ def get_raw_median_stake_snapshot( """ method = "hmyv2_getMedianRawStakeSnapshot" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index bffbcf8..baf98ed 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -7,12 +7,7 @@ import math from decimal import Decimal from functools import partial -from toolz import ( - pipe, - dissoc, - merge, - identity, -) +from toolz import ( pipe, dissoc, merge, identity, ) from hexbytes import HexBytes @@ -32,7 +27,7 @@ from eth_utils.curried import ( to_int, apply_formatters_to_sequence, apply_formatter_to_array, -) + ) from .constants import PRECISION, MAX_DECIMAL @@ -45,15 +40,13 @@ from .staking_structures import ( EditValidator, DelegateOrUndelegate, CollectRewards, -) + ) from .util import convert_one_to_hex # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L335 -def _convert_staking_percentage_to_number( - value, -): +def _convert_staking_percentage_to_number( value, ): """Convert from staking percentage to integer For example, 0.1 becomes 1000000000000000000. Since Python floats are problematic with precision, this function is used as a workaround. @@ -72,47 +65,45 @@ def _convert_staking_percentage_to_number( AssertionError, if data types are not as expected ValueError, if the input type is not supported """ - assert isinstance(value, (str, Decimal)), "Only strings or decimals are supported" - if isinstance(value, Decimal): - value = str(value) + assert isinstance( value, ( str, Decimal ) ), "Only strings or decimals are supported" + if isinstance( value, Decimal ): + value = str( value ) value1 = value - if value[0] == "-": - raise ValueError("Negative numbers are not accepted") - if value[0] == "+": - value1 = value[1:] - if len(value1) == 0: - raise ValueError("StakingDecimal string is empty") - spaced = value1.split(" ") - if len(spaced) > 1: - raise ValueError("Bad decimal string") - splitted = value1.split(".") - combined_str = splitted[0] - if len(splitted) == 2: - length = len(splitted[1]) - if length == 0 or len(combined_str) == 0: - raise ValueError("Bad StakingDecimal length") - if splitted[1][0] == "-": - raise ValueError("Bad StakingDecimal string") - combined_str += splitted[1] - elif len(splitted) > 2: - raise ValueError("Too many periods to be a StakingDecimal string") + if value[ 0 ] == "-": + raise ValueError( "Negative numbers are not accepted" ) + if value[ 0 ] == "+": + value1 = value[ 1 : ] + if len( value1 ) == 0: + raise ValueError( "StakingDecimal string is empty" ) + spaced = value1.split( " " ) + if len( spaced ) > 1: + raise ValueError( "Bad decimal string" ) + splitted = value1.split( "." ) + combined_str = splitted[ 0 ] + if len( splitted ) == 2: + length = len( splitted[ 1 ] ) + if length == 0 or len( combined_str ) == 0: + raise ValueError( "Bad StakingDecimal length" ) + if splitted[ 1 ][ 0 ] == "-": + raise ValueError( "Bad StakingDecimal string" ) + combined_str += splitted[ 1 ] + elif len( splitted ) > 2: + raise ValueError( "Too many periods to be a StakingDecimal string" ) if length > PRECISION: - raise ValueError( - "Too much precision, must be less than {PRECISION}" - ) + raise ValueError( "Too much precision, must be less than {PRECISION}" ) zeroes_to_add = PRECISION - length combined_str += ( "0" * zeroes_to_add ) # This will not have any periods, so it is effectively a large integer - val = int(combined_str) + val = int( combined_str ) assert val <= MAX_DECIMAL, "Staking percentage is too large" return val -def _get_account_and_transaction(transaction_dict, private_key): +def _get_account_and_transaction( transaction_dict, private_key ): """Create account from private key and sanitize the transaction Sanitization involves removal of 'from' key And conversion of chainId key - from str to int (if present) + from str to int ( if present ) Parameters ---------- @@ -133,21 +124,25 @@ def _get_account_and_transaction(transaction_dict, private_key): """ account, sanitized_transaction = sanitize_transaction( transaction_dict, private_key - ) # remove from, convert chain id (if present) to integer - sanitized_transaction["directive"] = sanitized_transaction[ - "directive" - ].value # convert to value, like in TypeScript + ) # remove from, convert chain id ( if present ) to integer + sanitized_transaction[ "directive" ] = sanitized_transaction[ + "directive" ].value # convert to value, like in TypeScript return account, sanitized_transaction + # pylint: disable=too-many-locals,protected-access,invalid-name -def _sign_transaction_generic(account, sanitized_transaction, parent_serializer): +def _sign_transaction_generic( + account, + sanitized_transaction, + parent_serializer + ): """Sign a generic staking transaction, given the serializer base class and account. Paramters --------- account: :obj:`eth_account.Account`, the account to use for signing - sanitized_transaction: :obj:`dict`, The sanitized transaction (chainId checks and no from key) + sanitized_transaction: :obj:`dict`, The sanitized transaction ( chainId checks and no from key ) parent_serializer: :obj: The serializer class from staking_structures Returns @@ -161,40 +156,52 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) rlp.exceptions.ObjectSerializationError, if data types are not as expected """ # obtain the serializers - if sanitized_transaction.get("chainId", 0) == 0: + if sanitized_transaction.get( "chainId", 0 ) == 0: unsigned_serializer, signed_serializer = ( - parent_serializer.Unsigned(), - parent_serializer.Signed(), + parent_serializer.Unsigned( ), + parent_serializer.Signed( ), ) # unsigned, signed else: unsigned_serializer, signed_serializer = ( - parent_serializer.SignedChainId(), - parent_serializer.SignedChainId(), + parent_serializer.SignedChainId( ), + parent_serializer.SignedChainId( ), ) # since chain_id_to_v adds v/r/s, unsigned is not used here # fill the transaction # https://github.com/ethereum/eth-account/blob/00e7b10005c5fa7090086fcef37a76296c524e17/eth_account/_utils/transactions.py#L39 filled_transaction = pipe( sanitized_transaction, dict, - partial(merge, {"chainId": None}), + partial( merge, { "chainId": None } ), chain_id_to_v, # will move chain id to v and add v/r/s - apply_formatters_to_dict(FORMATTERS), + apply_formatters_to_dict( FORMATTERS ), ) # get the unsigned transaction for field, _ in unsigned_serializer._meta.fields: assert field in filled_transaction, f"Could not find {field} in transaction" unsigned_transaction = unsigned_serializer.from_dict( - {f: filled_transaction[f] for f, _ in unsigned_serializer._meta.fields} + { + f: filled_transaction[ f ] + for f, + _ in unsigned_serializer._meta.fields + } ) # drop extras silently # sign the unsigned transaction - if "v" in unsigned_transaction.as_dict(): + if "v" in unsigned_transaction.as_dict( ): chain_id = unsigned_transaction.v else: chain_id = None - transaction_hash = unsigned_transaction.hash() - (v, r, s) = sign_transaction_hash(account._key_obj, transaction_hash, chain_id) + transaction_hash = unsigned_transaction.hash( ) + ( v, + r, + s + ) = sign_transaction_hash( account._key_obj, + transaction_hash, + chain_id ) chain_naive_transaction = dissoc( - unsigned_transaction.as_dict(), "v", "r", "s" + unsigned_transaction.as_dict( ), + "v", + "r", + "s" ) # remove extra v/r/s added by chain_id_to_v # serialize it # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L207 @@ -206,30 +213,33 @@ def _sign_transaction_generic(account, sanitized_transaction, parent_serializer) r=r, s=s, # in the below statement, remove everything not expected by signed_serializer **{ - f: chain_naive_transaction[f] + f: chain_naive_transaction[ f ] for f, _ in signed_serializer._meta.fields if f not in "vrs" }, ) # encode it - encoded_transaction = rlp.encode(signed_transaction) + encoded_transaction = rlp.encode( signed_transaction ) # hash it - signed_transaction_hash = keccak(encoded_transaction) + signed_transaction_hash = keccak( encoded_transaction ) # return is return SignedTransaction( - rawTransaction=HexBytes(encoded_transaction), - hash=HexBytes(signed_transaction_hash), - r=r, - s=s, - v=v, + rawTransaction = HexBytes( encoded_transaction ), + hash = HexBytes( signed_transaction_hash ), + r = r, + s = s, + v = v, ) -def _sign_delegate_or_undelegate(transaction_dict, private_key): +def _sign_delegate_or_undelegate( transaction_dict, private_key ): """Sign a delegate or undelegate transaction See sign_staking_transaction for details.""" # preliminary steps - if transaction_dict["directive"] not in [Directive.Delegate, Directive.Undelegate]: + if transaction_dict[ "directive" ] not in [ + Directive.Delegate, + Directive.Undelegate + ]: raise TypeError( "Only Delegate or Undelegate are supported by _sign_delegate_or_undelegate" ) @@ -238,43 +248,61 @@ def _sign_delegate_or_undelegate(transaction_dict, private_key): transaction_dict, private_key ) # encode the stakeMsg - sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( - [hexstr_if_str(to_bytes), hexstr_if_str(to_bytes), hexstr_if_str(to_int)], + sanitized_transaction[ "stakeMsg" ] = apply_formatters_to_sequence( + [ + hexstr_if_str( to_bytes ), + hexstr_if_str( to_bytes ), + hexstr_if_str( to_int ) + ], [ - convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")), - convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), - sanitized_transaction.pop("amount"), + convert_one_to_hex( + sanitized_transaction.pop( "delegatorAddress" ) + ), + convert_one_to_hex( + sanitized_transaction.pop( "validatorAddress" ) + ), + sanitized_transaction.pop( "amount" ), ], ) return _sign_transaction_generic( - account, sanitized_transaction, DelegateOrUndelegate + account, + sanitized_transaction, + DelegateOrUndelegate ) -def _sign_collect_rewards(transaction_dict, private_key): +def _sign_collect_rewards( transaction_dict, private_key ): """Sign a collect rewards transaction See sign_staking_transaction for details.""" # preliminary steps - if transaction_dict["directive"] != Directive.CollectRewards: - raise TypeError("Only CollectRewards is supported by _sign_collect_rewards") + if transaction_dict[ "directive" ] != Directive.CollectRewards: + raise TypeError( + "Only CollectRewards is supported by _sign_collect_rewards" + ) # first common step account, sanitized_transaction = _get_account_and_transaction( transaction_dict, private_key ) # encode the stakeMsg - sanitized_transaction["stakeMsg"] = [ - hexstr_if_str(to_bytes)( - convert_one_to_hex(sanitized_transaction.pop("delegatorAddress")) + sanitized_transaction[ "stakeMsg" ] = [ + hexstr_if_str( to_bytes )( + convert_one_to_hex( + sanitized_transaction.pop( "delegatorAddress" ) + ) ) ] - return _sign_transaction_generic(account, sanitized_transaction, CollectRewards) + return _sign_transaction_generic( + account, + sanitized_transaction, + CollectRewards + ) -def _sign_create_validator(transaction_dict, private_key): +def _sign_create_validator( transaction_dict, private_key ): """Sign a create validator transaction See sign_staking_transaction for details.""" # preliminary steps - if transaction_dict["directive"] != Directive.CreateValidator: + if transaction_dict[ "directive" ] != Directive.CreateValidator: raise TypeError( "Only CreateValidator is supported by _sign_create_or_edit_validator" ) @@ -284,70 +312,75 @@ def _sign_create_validator(transaction_dict, private_key): ) # encode the stakeMsg description = [ - sanitized_transaction.pop("name"), - sanitized_transaction.pop("identity"), - sanitized_transaction.pop("website"), - sanitized_transaction.pop("security-contact"), - sanitized_transaction.pop("details"), + sanitized_transaction.pop( "name" ), + sanitized_transaction.pop( "identity" ), + sanitized_transaction.pop( "website" ), + sanitized_transaction.pop( "security-contact" ), + sanitized_transaction.pop( "details" ), ] commission = apply_formatter_to_array( - hexstr_if_str(to_int), # formatter + hexstr_if_str( to_int ), # formatter [ - _convert_staking_percentage_to_number(sanitized_transaction.pop("rate")), + _convert_staking_percentage_to_number( sanitized_transaction.pop( "rate" ) ), _convert_staking_percentage_to_number( - sanitized_transaction.pop("max-rate") + sanitized_transaction.pop( "max-rate" ) ), _convert_staking_percentage_to_number( - sanitized_transaction.pop("max-change-rate") + sanitized_transaction.pop( "max-change-rate" ) ), ], ) - commission = [[element] for element in commission] + commission = [ [ element ] for element in commission ] bls_keys = apply_formatter_to_array( - hexstr_if_str(to_bytes), # formatter - sanitized_transaction.pop("bls-public-keys"), + hexstr_if_str( to_bytes ), # formatter + sanitized_transaction.pop( "bls-public-keys" ), ) bls_key_sigs = apply_formatter_to_array( - hexstr_if_str(to_bytes), sanitized_transaction.pop("bls-key-sigs") # formatter + hexstr_if_str( to_bytes ), + sanitized_transaction.pop( "bls-key-sigs" ) # formatter ) - sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + sanitized_transaction[ "stakeMsg" ] = apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), # address + hexstr_if_str( to_bytes ), # address identity, # description identity, # commission rates hexstr_if_str( to_int - ), # min self delegation (in ONE), decimals are silently dropped + ), # min self delegation ( in ONE ), decimals are silently dropped hexstr_if_str( to_int - ), # max total delegation (in ONE), decimals are silently dropped + ), # max total delegation ( in ONE ), decimals are silently dropped identity, # bls public keys identity, # bls key sigs hexstr_if_str( to_int - ), # amount (the Hexlify in the SDK drops the decimals, which is what we will do too) + ), # amount ( the Hexlify in the SDK drops the decimals, which is what we will do too ) ], [ - convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), + convert_one_to_hex( sanitized_transaction.pop( "validatorAddress" ) ), description, commission, math.floor( - sanitized_transaction.pop("min-self-delegation") + sanitized_transaction.pop( "min-self-delegation" ) ), # Decimal floors it correctly - math.floor(sanitized_transaction.pop("max-total-delegation")), + math.floor( sanitized_transaction.pop( "max-total-delegation" ) ), bls_keys, bls_key_sigs, - math.floor(sanitized_transaction.pop("amount")), + math.floor( sanitized_transaction.pop( "amount" ) ), ], ) - return _sign_transaction_generic(account, sanitized_transaction, CreateValidator) + return _sign_transaction_generic( + account, + sanitized_transaction, + CreateValidator + ) -def _sign_edit_validator(transaction_dict, private_key): +def _sign_edit_validator( transaction_dict, private_key ): """Sign an edit validator transaction See sign_staking_transaction for details.""" # preliminary steps - if transaction_dict["directive"] != Directive.EditValidator: + if transaction_dict[ "directive" ] != Directive.EditValidator: raise TypeError( "Only EditValidator is supported by _sign_create_or_edit_validator" ) @@ -357,44 +390,48 @@ def _sign_edit_validator(transaction_dict, private_key): ) # encode the stakeMsg description = [ - sanitized_transaction.pop("name"), - sanitized_transaction.pop("identity"), - sanitized_transaction.pop("website"), - sanitized_transaction.pop("security-contact"), - sanitized_transaction.pop("details"), + sanitized_transaction.pop( "name" ), + sanitized_transaction.pop( "identity" ), + sanitized_transaction.pop( "website" ), + sanitized_transaction.pop( "security-contact" ), + sanitized_transaction.pop( "details" ), ] - sanitized_transaction["stakeMsg"] = apply_formatters_to_sequence( + sanitized_transaction[ "stakeMsg" ] = apply_formatters_to_sequence( [ - hexstr_if_str(to_bytes), # address + hexstr_if_str( to_bytes ), # address identity, # description - identity, # new rate (it's in a list so can't do hexstr_if_str) + identity, # new rate ( it's in a list so can't do hexstr_if_str ) hexstr_if_str( to_int - ), # min self delegation (in ONE), decimals are silently dropped + ), # min self delegation ( in ONE ), decimals are silently dropped hexstr_if_str( to_int - ), # max total delegation (in ONE), decimals are silently dropped - hexstr_if_str(to_bytes), # key to remove - hexstr_if_str(to_bytes), # key to add - hexstr_if_str(to_bytes), # key to add sig + ), # max total delegation ( in ONE ), decimals are silently dropped + hexstr_if_str( to_bytes ), # key to remove + hexstr_if_str( to_bytes ), # key to add + hexstr_if_str( to_bytes ), # key to add sig ], [ - convert_one_to_hex(sanitized_transaction.pop("validatorAddress")), + convert_one_to_hex( sanitized_transaction.pop( "validatorAddress" ) ), description, - [_convert_staking_percentage_to_number(sanitized_transaction.pop("rate"))], + [ _convert_staking_percentage_to_number( sanitized_transaction.pop( "rate" ) ) ], math.floor( - sanitized_transaction.pop("min-self-delegation") + sanitized_transaction.pop( "min-self-delegation" ) ), # Decimal floors it correctly - math.floor(sanitized_transaction.pop("max-total-delegation")), - sanitized_transaction.pop("bls-key-to-remove"), - sanitized_transaction.pop("bls-key-to-add"), - sanitized_transaction.pop("bls-key-to-add-sig"), + math.floor( sanitized_transaction.pop( "max-total-delegation" ) ), + sanitized_transaction.pop( "bls-key-to-remove" ), + sanitized_transaction.pop( "bls-key-to-add" ), + sanitized_transaction.pop( "bls-key-to-add-sig" ), ], ) - return _sign_transaction_generic(account, sanitized_transaction, EditValidator) + return _sign_transaction_generic( + account, + sanitized_transaction, + EditValidator + ) -def sign_staking_transaction(transaction_dict, private_key): +def sign_staking_transaction( transaction_dict, private_key ): """Sign a supplied transaction_dict with the private_key. Parameters @@ -412,7 +449,7 @@ def sign_staking_transaction(transaction_dict, private_key): Delegate/Undelegate: delegatorAddress: :obj:`str`, Address of the delegator validatorAddress: :obj:`str`, Address of the validator - amount: :obj:`int`, Amount to (un)delegate in ATTO + amount: :obj:`int`, Amount to ( un )delegate in ATTO CreateValidator: validatorAddress: :obj:`str`, Address of the validator name: ;obj:`str`, Name of the validator @@ -468,16 +505,16 @@ def sign_staking_transaction(transaction_dict, private_key): assert "chainId" in transaction_dict, "chainId missing" assert "directive" in transaction_dict, "Staking transaction type not specified" assert isinstance( - transaction_dict["directive"], Directive + transaction_dict[ "directive" ], Directive ), "Unknown staking transaction type" - if transaction_dict["directive"] == Directive.CollectRewards: - return _sign_collect_rewards(transaction_dict, private_key) - if transaction_dict["directive"] == Directive.Delegate: - return _sign_delegate_or_undelegate(transaction_dict, private_key) - if transaction_dict["directive"] == Directive.Undelegate: - return _sign_delegate_or_undelegate(transaction_dict, private_key) - if transaction_dict["directive"] == Directive.CreateValidator: - return _sign_create_validator(transaction_dict, private_key) - if transaction_dict["directive"] == Directive.EditValidator: - return _sign_edit_validator(transaction_dict, private_key) - raise ValueError('Unknown staking transaction type') + if transaction_dict[ "directive" ] == Directive.CollectRewards: + return _sign_collect_rewards( transaction_dict, private_key ) + if transaction_dict[ "directive" ] == Directive.Delegate: + return _sign_delegate_or_undelegate( transaction_dict, private_key ) + if transaction_dict[ "directive" ] == Directive.Undelegate: + return _sign_delegate_or_undelegate( transaction_dict, private_key ) + if transaction_dict[ "directive" ] == Directive.CreateValidator: + return _sign_create_validator( transaction_dict, private_key ) + if transaction_dict[ "directive" ] == Directive.EditValidator: + return _sign_edit_validator( transaction_dict, private_key ) + raise ValueError( 'Unknown staking transaction type' ) diff --git a/pyhmy/staking_structures.py b/pyhmy/staking_structures.py index ced6a0b..5cde3ed 100644 --- a/pyhmy/staking_structures.py +++ b/pyhmy/staking_structures.py @@ -10,16 +10,12 @@ from rlp.sedes import big_endian_int, Binary, CountableList, List, Text from eth_rlp import HashableRLP -from eth_utils.curried import ( - to_int, - hexstr_if_str, -) +from eth_utils.curried import ( to_int, hexstr_if_str, ) + # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 -class Directive( - Enum -): - def _generate_next_value_(name, start, count, last_values): # pylint: disable=no-self-argument +class Directive( Enum ): + def _generate_next_value_( name, start, count, last_values ): # pylint: disable=no-self-argument return count CreateValidator = auto() @@ -39,24 +35,36 @@ FORMATTERS = { "chainId": hexstr_if_str(to_int), } + class CollectRewards: @staticmethod def UnsignedChainId(): - class UnsignedChainId(HashableRLP): + class UnsignedChainId( HashableRLP ): fields = ( - ("directive", big_endian_int), - ("stakeMsg", CountableList(Binary.fixed_length(20, allow_empty=True))), - ("nonce", big_endian_int), - ("gasPrice", big_endian_int), - ("gasLimit", big_endian_int), - ("chainId", big_endian_int), + ( "directive", + big_endian_int ), + ( + "stakeMsg", + CountableList( + Binary.fixed_length( 20, + allow_empty = True ) + ) + ), + ( "nonce", + big_endian_int ), + ( "gasPrice", + big_endian_int ), + ( "gasLimit", + big_endian_int ), + ( "chainId", + big_endian_int ), ) return UnsignedChainId @staticmethod def SignedChainId(): - class SignedChainId(HashableRLP): + class SignedChainId( HashableRLP ): fields = CollectRewards.UnsignedChainId()._meta.fields[ :-1 ] + ( # drop chainId @@ -69,14 +77,15 @@ class CollectRewards: @staticmethod def Unsigned(): - class Unsigned(HashableRLP): - fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId + class Unsigned( HashableRLP ): + fields = CollectRewards.UnsignedChainId( + )._meta.fields[ :-1 ] # drop chainId return Unsigned @staticmethod def Signed(): - class Signed(HashableRLP): + class Signed( HashableRLP ): fields = CollectRewards.Unsigned()._meta.fields[ :-3 ] + ( # drop last 3 for raw.pop() @@ -91,31 +100,38 @@ class CollectRewards: class DelegateOrUndelegate: @staticmethod def UnsignedChainId(): - class UnsignedChainId(HashableRLP): + class UnsignedChainId( HashableRLP ): fields = ( - ("directive", big_endian_int), + ( "directive", + big_endian_int ), ( "stakeMsg", List( [ - Binary.fixed_length(20, allow_empty=True), - Binary.fixed_length(20, allow_empty=True), + Binary.fixed_length( 20, + allow_empty = True ), + Binary.fixed_length( 20, + allow_empty = True ), big_endian_int, ], True, ), ), - ("nonce", big_endian_int), - ("gasPrice", big_endian_int), - ("gasLimit", big_endian_int), - ("chainId", big_endian_int), + ( "nonce", + big_endian_int ), + ( "gasPrice", + big_endian_int ), + ( "gasLimit", + big_endian_int ), + ( "chainId", + big_endian_int ), ) return UnsignedChainId @staticmethod def SignedChainId(): - class SignedChainId(HashableRLP): + class SignedChainId( HashableRLP ): fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ :-1 ] + ( # drop chainId @@ -128,16 +144,15 @@ class DelegateOrUndelegate: @staticmethod def Unsigned(): - class Unsigned(HashableRLP): - fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ - :-1 - ] # drop chainId + class Unsigned( HashableRLP ): + fields = DelegateOrUndelegate.UnsignedChainId( + )._meta.fields[ :-1 ] # drop chainId return Unsigned @staticmethod def Signed(): - class Signed(HashableRLP): + class Signed( HashableRLP ): fields = DelegateOrUndelegate.Unsigned()._meta.fields[ :-3 ] + ( # drop last 3 for raw.pop() @@ -152,7 +167,7 @@ class DelegateOrUndelegate: class CreateValidator: @staticmethod def UnsignedChainId(): - class UnsignedChainId(HashableRLP): + class UnsignedChainId( HashableRLP ): fields = ( ("directive", big_endian_int), ( @@ -196,7 +211,7 @@ class CreateValidator: @staticmethod def SignedChainId(): - class SignedChainId(HashableRLP): + class SignedChainId( HashableRLP ): fields = CreateValidator.UnsignedChainId()._meta.fields[ :-1 ] + ( # drop chainId @@ -209,14 +224,15 @@ class CreateValidator: @staticmethod def Unsigned(): - class Unsigned(HashableRLP): - fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId + class Unsigned( HashableRLP ): + fields = CreateValidator.UnsignedChainId( + )._meta.fields[ :-1 ] # drop chainId return Unsigned @staticmethod def Signed(): - class Signed(HashableRLP): + class Signed( HashableRLP ): fields = CreateValidator.Unsigned()._meta.fields[ :-3 ] + ( # drop last 3 for raw.pop() @@ -231,7 +247,7 @@ class CreateValidator: class EditValidator: @staticmethod def UnsignedChainId(): - class UnsignedChainId(HashableRLP): + class UnsignedChainId( HashableRLP ): fields = ( ("directive", big_endian_int), ( @@ -276,7 +292,7 @@ class EditValidator: @staticmethod def SignedChainId(): - class SignedChainId(HashableRLP): + class SignedChainId( HashableRLP ): fields = EditValidator.UnsignedChainId()._meta.fields[ :-1 ] + ( # drop chainId @@ -289,14 +305,15 @@ class EditValidator: @staticmethod def Unsigned(): - class Unsigned(HashableRLP): - fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId + class Unsigned( HashableRLP ): + fields = EditValidator.UnsignedChainId( + )._meta.fields[ :-1 ] # drop chainId return Unsigned @staticmethod def Signed(): - class Signed(HashableRLP): + class Signed( HashableRLP ): fields = EditValidator.Unsigned()._meta.fields[ :-3 ] + ( # drop last 3 for raw.pop() diff --git a/pyhmy/transaction.py b/pyhmy/transaction.py index 97eb407..79ef662 100644 --- a/pyhmy/transaction.py +++ b/pyhmy/transaction.py @@ -13,7 +13,8 @@ from .exceptions import TxConfirmationTimedoutError, InvalidRPCReplyError # Transaction Pool RPCs # ######################### def get_pending_transactions( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of pending transactions. @@ -39,13 +40,16 @@ def get_pending_transactions( """ method = "hmyv2_pendingTransactions" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_transaction_error_sink( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get current transactions error sink. @@ -74,13 +78,16 @@ def get_transaction_error_sink( """ method = "hmyv2_getCurrentTransactionErrorSink" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_pending_staking_transactions( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of pending staking transactions. @@ -106,13 +113,16 @@ def get_pending_staking_transactions( """ method = "hmyv2_pendingStakingTransactions" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_staking_transaction_error_sink( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get current staking transactions error sink. @@ -142,12 +152,17 @@ def get_staking_transaction_error_sink( """ method = "hmyv2_getCurrentStakingErrorSink" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception -def get_pool_stats(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: +def get_pool_stats( + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT +) -> dict: """Get stats of the pool, that is, number of pending and queued (non- executable) transactions. @@ -175,16 +190,20 @@ def get_pool_stats(endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT) -> dict: """ method = "hmyv2_getPoolStats" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception #################### # Transaction RPCs # #################### def get_transaction_by_hash( - tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + tx_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get transaction by hash. @@ -231,17 +250,23 @@ def get_transaction_by_hash( https://api.hmny.io/#117e84f6-a0ec-444e-abe0-455701310389 """ method = "hmyv2_getTransactionByHash" - params = [tx_hash] + params = [ tx_hash ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_transaction_by_block_hash_and_index( - block_hash, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_hash, + tx_index, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get transaction based on index in list of transactions in a block by block hash. @@ -271,17 +296,23 @@ def get_transaction_by_block_hash_and_index( https://api.hmny.io/#7c7e8d90-4984-4ebe-bb7e-d7adec167503 """ method = "hmyv2_getTransactionByBlockHashAndIndex" - params = [block_hash, tx_index] + params = [ block_hash, tx_index ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_transaction_by_block_number_and_index( - block_num, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + tx_index, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get transaction based on index in list of transactions in a block by block number. @@ -311,17 +342,22 @@ def get_transaction_by_block_number_and_index( https://api.hmny.io/#bcde8b1c-6ab9-4950-9835-3c7564e49c3e """ method = "hmyv2_getTransactionByBlockNumberAndIndex" - params = [block_num, tx_index] + params = [ block_num, tx_index ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_transaction_receipt( - tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + tx_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get transaction receipt corresponding to tx_hash. @@ -364,17 +400,22 @@ def get_transaction_receipt( https://api.hmny.io/#0c2799f8-bcdc-41a4-b362-c3a6a763bb5e """ method = "hmyv2_getTransactionReceipt" - params = [tx_hash] + params = [ tx_hash ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def send_raw_transaction( - signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + signed_tx, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> str: """Send signed transaction. @@ -403,18 +444,23 @@ def send_raw_transaction( ------------- https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0 """ - params = [signed_tx] + params = [ signed_tx ] method = "hmyv2_sendRawTransaction" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def send_and_confirm_raw_transaction( - signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + signed_tx, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Send signed transaction and wait for it to be confirmed. @@ -445,24 +491,27 @@ def send_and_confirm_raw_transaction( ------------- https://api.hmny.io/#f40d124a-b897-4b7c-baf3-e0dedf8f40a0 """ - tx_hash = send_raw_transaction(signed_tx, endpoint=endpoint) + tx_hash = send_raw_transaction( signed_tx, endpoint = endpoint ) start_time = time.time() - while (time.time() - start_time) <= timeout: - tx_response = get_transaction_by_hash(tx_hash, endpoint=endpoint) + while ( time.time() - start_time ) <= timeout: + tx_response = get_transaction_by_hash( tx_hash, endpoint = endpoint ) if tx_response is not None: - block_hash = tx_response.get("blockHash", "0x00") - unique_chars = "".join(set(list(block_hash[2:]))) + block_hash = tx_response.get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) if unique_chars != "0": return tx_response - time.sleep(random.uniform(0.2, 0.5)) - raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") + time.sleep( random.uniform( 0.2, 0.5 ) ) + raise TxConfirmationTimedoutError( + "Could not confirm transaction on-chain." + ) ############################### # CrossShard Transaction RPCs # ############################### def get_pending_cx_receipts( - endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Get list of pending cross shard transactions. @@ -511,13 +560,17 @@ def get_pending_cx_receipts( """ method = "hmyv2_getPendingCXReceipts" try: - return rpc_request(method, endpoint=endpoint, timeout=timeout)["result"] + return rpc_request( method, + endpoint = endpoint, + timeout = timeout )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_cx_receipt_by_hash( - cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + cx_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get cross shard receipt by hash on the receiving shard end point. @@ -552,18 +605,23 @@ def get_cx_receipt_by_hash( ------------- https://api.hmny.io/#3d6ad045-800d-4021-aeb5-30a0fbf724fe """ - params = [cx_hash] + params = [ cx_hash ] method = "hmyv2_getCXReceiptByHash" try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def resend_cx_receipt( - cx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + cx_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> bool: """Resend the cross shard receipt to the receiving shard to re-process if the transaction did not pay out. @@ -592,20 +650,25 @@ def resend_cx_receipt( https://api.hmny.io/#c658b56b-d20b-480d-b71a-b0bc505d2164 """ method = "hmyv2_resendCx" - params = [cx_hash] + params = [ cx_hash ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception ############################ # Staking Transaction RPCs # ############################ def get_staking_transaction_by_hash( - tx_hash, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + tx_hash, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get staking transaction by hash. @@ -646,17 +709,23 @@ def get_staking_transaction_by_hash( https://api.hmny.io/#296cb4d0-bce2-48e3-bab9-64c3734edd27 """ method = "hmyv2_getStakingTransactionByHash" - params = [tx_hash] + params = [ tx_hash ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_staking_transaction_by_block_hash_and_index( - block_hash, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_hash, + tx_index, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get staking transaction by block hash and transaction index. @@ -685,17 +754,23 @@ def get_staking_transaction_by_block_hash_and_index( https://api.hmny.io/#ba96cf61-61fe-464a-aa06-2803bb4b358f """ method = "hmyv2_getStakingTransactionByBlockHashAndIndex" - params = [block_hash, tx_index] + params = [ block_hash, tx_index ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def get_staking_transaction_by_block_number_and_index( - block_num, tx_index, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + block_num, + tx_index, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> dict: """Get staking transaction by block number and transaction index. @@ -724,17 +799,22 @@ def get_staking_transaction_by_block_number_and_index( https://api.hmny.io/#fb41d717-1645-4d3e-8071-6ce8e1b65dd3 """ method = "hmyv2_getStakingTransactionByBlockNumberAndIndex" - params = [block_num, tx_index] + params = [ block_num, tx_index ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def send_raw_staking_transaction( - raw_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + raw_tx, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> str: """Send signed staking transaction. @@ -764,17 +844,22 @@ def send_raw_staking_transaction( https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401 """ method = "hmyv2_sendRawStakingTransaction" - params = [raw_tx] + params = [ raw_tx ] try: - return rpc_request(method, params=params, endpoint=endpoint, timeout=timeout)[ - "result" - ] + return rpc_request( + method, + params = params, + endpoint = endpoint, + timeout = timeout + )[ "result" ] except KeyError as exception: - raise InvalidRPCReplyError(method, endpoint) from exception + raise InvalidRPCReplyError( method, endpoint ) from exception def send_and_confirm_raw_staking_transaction( - signed_tx, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + signed_tx, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> list: """Send signed staking transaction and wait for it to be confirmed. @@ -805,14 +890,19 @@ def send_and_confirm_raw_staking_transaction( ------------- https://api.hmny.io/#e8c17fe9-e730-4c38-95b3-6f1a5b1b9401 """ - tx_hash = send_raw_staking_transaction(signed_tx, endpoint=endpoint) + tx_hash = send_raw_staking_transaction( signed_tx, endpoint = endpoint ) start_time = time.time() - while (time.time() - start_time) <= timeout: - tx_response = get_staking_transaction_by_hash(tx_hash, endpoint=endpoint) + while ( time.time() - start_time ) <= timeout: + tx_response = get_staking_transaction_by_hash( + tx_hash, + endpoint = endpoint + ) if tx_response is not None: - block_hash = tx_response.get("blockHash", "0x00") - unique_chars = "".join(set(list(block_hash[2:]))) + block_hash = tx_response.get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) if unique_chars != "0": return tx_response - time.sleep(random.uniform(0.2, 0.5)) - raise TxConfirmationTimedoutError("Could not confirm transaction on-chain.") + time.sleep( random.uniform( 0.2, 0.5 ) ) + raise TxConfirmationTimedoutError( + "Could not confirm transaction on-chain." + ) diff --git a/pyhmy/util.py b/pyhmy/util.py index e055b95..77c3ffb 100644 --- a/pyhmy/util.py +++ b/pyhmy/util.py @@ -13,17 +13,14 @@ from eth_utils import to_checksum_address from .blockchain import get_latest_header -from .rpc.exceptions import ( - RPCError, - RequestsError, - RequestsTimeoutError, -) +from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError, ) from .account import is_valid_address from .bech32.bech32 import bech32_decode, bech32_encode, convertbits -class Typgpy(str): + +class Typgpy( str ): """Typography constants for pretty printing. Note that an ENDC is needed to mark the end of a 'highlighted' text @@ -40,7 +37,7 @@ class Typgpy(str): UNDERLINE = "\033[4m" -def chain_id_to_int(chain_id): +def chain_id_to_int( chain_id ): """ If chain_id is a string, converts it to int. If chain_id is an int, returns the int. @@ -48,72 +45,73 @@ def chain_id_to_int(chain_id): Else raises TypeError """ chain_ids = dict( - Default=0, - EthMainnet=1, - Morden=2, - Ropsten=3, - Rinkeby=4, - RootstockMainnet=30, - RootstockTestnet=31, - Kovan=42, - EtcMainnet=61, - EtcTestnet=62, - Geth=1337, - Ganache=0, - HmyMainnet=1, - HmyTestnet=2, - HmyLocal=2, - HmyPangaea=3, + Default = 0, + EthMainnet = 1, + Morden = 2, + Ropsten = 3, + Rinkeby = 4, + RootstockMainnet = 30, + RootstockTestnet = 31, + Kovan = 42, + EtcMainnet = 61, + EtcTestnet = 62, + Geth = 1337, + Ganache = 0, + HmyMainnet = 1, + HmyTestnet = 2, + HmyLocal = 2, + HmyPangaea = 3, ) # do not validate integer chainids, only known strings - if isinstance(chain_id, str): + if isinstance( chain_id, str ): assert ( chain_id in chain_ids ), f"Chain {chain_id} unknown, specify an integer chainId" - return chain_ids.get(chain_id) - if isinstance(chain_id, int): + return chain_ids.get( chain_id ) + if isinstance( chain_id, int ): return chain_id - raise TypeError("chainId must be str or int") + raise TypeError( "chainId must be str or int" ) def get_gopath(): """ :returns The go-path, assuming that go is installed. """ - return subprocess.check_output(["go", "env", "GOPATH"]).decode().strip() + return subprocess.check_output( [ "go", "env", "GOPATH" ] ).decode().strip() def get_goversion(): """ :returns The go-version, assuming that go is installed. """ - return subprocess.check_output(["go", "version"]).decode().strip() + return subprocess.check_output( [ "go", "version" ] ).decode().strip() -def convert_one_to_hex(addr): +def convert_one_to_hex( addr ): """Given a one address, convert it to hex checksum address.""" - if not is_valid_address(addr): - return to_checksum_address(addr) - _, data = bech32_decode(addr) - buf = convertbits(data, 5, 8, False) - address = "0x" + "".join(f"{x:02x}" for x in buf) - return str(to_checksum_address(address)) + if not is_valid_address( addr ): + return to_checksum_address( addr ) + _, data = bech32_decode( addr ) + buf = convertbits( data, 5, 8, False ) + address = "0x" + "".join( f"{x:02x}" for x in buf ) + return str( to_checksum_address( address ) ) -def convert_hex_to_one(addr): +def convert_hex_to_one( addr ): """Given a hex address, convert it to a one address.""" - if is_valid_address(addr): + if is_valid_address( addr ): return addr - checksum_addr = str(to_checksum_address(addr)) + checksum_addr = str( to_checksum_address( addr ) ) data = bytearray.fromhex( - checksum_addr[2:] if checksum_addr.startswith("0x") else checksum_addr + checksum_addr[ 2 : ] if checksum_addr + .startswith( "0x" ) else checksum_addr ) - buf = convertbits(data, 8, 5) - return str(bech32_encode("one", buf)) + buf = convertbits( data, 8, 5 ) + return str( bech32_encode( "one", buf ) ) -def is_active_shard(endpoint, delay_tolerance=60): +def is_active_shard( endpoint, delay_tolerance = 60 ): """ :param endpoint: The endpoint of the SHARD to check :param delay_tolerance: The time (in seconds) that the shard timestamp can be behind @@ -121,14 +119,15 @@ def is_active_shard(endpoint, delay_tolerance=60): """ try: curr_time = datetime.datetime.utcnow() - latest_header = get_latest_header(endpoint=endpoint) - time_str = latest_header["timestamp"][:19] + ".0" # Fit time format + latest_header = get_latest_header( endpoint = endpoint ) + time_str = latest_header[ "timestamp" ][ : 19 ] + ".0" # Fit time format timestamp = datetime.datetime.strptime( - time_str, "%Y-%m-%d %H:%M:%S.%f" - ).replace(tzinfo=None) + time_str, + "%Y-%m-%d %H:%M:%S.%f" + ).replace( tzinfo = None ) time_delta = curr_time - timestamp - return abs(time_delta.seconds) < delay_tolerance - except (RPCError, RequestsError, RequestsTimeoutError): + return abs( time_delta.seconds ) < delay_tolerance + except ( RPCError, RequestsError, RequestsTimeoutError ): return False @@ -144,33 +143,36 @@ def get_bls_build_variables(): variables = {} try: openssl_dir = ( - subprocess.check_output(["which", "openssl"]) - .decode() - .strip() - .split("\n", maxsplit=1)[0] + subprocess.check_output( + [ "which", + "openssl" ] + ).decode().strip().split( "\n", + maxsplit = 1 )[ 0 ] ) - except (IndexError, subprocess.CalledProcessError) as exception: - raise RuntimeError("`openssl` not found") from exception + except ( IndexError, subprocess.CalledProcessError ) as exception: + raise RuntimeError( "`openssl` not found" ) from exception hmy_path = f"{get_gopath()}/src/github.com/harmony-one" bls_dir = f"{hmy_path}/bls" mcl_dir = f"{hmy_path}/mcl" - assert os.path.exists(bls_dir), f"Harmony BLS repo not found at {bls_dir}" - assert os.path.exists(mcl_dir), f"Harmony MCL repo not found at {mcl_dir}" - if sys.platform.startswith("darwin"): + assert os.path.exists( bls_dir ), f"Harmony BLS repo not found at {bls_dir}" + assert os.path.exists( mcl_dir ), f"Harmony MCL repo not found at {mcl_dir}" + if sys.platform.startswith( "darwin" ): variables[ "CGO_CFLAGS" ] = f"-I{bls_dir}/include -I{mcl_dir}/include -I{openssl_dir}/include" - variables["CGO_LDFLAGS"] = f"-L{bls_dir}/lib -L{openssl_dir}/lib" - variables["LD_LIBRARY_PATH"] = f"{bls_dir}/lib:{mcl_dir}/lib:{openssl_dir}/lib" - variables["DYLD_FALLBACK_LIBRARY_PATH"] = variables["LD_LIBRARY_PATH"] + variables[ "CGO_LDFLAGS" ] = f"-L{bls_dir}/lib -L{openssl_dir}/lib" + variables[ "LD_LIBRARY_PATH" + ] = f"{bls_dir}/lib:{mcl_dir}/lib:{openssl_dir}/lib" + variables[ "DYLD_FALLBACK_LIBRARY_PATH" ] = variables[ "LD_LIBRARY_PATH" + ] else: - variables["CGO_CFLAGS"] = f"-I{bls_dir}/include -I{mcl_dir}/include" - variables["CGO_LDFLAGS"] = f"-L{bls_dir}/lib" - variables["LD_LIBRARY_PATH"] = f"{bls_dir}/lib:{mcl_dir}/lib" + variables[ "CGO_CFLAGS" ] = f"-I{bls_dir}/include -I{mcl_dir}/include" + variables[ "CGO_LDFLAGS" ] = f"-L{bls_dir}/lib" + variables[ "LD_LIBRARY_PATH" ] = f"{bls_dir}/lib:{mcl_dir}/lib" return variables -def json_load(string, **kwargs): +def json_load( string, **kwargs ): """ :param string: The JSON string to load :returns A dictionary loaded from a JSON string to a dictionary. @@ -179,7 +181,7 @@ def json_load(string, **kwargs): Note that this prints the failed input should an error arise. """ try: - return json.loads(string, **kwargs) + return json.loads( string, **kwargs ) except Exception as exception: - print(f"{Typgpy.FAIL}Could not parse input: '{string}'{Typgpy.ENDC}") + print( f"{Typgpy.FAIL}Could not parse input: '{string}'{Typgpy.ENDC}" ) raise exception diff --git a/pyhmy/validator.py b/pyhmy/validator.py index 1b77093..7104295 100644 --- a/pyhmy/validator.py +++ b/pyhmy/validator.py @@ -22,11 +22,7 @@ from .constants import ( from .exceptions import InvalidValidatorError -from .rpc.exceptions import ( - RPCError, - RequestsError, - RequestsTimeoutError, -) +from .rpc.exceptions import ( RPCError, RequestsError, RequestsTimeoutError, ) from .staking import get_all_validator_addresses, get_validator_information @@ -34,16 +30,22 @@ from .staking_structures import Directive from .staking_signing import sign_staking_transaction -class Validator: # pylint: disable=too-many-instance-attributes, too-many-public-methods + +class Validator: # pylint: disable=too-many-instance-attributes, too-many-public-methods """ Harmony validator """ - - def __init__(self, address): - if not isinstance(address, str): - raise InvalidValidatorError(1, "given ONE address was not a string") - if not is_valid_address(address): - raise InvalidValidatorError(1, f"{address} is not valid ONE address") + def __init__( self, address ): + if not isinstance( address, str ): + raise InvalidValidatorError( + 1, + "given ONE address was not a string" + ) + if not is_valid_address( address ): + raise InvalidValidatorError( + 1, + f"{address} is not valid ONE address" + ) self._address = address self._bls_keys = [] self._bls_key_sigs = [] @@ -62,7 +64,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public self._max_change_rate = None self._max_rate = None - def _sanitize_input(self, data, check_str=False) -> str: + def _sanitize_input( self, data, check_str = False ) -> str: """If data is None, return '' else return data. Raises @@ -70,26 +72,26 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError if check_str is True and str is not passed """ if check_str: - if not isinstance(data, str): + if not isinstance( data, str ): raise InvalidValidatorError( 3, "Expected data to be string " f"to avoid floating point precision issues but got {data}", ) - return "" if not data else str(data) + return "" if not data else str( data ) - def __str__(self) -> str: + def __str__( self ) -> str: """Returns JSON string representation of Validator fields.""" info = self.export() for key, value in info.items(): - if isinstance(value, Decimal): - info[key] = str(value) - return json.dumps(info) + if isinstance( value, Decimal ): + info[ key ] = str( value ) + return json.dumps( info ) - def __repr__(self) -> str: + def __repr__( self ) -> str: return f"" - def get_address(self) -> str: + def get_address( self ) -> str: """Get validator address. Returns @@ -99,7 +101,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._address - def add_bls_key(self, key) -> bool: + def add_bls_key( self, key ) -> bool: """Add BLS public key to validator BLS keys if not already in list. Returns @@ -107,13 +109,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public bool If adding BLS key succeeded """ - key = self._sanitize_input(key) + key = self._sanitize_input( key ) if key not in self._bls_keys: - self._bls_keys.append(key) + self._bls_keys.append( key ) return True return False - def remove_bls_key(self, key) -> bool: + def remove_bls_key( self, key ) -> bool: """Remove BLS public key from validator BLS keys if exists. Returns @@ -121,13 +123,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public bool If removing BLS key succeeded """ - key = self._sanitize_input(key) + key = self._sanitize_input( key ) if key in self._bls_keys: - self._bls_keys.remove(key) + self._bls_keys.remove( key ) return True return False - def get_bls_keys(self) -> list: + def get_bls_keys( self ) -> list: """Get list of validator BLS keys. Returns @@ -137,7 +139,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._bls_keys - def add_bls_key_sig(self, key) -> bool: + def add_bls_key_sig( self, key ) -> bool: """Add BLS public key to validator BLS keys if not already in list. Returns @@ -145,13 +147,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public bool If adding BLS key succeeded """ - key = self._sanitize_input(key) + key = self._sanitize_input( key ) if key not in self._bls_key_sigs: - self._bls_key_sigs.append(key) + self._bls_key_sigs.append( key ) return True return False - def remove_bls_key_sig(self, key) -> bool: + def remove_bls_key_sig( self, key ) -> bool: """Remove BLS public key from validator BLS keys if exists. Returns @@ -159,13 +161,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public bool If removing BLS key succeeded """ - key = self._sanitize_input(key) + key = self._sanitize_input( key ) if key in self._bls_key_sigs: - self._bls_key_sigs.remove(key) + self._bls_key_sigs.remove( key ) return True return False - def get_bls_key_sigs(self) -> list: + def get_bls_key_sigs( self ) -> list: """Get list of validator BLS keys. Returns @@ -175,7 +177,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._bls_key_sigs - def set_name(self, name): + def set_name( self, name ): """Set validator name. Parameters @@ -188,14 +190,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - name = self._sanitize_input(name) - if len(name) > NAME_CHAR_LIMIT: + name = self._sanitize_input( name ) + if len( name ) > NAME_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Name must be less than {NAME_CHAR_LIMIT} characters" + 3, + f"Name must be less than {NAME_CHAR_LIMIT} characters" ) self._name = name - def get_name(self) -> str: + def get_name( self ) -> str: """Get validator name. Returns @@ -205,7 +208,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._name - def set_identity(self, identity): + def set_identity( self, identity ): """Set validator identity. Parameters @@ -218,14 +221,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - identity = self._sanitize_input(identity) - if len(identity) > IDENTITY_CHAR_LIMIT: + identity = self._sanitize_input( identity ) + if len( identity ) > IDENTITY_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Identity must be less than {IDENTITY_CHAR_LIMIT} characters" + 3, + f"Identity must be less than {IDENTITY_CHAR_LIMIT} characters" ) self._identity = identity - def get_identity(self) -> str: + def get_identity( self ) -> str: """Get validator identity. Returns @@ -235,7 +239,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._identity - def set_website(self, website): + def set_website( self, website ): """Set validator website. Parameters @@ -248,14 +252,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - website = self._sanitize_input(website) - if len(website) > WEBSITE_CHAR_LIMIT: + website = self._sanitize_input( website ) + if len( website ) > WEBSITE_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Website must be less than {WEBSITE_CHAR_LIMIT} characters" + 3, + f"Website must be less than {WEBSITE_CHAR_LIMIT} characters" ) self._website = website - def get_website(self) -> str: + def get_website( self ) -> str: """Get validator website. Returns @@ -265,7 +270,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._website - def set_security_contact(self, contact): + def set_security_contact( self, contact ): """Set validator security contact. Parameters @@ -278,15 +283,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - contact = self._sanitize_input(contact) - if len(contact) > SECURITY_CONTACT_CHAR_LIMIT: + contact = self._sanitize_input( contact ) + if len( contact ) > SECURITY_CONTACT_CHAR_LIMIT: raise InvalidValidatorError( 3, f"Security contact must be less than {SECURITY_CONTACT_CHAR_LIMIT} characters", ) self._security_contact = contact - def get_security_contact(self) -> str: + def get_security_contact( self ) -> str: """Get validator security contact. Returns @@ -296,7 +301,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._security_contact - def set_details(self, details): + def set_details( self, details ): """Set validator details. Parameters @@ -309,14 +314,15 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - details = self._sanitize_input(details) - if len(details) > DETAILS_CHAR_LIMIT: + details = self._sanitize_input( details ) + if len( details ) > DETAILS_CHAR_LIMIT: raise InvalidValidatorError( - 3, f"Details must be less than {DETAILS_CHAR_LIMIT} characters" + 3, + f"Details must be less than {DETAILS_CHAR_LIMIT} characters" ) self._details = details - def get_details(self) -> str: + def get_details( self ) -> str: """Get validator details. Returns @@ -326,7 +332,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._details - def set_min_self_delegation(self, delegation): + def set_min_self_delegation( self, delegation ): """Set validator min self delegation. Parameters @@ -339,12 +345,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - delegation = self._sanitize_input(delegation) + delegation = self._sanitize_input( delegation ) try: - delegation = Decimal(delegation) - except (TypeError, InvalidOperation) as exception: + delegation = Decimal( delegation ) + except ( TypeError, InvalidOperation ) as exception: raise InvalidValidatorError( - 3, "Min self delegation must be a number" + 3, + "Min self delegation must be a number" ) from exception if delegation < MIN_REQUIRED_DELEGATION: raise InvalidValidatorError( @@ -353,7 +360,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public ) self._min_self_delegation = delegation - def get_min_self_delegation(self) -> Decimal: + def get_min_self_delegation( self ) -> Decimal: """Get validator min self delegation. Returns @@ -363,7 +370,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._min_self_delegation - def set_max_total_delegation(self, max_delegation): + def set_max_total_delegation( self, max_delegation ): """Set validator max total delegation. Parameters @@ -376,12 +383,13 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - max_delegation = self._sanitize_input(max_delegation) + max_delegation = self._sanitize_input( max_delegation ) try: - max_delegation = Decimal(max_delegation) - except (TypeError, InvalidOperation) as exception: + max_delegation = Decimal( max_delegation ) + except ( TypeError, InvalidOperation ) as exception: raise InvalidValidatorError( - 3, "Max total delegation must be a number" + 3, + "Max total delegation must be a number" ) from exception if self._min_self_delegation: if max_delegation < self._min_self_delegation: @@ -392,11 +400,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public ) else: raise InvalidValidatorError( - 4, "Min self delegation must be set before max total delegation" + 4, + "Min self delegation must be set before max total delegation" ) self._max_total_delegation = max_delegation - def get_max_total_delegation(self) -> Decimal: + def get_max_total_delegation( self ) -> Decimal: """Get validator max total delegation. Returns @@ -406,7 +415,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._max_total_delegation - def set_amount(self, amount): + def set_amount( self, amount ): """Set validator initial delegation amount. Parameters @@ -419,11 +428,14 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - amount = self._sanitize_input(amount) + amount = self._sanitize_input( amount ) try: - amount = Decimal(amount) - except (TypeError, InvalidOperation) as exception: - raise InvalidValidatorError(3, "Amount must be a number") from exception + amount = Decimal( amount ) + except ( TypeError, InvalidOperation ) as exception: + raise InvalidValidatorError( + 3, + "Amount must be a number" + ) from exception if self._min_self_delegation: if amount < self._min_self_delegation: raise InvalidValidatorError( @@ -433,7 +445,8 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public ) else: raise InvalidValidatorError( - 4, "Min self delegation must be set before amount" + 4, + "Min self delegation must be set before amount" ) if self._max_total_delegation: if amount > self._max_total_delegation: @@ -444,11 +457,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public ) else: raise InvalidValidatorError( - 4, "Max total delegation must be set before amount" + 4, + "Max total delegation must be set before amount" ) self._inital_delegation = amount - def get_amount(self) -> Decimal: + def get_amount( self ) -> Decimal: """Get validator initial delegation amount. Returns @@ -458,7 +472,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._inital_delegation - def set_max_rate(self, rate): + def set_max_rate( self, rate ): """Set validator max commission rate. Parameters @@ -471,16 +485,19 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - rate = self._sanitize_input(rate, True) + rate = self._sanitize_input( rate, True ) try: - rate = Decimal(rate) - except (TypeError, InvalidOperation) as exception: - raise InvalidValidatorError(3, "Max rate must be a number") from exception + rate = Decimal( rate ) + except ( TypeError, InvalidOperation ) as exception: + raise InvalidValidatorError( + 3, + "Max rate must be a number" + ) from exception if rate < 0 or rate > 1: - raise InvalidValidatorError(3, "Max rate must be between 0 and 1") + raise InvalidValidatorError( 3, "Max rate must be between 0 and 1" ) self._max_rate = rate - def get_max_rate(self) -> Decimal: + def get_max_rate( self ) -> Decimal: """Get validator max commission rate. Returns @@ -490,7 +507,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._max_rate - def set_max_change_rate(self, rate): + def set_max_change_rate( self, rate ): """Set validator max commission change rate. Parameters @@ -503,14 +520,18 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - rate = self._sanitize_input(rate, True) + rate = self._sanitize_input( rate, True ) try: - rate = Decimal(rate) - except (TypeError, InvalidOperation) as exception: - raise InvalidValidatorError(3, "Max change rate must be a number") from exception + rate = Decimal( rate ) + except ( TypeError, InvalidOperation ) as exception: + raise InvalidValidatorError( + 3, + "Max change rate must be a number" + ) from exception if rate < 0: raise InvalidValidatorError( - 3, "Max change rate must be greater than or equal to 0" + 3, + "Max change rate must be greater than or equal to 0" ) if self._max_rate: if rate > self._max_rate: @@ -520,11 +541,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public ) else: raise InvalidValidatorError( - 4, "Max rate must be set before max change rate" + 4, + "Max rate must be set before max change rate" ) self._max_change_rate = rate - def get_max_change_rate(self) -> Decimal: + def get_max_change_rate( self ) -> Decimal: """Get validator max commission change rate. Returns @@ -534,7 +556,7 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public """ return self._max_change_rate - def set_rate(self, rate): + def set_rate( self, rate ): """Set validator commission rate. Parameters @@ -547,23 +569,30 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public InvalidValidatorError If input is invalid """ - rate = self._sanitize_input(rate, True) + rate = self._sanitize_input( rate, True ) try: - rate = Decimal(rate) - except (TypeError, InvalidOperation) as exception: - raise InvalidValidatorError(3, "Rate must be a number") from exception + rate = Decimal( rate ) + except ( TypeError, InvalidOperation ) as exception: + raise InvalidValidatorError( + 3, + "Rate must be a number" + ) from exception if rate < 0: - raise InvalidValidatorError(3, "Rate must be greater than or equal to 0") + raise InvalidValidatorError( + 3, + "Rate must be greater than or equal to 0" + ) if self._max_rate: if rate > self._max_rate: raise InvalidValidatorError( - 3, f"Rate must be less than or equal to max rate: {self._max_rate}" + 3, + f"Rate must be less than or equal to max rate: {self._max_rate}" ) else: - raise InvalidValidatorError(4, "Max rate must be set before rate") + raise InvalidValidatorError( 4, "Max rate must be set before rate" ) self._rate = rate - def get_rate(self) -> Decimal: + def get_rate( self ) -> Decimal: """Get validator commission rate. Returns @@ -574,7 +603,9 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public return self._rate def does_validator_exist( - self, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + self, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ) -> bool: """Check if validator exists on blockchain. @@ -595,12 +626,12 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public RPCError, RequestsError, RequestsTimeoutError If unable to get list of validators on chain """ - all_validators = get_all_validator_addresses(endpoint, timeout) + all_validators = get_all_validator_addresses( endpoint, timeout ) if self._address in all_validators: return True return False - def load(self, info): + def load( self, info ): """Import validator information. Parameters @@ -631,32 +662,37 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public If input value is invalid """ try: - self.set_name(info["name"]) - self.set_identity(info["identity"]) - self.set_website(info["website"]) - self.set_details(info["details"]) - self.set_security_contact(info["security-contact"]) + self.set_name( info[ "name" ] ) + self.set_identity( info[ "identity" ] ) + self.set_website( info[ "website" ] ) + self.set_details( info[ "details" ] ) + self.set_security_contact( info[ "security-contact" ] ) - self.set_min_self_delegation(info["min-self-delegation"]) - self.set_max_total_delegation(info["max-total-delegation"]) - self.set_amount(info["amount"]) + self.set_min_self_delegation( info[ "min-self-delegation" ] ) + self.set_max_total_delegation( info[ "max-total-delegation" ] ) + self.set_amount( info[ "amount" ] ) - self.set_max_rate(info["max-rate"]) - self.set_max_change_rate(info["max-change-rate"]) - self.set_rate(info["rate"]) + self.set_max_rate( info[ "max-rate" ] ) + self.set_max_change_rate( info[ "max-change-rate" ] ) + self.set_rate( info[ "rate" ] ) self._bls_keys = [] - for key in info["bls-public-keys"]: - self.add_bls_key(key) + for key in info[ "bls-public-keys" ]: + self.add_bls_key( key ) self._bls_key_sigs = [] - for key in info["bls-key-sigs"]: - self.add_bls_key_sig(key) + for key in info[ "bls-key-sigs" ]: + self.add_bls_key_sig( key ) except KeyError as exception: - raise InvalidValidatorError(3, "Info has missing key") from exception + raise InvalidValidatorError( + 3, + "Info has missing key" + ) from exception def load_from_blockchain( - self, endpoint=DEFAULT_ENDPOINT, timeout=DEFAULT_TIMEOUT + self, + endpoint = DEFAULT_ENDPOINT, + timeout = DEFAULT_TIMEOUT ): """Import validator information from blockchain with given address At the moment, this is unable to fetch the BLS Signature, which is not @@ -675,46 +711,54 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public If any error occur getting & importing validator information from the blockchain """ try: - if not self.does_validator_exist(endpoint, timeout): + if not self.does_validator_exist( endpoint, timeout ): raise InvalidValidatorError( - 5, f"Validator does not exist on chain according to {endpoint}" + 5, + f"Validator does not exist on chain according to {endpoint}" ) - except (RPCError, RequestsError, RequestsTimeoutError) as exception: + except ( RPCError, RequestsError, RequestsTimeoutError ) as exception: raise InvalidValidatorError( - 5, "Error requesting validator information" + 5, + "Error requesting validator information" ) from exception try: - validator_info = get_validator_information(self._address, endpoint, timeout) - except (RPCError, RequestsError, RequestsTimeoutError) as exception: + validator_info = get_validator_information( + self._address, + endpoint, + timeout + ) + except ( RPCError, RequestsError, RequestsTimeoutError ) as exception: raise InvalidValidatorError( - 5, "Error requesting validator information" + 5, + "Error requesting validator information" ) from exception # Skip additional sanity checks when importing from chain try: - info = validator_info["validator"] - self._name = info["name"] - self._identity = info["identity"] - self._website = info["website"] - self._details = info["details"] - self._security_contact = info["security-contact"] - - self._min_self_delegation = info["min-self-delegation"] - self._max_total_delegation = info["max-total-delegation"] + info = validator_info[ "validator" ] + self._name = info[ "name" ] + self._identity = info[ "identity" ] + self._website = info[ "website" ] + self._details = info[ "details" ] + self._security_contact = info[ "security-contact" ] + + self._min_self_delegation = info[ "min-self-delegation" ] + self._max_total_delegation = info[ "max-total-delegation" ] self._inital_delegation = ( self._min_self_delegation ) # Since validator exists, set initial delegation to 0 - self._max_rate = Decimal(info["max-rate"]) - self._max_change_rate = Decimal(info["max-change-rate"]) - self._rate = Decimal(info["rate"]) - self._bls_keys = info["bls-public-keys"] + self._max_rate = Decimal( info[ "max-rate" ] ) + self._max_change_rate = Decimal( info[ "max-change-rate" ] ) + self._rate = Decimal( info[ "rate" ] ) + self._bls_keys = info[ "bls-public-keys" ] except KeyError as exception: raise InvalidValidatorError( - 5, "Error importing validator information from RPC result" + 5, + "Error importing validator information from RPC result" ) from exception - def export(self) -> dict: + def export( self ) -> dict: """Export validator information as dict. Returns @@ -760,14 +804,16 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L413 """ info = self.export().copy() - info["directive"] = Directive.CreateValidator - info["validatorAddress"] = info.pop("validator-addr") # change the key - info["nonce"] = nonce - info["gasPrice"] = gas_price - info["gasLimit"] = gas_limit + info[ "directive" ] = Directive.CreateValidator + info[ "validatorAddress" ] = info.pop( + "validator-addr" + ) # change the key + info[ "nonce" ] = nonce + info[ "gasPrice" ] = gas_price + info[ "gasLimit" ] = gas_limit if chain_id: - info["chainId"] = chain_id - return sign_staking_transaction(info, private_key) + info[ "chainId" ] = chain_id + return sign_staking_transaction( info, private_key ) def sign_edit_validator_transaction( # pylint: disable=too-many-arguments self, @@ -797,22 +843,24 @@ class Validator: # pylint: disable=too-many-instance-attributes, too-many-public ------------- https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L460 """ - self.set_rate(rate) - self.add_bls_key(bls_key_to_add) - self.remove_bls_key(bls_key_to_remove) + self.set_rate( rate ) + self.add_bls_key( bls_key_to_add ) + self.remove_bls_key( bls_key_to_remove ) info = self.export().copy() - info["directive"] = Directive.EditValidator - info["validatorAddress"] = info.pop("validator-addr") # change the key - info["nonce"] = nonce - info["gasPrice"] = gas_price - info["gasLimit"] = gas_limit - _ = info.pop("max-rate") # not needed - _ = info.pop("max-change-rate") # not needed - _ = info.pop("bls-public-keys") # remove this list - _ = info.pop("amount") # also unused - info["bls-key-to-remove"] = bls_key_to_remove - info["bls-key-to-add"] = bls_key_to_add - info["bls-key-to-add-sig"] = bls_key_to_add_sig + info[ "directive" ] = Directive.EditValidator + info[ "validatorAddress" ] = info.pop( + "validator-addr" + ) # change the key + info[ "nonce" ] = nonce + info[ "gasPrice" ] = gas_price + info[ "gasLimit" ] = gas_limit + _ = info.pop( "max-rate" ) # not needed + _ = info.pop( "max-change-rate" ) # not needed + _ = info.pop( "bls-public-keys" ) # remove this list + _ = info.pop( "amount" ) # also unused + info[ "bls-key-to-remove" ] = bls_key_to_remove + info[ "bls-key-to-add" ] = bls_key_to_add + info[ "bls-key-to-add-sig" ] = bls_key_to_add_sig if chain_id: - info["chainId"] = chain_id - return sign_staking_transaction(info, private_key) + info[ "chainId" ] = chain_id + return sign_staking_transaction( info, private_key ) diff --git a/tests/bech32-pyhmy/test_bech32.py b/tests/bech32-pyhmy/test_bech32.py index e92202f..f9d3e77 100644 --- a/tests/bech32-pyhmy/test_bech32.py +++ b/tests/bech32-pyhmy/test_bech32.py @@ -2,8 +2,8 @@ from pyhmy.bech32 import bech32 def test_encode(): - bech32.encode("one", 5, [121, 161]) + bech32.encode( "one", 5, [ 121, 161 ] ) def test_decode(): - bech32.decode("one", "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") + bech32.decode( "one", "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" ) diff --git a/tests/cli-pyhmy/test_cli.py b/tests/cli-pyhmy/test_cli.py index e6596f5..844b979 100644 --- a/tests/cli-pyhmy/test_cli.py +++ b/tests/cli-pyhmy/test_cli.py @@ -10,57 +10,58 @@ TEMP_DIR = "/tmp/pyhmy-testing/test-cli" BINARY_FILE_PATH = f"{TEMP_DIR}/bin/cli_test_binary" -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture( scope = "session", autouse = True ) def setup(): - shutil.rmtree(TEMP_DIR, ignore_errors=True) - os.makedirs(TEMP_DIR, exist_ok=True) + shutil.rmtree( TEMP_DIR, ignore_errors = True ) + os.makedirs( TEMP_DIR, exist_ok = True ) def test_download_cli(): - env = cli.download(BINARY_FILE_PATH, replace=False, verbose=False) - cli.environment.update(env) - assert os.path.exists(BINARY_FILE_PATH) + env = cli.download( BINARY_FILE_PATH, replace = False, verbose = False ) + cli.environment.update( env ) + assert os.path.exists( BINARY_FILE_PATH ) def test_is_valid(): - bad_file_path = os.path.realpath(f"{TEMP_DIR}/test_is_valid/bad_hmy") - shutil.rmtree(Path(bad_file_path).parent, ignore_errors=True) - os.makedirs(Path(bad_file_path).parent, exist_ok=True) - Path(bad_file_path).touch() - assert os.path.exists(BINARY_FILE_PATH), "harmony cli did not download" - assert os.path.exists(bad_file_path), "did not create bad binary" - assert cli.is_valid_binary(BINARY_FILE_PATH) - assert not cli.is_valid_binary(bad_file_path) + bad_file_path = os.path.realpath( f"{TEMP_DIR}/test_is_valid/bad_hmy" ) + shutil.rmtree( Path( bad_file_path ).parent, ignore_errors = True ) + os.makedirs( Path( bad_file_path ).parent, exist_ok = True ) + Path( bad_file_path ).touch() + assert os.path.exists( BINARY_FILE_PATH ), "harmony cli did not download" + assert os.path.exists( bad_file_path ), "did not create bad binary" + assert cli.is_valid_binary( BINARY_FILE_PATH ) + assert not cli.is_valid_binary( bad_file_path ) def test_bad_bin_set(): - bad_file_path = os.path.realpath(f"{TEMP_DIR}/test_bad_bin_set/hmy") - shutil.rmtree(Path(bad_file_path).parent, ignore_errors=True) - os.makedirs(Path(bad_file_path).parent, exist_ok=True) - Path(bad_file_path).touch() - is_set = cli.set_binary(bad_file_path) + bad_file_path = os.path.realpath( f"{TEMP_DIR}/test_bad_bin_set/hmy" ) + shutil.rmtree( Path( bad_file_path ).parent, ignore_errors = True ) + os.makedirs( Path( bad_file_path ).parent, exist_ok = True ) + Path( bad_file_path ).touch() + is_set = cli.set_binary( bad_file_path ) assert not is_set assert cli.get_binary_path() != bad_file_path def test_bin_set(): - cli.set_binary(BINARY_FILE_PATH) + cli.set_binary( BINARY_FILE_PATH ) cli_binary_path = cli.get_binary_path() - assert os.path.realpath(cli_binary_path) == os.path.realpath(BINARY_FILE_PATH) + assert os.path.realpath( cli_binary_path + ) == os.path.realpath( BINARY_FILE_PATH ) def test_update_keystore(): - cli.single_call("hmy keys add test1") + cli.single_call( "hmy keys add test1" ) addrs = cli.get_accounts_keystore() assert "test1" in addrs.keys() - check_addr = addrs["test1"] - accounts_list = cli.get_accounts(check_addr) - check_acc = accounts_list[0] + check_addr = addrs[ "test1" ] + accounts_list = cli.get_accounts( check_addr ) + check_acc = accounts_list[ 0 ] assert check_acc == "test1" - raw_cli_keys_list_print = cli.single_call("hmy keys list", timeout=2) + raw_cli_keys_list_print = cli.single_call( "hmy keys list", timeout = 2 ) assert check_addr in raw_cli_keys_list_print assert check_acc in raw_cli_keys_list_print - assert addrs[check_acc] == check_addr - cli.remove_address(check_addr) + assert addrs[ check_acc ] == check_addr + cli.remove_address( check_addr ) assert check_addr not in addrs.values() assert "test1" not in addrs.keys() diff --git a/tests/logging-pyhmy/test_logging.py b/tests/logging-pyhmy/test_logging.py index f15796d..30d6063 100644 --- a/tests/logging-pyhmy/test_logging.py +++ b/tests/logging-pyhmy/test_logging.py @@ -6,19 +6,19 @@ from pyhmy import logging def test_basic_logger(): - if os.path.exists(f"{os.getcwd()}/logs/pytest.log"): - os.remove(f"{os.getcwd()}/logs/pytest.log") - logger = logging.ControlledLogger("pytest", "logs/") - assert os.path.exists(f"{os.getcwd()}/logs/pytest.log") - logger.info("test info") - logger.debug("test debug") - logger.error("test error") - logger.warning("test warning") - with open(f"{os.getcwd()}/logs/pytest.log", "r") as f: + if os.path.exists( f"{os.getcwd()}/logs/pytest.log" ): + os.remove( f"{os.getcwd()}/logs/pytest.log" ) + logger = logging.ControlledLogger( "pytest", "logs/" ) + assert os.path.exists( f"{os.getcwd()}/logs/pytest.log" ) + logger.info( "test info" ) + logger.debug( "test debug" ) + logger.error( "test error" ) + logger.warning( "test warning" ) + with open( f"{os.getcwd()}/logs/pytest.log", "r" ) as f: log_file_contents = f.readlines() assert not log_file_contents logger.write() - with open(f"{os.getcwd()}/logs/pytest.log", "r") as f: + with open( f"{os.getcwd()}/logs/pytest.log", "r" ) as f: log_file_contents = f.readlines() for line in log_file_contents: if "INFO" in line: diff --git a/tests/numbers-pyhmy/test_numbers.py b/tests/numbers-pyhmy/test_numbers.py index b8d277a..6384fe2 100644 --- a/tests/numbers-pyhmy/test_numbers.py +++ b/tests/numbers-pyhmy/test_numbers.py @@ -4,28 +4,28 @@ from pyhmy import numbers def test_convert_atto_to_one(): - a = numbers.convert_atto_to_one(1e18) - assert Decimal(1) == a + a = numbers.convert_atto_to_one( 1e18 ) + assert Decimal( 1 ) == a - b = numbers.convert_atto_to_one(1e18 + 0.6) - assert Decimal(1) == b + b = numbers.convert_atto_to_one( 1e18 + 0.6 ) + assert Decimal( 1 ) == b - c = numbers.convert_atto_to_one("1" + ("0" * 18)) - assert Decimal(1) == c + c = numbers.convert_atto_to_one( "1" + ( "0" * 18 ) ) + assert Decimal( 1 ) == c - d = numbers.convert_atto_to_one(Decimal(1e18)) - assert Decimal(1) == d + d = numbers.convert_atto_to_one( Decimal( 1e18 ) ) + assert Decimal( 1 ) == d def test_convert_one_to_atto(): - a = numbers.convert_one_to_atto(1e-18) - assert Decimal(1) == a + a = numbers.convert_one_to_atto( 1e-18 ) + assert Decimal( 1 ) == a - b = numbers.convert_one_to_atto(1.5) - assert Decimal(1.5e18) == b + b = numbers.convert_one_to_atto( 1.5 ) + assert Decimal( 1.5e18 ) == b - c = numbers.convert_one_to_atto("1") - assert Decimal(1e18) == c + c = numbers.convert_one_to_atto( "1" ) + assert Decimal( 1e18 ) == c - d = numbers.convert_one_to_atto(Decimal(1)) - assert Decimal(1e18) == d + d = numbers.convert_one_to_atto( Decimal( 1 ) ) + assert Decimal( 1e18 ) == d diff --git a/tests/request-pyhmy/test_request.py b/tests/request-pyhmy/test_request.py index 4505069..74a2e7c 100644 --- a/tests/request-pyhmy/test_request.py +++ b/tests/request-pyhmy/test_request.py @@ -7,56 +7,70 @@ import requests from pyhmy.rpc import exceptions, request -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture( scope = "session", autouse = True ) def setup(): endpoint = "http://localhost:9500" timeout = 30 method = "hmyv2_getNodeMetadata" params = [] - payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} - headers = {"Content-Type": "application/json"} + payload = { + "id": "1", + "jsonrpc": "2.0", + "method": method, + "params": params + } + headers = { + "Content-Type": "application/json" + } try: response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) except Exception as e: - pytest.skip("can not connect to local blockchain", allow_module_level=True) + pytest.skip( + "can not connect to local blockchain", + allow_module_level = True + ) def test_request_connection_error(): # Find available port s = socket.socket() - s.bind(("localhost", 0)) - port = s.getsockname()[1] + s.bind( ( "localhost", 0 ) ) + port = s.getsockname()[ 1 ] s.close() if port == 0: - pytest.skip("could not find available port") + pytest.skip( "could not find available port" ) bad_endpoint = f"http://localhost:{port}" bad_request = None try: bad_request = request.rpc_request( - "hmyv2_getNodeMetadata", endpoint=bad_endpoint + "hmyv2_getNodeMetadata", + endpoint = bad_endpoint ) except Exception as e: - assert isinstance(e, exceptions.RequestsError) + assert isinstance( e, exceptions.RequestsError ) assert bad_request is None def test_request_rpc_error(): error_request = None try: - error_request = request.rpc_request("hmyv2_getBalance") - except (exceptions.RequestsTimeoutError, exceptions.RequestsError) as err: - pytest.skip("can not connect to local blockchain", allow_module_level=True) + error_request = request.rpc_request( "hmyv2_getBalance" ) + except ( exceptions.RequestsTimeoutError, exceptions.RequestsError ) as err: + pytest.skip( + "can not connect to local blockchain", + allow_module_level = True + ) except Exception as e: - assert isinstance(e, exceptions.RPCError) + assert isinstance( e, exceptions.RPCError ) assert error_request is None @@ -65,33 +79,40 @@ def test_rpc_request(): timeout = 30 method = "hmyv2_getNodeMetadata" params = [] - payload = {"id": "1", "jsonrpc": "2.0", "method": method, "params": params} - headers = {"Content-Type": "application/json"} + payload = { + "id": "1", + "jsonrpc": "2.0", + "method": method, + "params": params + } + headers = { + "Content-Type": "application/json" + } response = None try: response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) except: - pytest.skip("can not connect to local blockchain") + pytest.skip( "can not connect to local blockchain" ) assert response is not None resp = None try: - resp = json.loads(response.content) + resp = json.loads( response.content ) except json.decoder.JSONDecodeError as err: - pytest.skip("unable to decode response") + pytest.skip( "unable to decode response" ) assert resp is not None rpc_response = None try: - rpc_response = request.rpc_request(method, params, endpoint, timeout) + rpc_response = request.rpc_request( method, params, endpoint, timeout ) except exceptions.RPCError as e: assert "error" in resp diff --git a/tests/sdk-pyhmy/conftest.py b/tests/sdk-pyhmy/conftest.py index d9f3af3..b155b92 100644 --- a/tests/sdk-pyhmy/conftest.py +++ b/tests/sdk-pyhmy/conftest.py @@ -11,7 +11,9 @@ import requests endpoint = "http://localhost:9500" timeout = 30 -headers = {"Content-Type": "application/json"} +headers = { + "Content-Type": "application/json" +} txs = [ # same shard 503 ONE transfer from one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3 to one1ru3p8ff0wsyl7ncsx3vwd5szuze64qz60upg37 (0 nonce) "0xf86f8085174876e8008252088080941f2213a52f7409ff4f103458e6d202e0b3aa805a891b4486fafde57c00008027a0d7c0b20207dcc9dde376822dc3f5625eac6f59a7526111695cdba3e29553ca17a05d4ca9a421ae16f89cbf6848186eaea7a800da732446dff9952e7c1e91d414e3", @@ -35,35 +37,37 @@ stx_hashes = [ "0x400e9831d358f5daccd153cad5bf53650a0d413bd8682ec0ffad55367d162968", "0xc8177ace2049d9f4eb4a45fd6bd6b16f693573d036322c36774cc00d05a3e24f", ] -assert len(txs) == len(tx_hashes), "Mismatch in tx and tx_hash count" -assert len(stxs) == len(stx_hashes), "Mismatch in stx and stx_hash count" +assert len( txs ) == len( tx_hashes ), "Mismatch in tx and tx_hash count" +assert len( stxs ) == len( stx_hashes ), "Mismatch in stx and stx_hash count" -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture( scope = "session", autouse = True ) def setup_blockchain(): # return metadata = _check_connection() - _check_staking_epoch(metadata) + _check_staking_epoch( metadata ) - for i in range(len(txs)): - tx = txs[i] - tx_hash = tx_hashes[i] - _send_transaction(tx, endpoint) - if not _wait_for_transaction_confirmed(tx_hash, endpoint): + for i in range( len( txs ) ): + tx = txs[ i ] + tx_hash = tx_hashes[ i ] + _send_transaction( tx, endpoint ) + if not _wait_for_transaction_confirmed( tx_hash, endpoint ): pytest.skip( - "Could not confirm initial transaction #{} on chain".format(i), - allow_module_level=True, + "Could not confirm initial transaction #{} on chain" + .format( i ), + allow_module_level = True, ) - for i in range(len(stxs)): - stx = stxs[i] - stx_hash = stx_hashes[i] - _send_staking_transaction(stx, endpoint) - if not _wait_for_staking_transaction_confirmed(stx_hash, endpoint): + for i in range( len( stxs ) ): + stx = stxs[ i ] + stx_hash = stx_hashes[ i ] + _send_staking_transaction( stx, endpoint ) + if not _wait_for_staking_transaction_confirmed( stx_hash, endpoint ): pytest.skip( - "Could not confirm initial staking transaction #{} on chain".format(i), - allow_module_level=True, + "Could not confirm initial staking transaction #{} on chain" + .format( i ), + allow_module_level = True, ) @@ -78,31 +82,31 @@ def _check_connection(): response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) - metadata = json.loads(response.content) + metadata = json.loads( response.content ) if "error" in metadata: pytest.skip( f"Error in hmyv2_getNodeMetadata reply: {metadata['error']}", - allow_module_level=True, + allow_module_level = True, ) - if "chain-config" not in metadata["result"]: + if "chain-config" not in metadata[ "result" ]: pytest.skip( "Chain config not found in hmyv2_getNodeMetadata reply", - allow_module_level=True, + allow_module_level = True, ) return metadata except Exception as e: pytest.skip( "Can not connect to local blockchain or bad hmyv2_getNodeMetadata reply", - allow_module_level=True, + allow_module_level = True, ) -def _check_staking_epoch(metadata): +def _check_staking_epoch( metadata ): latest_header = None try: payload = { @@ -114,158 +118,164 @@ def _check_staking_epoch(metadata): response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) - latest_header = json.loads(response.content) + latest_header = json.loads( response.content ) if "error" in latest_header: pytest.skip( f"Error in hmyv2_latestHeader reply: {latest_header['error']}", - allow_module_level=True, + allow_module_level = True, ) except Exception as e: - pytest.skip("Failed to get hmyv2_latestHeader reply", allow_module_level=True) + pytest.skip( + "Failed to get hmyv2_latestHeader reply", + allow_module_level = True + ) if metadata and latest_header: - staking_epoch = metadata["result"]["chain-config"]["staking-epoch"] - current_epoch = latest_header["result"]["epoch"] + staking_epoch = metadata[ "result" ][ "chain-config" ][ "staking-epoch" + ] + current_epoch = latest_header[ "result" ][ "epoch" ] if staking_epoch > current_epoch: pytest.skip( f"Not staking epoch: current {current_epoch}, staking {staking_epoch}", - allow_module_level=True, + allow_module_level = True, ) -def _send_transaction(raw_tx, endpoint): +def _send_transaction( raw_tx, endpoint ): try: payload = { "id": "1", "jsonrpc": "2.0", "method": "hmyv2_sendRawTransaction", - "params": [raw_tx], + "params": [ raw_tx ], } response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) - tx = json.loads(response.content) + tx = json.loads( response.content ) if "error" in tx: pytest.skip( f"Error in hmyv2_sendRawTransaction reply: {tx['error']}", - allow_module_level=True, + allow_module_level = True, ) except Exception as e: pytest.skip( - "Failed to get hmyv2_sendRawTransaction reply", allow_module_level=True + "Failed to get hmyv2_sendRawTransaction reply", + allow_module_level = True ) -def _check_transaction(tx_hash, endpoint): +def _check_transaction( tx_hash, endpoint ): try: payload = { "id": "1", "jsonrpc": "2.0", "method": "hmyv2_getTransactionByHash", - "params": [tx_hash], + "params": [ tx_hash ], } response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) - tx_data = json.loads(response.content) + tx_data = json.loads( response.content ) return tx_data except Exception as e: pytest.skip( - "Failed to get hmyv2_getTransactionByHash reply", allow_module_level=True + "Failed to get hmyv2_getTransactionByHash reply", + allow_module_level = True ) -def _wait_for_transaction_confirmed(tx_hash, endpoint, timeout=30): +def _wait_for_transaction_confirmed( tx_hash, endpoint, timeout = 30 ): start_time = time.time() - while (time.time() - start_time) <= timeout: - tx_data = _check_transaction(tx_hash, endpoint) + while ( time.time() - start_time ) <= timeout: + tx_data = _check_transaction( tx_hash, endpoint ) if tx_data is not None: - block_hash = tx_data["result"].get("blockHash", "0x00") - unique_chars = "".join(set(list(block_hash[2:]))) + block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) if unique_chars != "0": return True - time.sleep(random.uniform(0.2, 0.5)) + time.sleep( random.uniform( 0.2, 0.5 ) ) return False -def _send_staking_transaction(raw_tx, endpoint=endpoint): +def _send_staking_transaction( raw_tx, endpoint = endpoint ): try: payload = { "id": "1", "jsonrpc": "2.0", "method": "hmyv2_sendRawStakingTransaction", - "params": [raw_tx], + "params": [ raw_tx ], } response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) - staking_tx = json.loads(response.content) + staking_tx = json.loads( response.content ) if "error" in staking_tx: pytest.skip( f"Error in hmyv2_sendRawStakingTransaction reply: {staking_tx['error']}", - allow_module_level=True, + allow_module_level = True, ) except Exception as e: pytest.skip( "Failed to get hmyv2_sendRawStakingTransaction reply", - allow_module_level=True, + allow_module_level = True, ) -def _check_staking_transaction(stx_hash, endpoint=endpoint): +def _check_staking_transaction( stx_hash, endpoint = endpoint ): try: payload = { "id": "1", "jsonrpc": "2.0", "method": "hmyv2_getStakingTransactionByHash", - "params": [stx_hash], + "params": [ stx_hash ], } response = requests.request( "POST", endpoint, - headers=headers, - data=json.dumps(payload), - timeout=timeout, - allow_redirects=True, + headers = headers, + data = json.dumps( payload ), + timeout = timeout, + allow_redirects = True, ) - stx_data = json.loads(response.content) + stx_data = json.loads( response.content ) return stx_data except Exception as e: pytest.skip( "Failed to get hmyv2_getStakingTransactionByHash reply", - allow_module_level=True, + allow_module_level = True, ) -def _wait_for_staking_transaction_confirmed(tx_hash, endpoint, timeout=30): +def _wait_for_staking_transaction_confirmed( tx_hash, endpoint, timeout = 30 ): answer = False start_time = time.time() - while (time.time() - start_time) <= timeout: - tx_data = _check_staking_transaction(tx_hash, endpoint) + while ( time.time() - start_time ) <= timeout: + tx_data = _check_staking_transaction( tx_hash, endpoint ) if tx_data is not None: - block_hash = tx_data["result"].get("blockHash", "0x00") - unique_chars = "".join(set(list(block_hash[2:]))) + block_hash = tx_data[ "result" ].get( "blockHash", "0x00" ) + unique_chars = "".join( set( list( block_hash[ 2 : ] ) ) ) if unique_chars != "0": answer = True - time.sleep(random.uniform(0.2, 0.5)) + time.sleep( random.uniform( 0.2, 0.5 ) ) return answer diff --git a/tests/sdk-pyhmy/test_account.py b/tests/sdk-pyhmy/test_account.py index b6f0ed5..89a65e6 100644 --- a/tests/sdk-pyhmy/test_account.py +++ b/tests/sdk-pyhmy/test_account.py @@ -5,7 +5,6 @@ from pyhmy import account from pyhmy.rpc import exceptions - explorer_endpoint = "http://localhost:9700" endpoint_shard_one = "http://localhost:9502" local_test_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" @@ -15,122 +14,142 @@ test_block_number = 1 fake_shard = "http://example.com" -def _test_account_rpc(fn, *args, **kwargs): - if not callable(fn): - pytest.fail(f"Invalid function: {fn}") +def _test_account_rpc( fn, *args, **kwargs ): + if not callable( fn ): + pytest.fail( f"Invalid function: {fn}" ) try: - response = fn(*args, **kwargs) + response = fn( *args, **kwargs ) except Exception as e: - if isinstance( - e, exceptions.RPCError - ) and "does not exist/is not available" in str(e): - pytest.skip(f"{str(e)}") - pytest.fail(f"Unexpected error: {e.__class__} {e}") + if isinstance( e, + exceptions.RPCError + ) and "does not exist/is not available" in str( e ): + pytest.skip( f"{str(e)}" ) + pytest.fail( f"Unexpected error: {e.__class__} {e}" ) return response -def test_get_balance(setup_blockchain): - balance = _test_account_rpc(account.get_balance, local_test_address) - assert isinstance(balance, int) +def test_get_balance( setup_blockchain ): + balance = _test_account_rpc( account.get_balance, local_test_address ) + assert isinstance( balance, int ) assert balance > 0 -def test_get_balance_by_block(setup_blockchain): +def test_get_balance_by_block( setup_blockchain ): balance = _test_account_rpc( - account.get_balance_by_block, local_test_address, genesis_block_number + account.get_balance_by_block, + local_test_address, + genesis_block_number ) - assert isinstance(balance, int) + assert isinstance( balance, int ) assert balance > 0 -def test_get_account_nonce(setup_blockchain): +def test_get_account_nonce( setup_blockchain ): true_nonce = _test_account_rpc( account.get_account_nonce, local_test_address, test_block_number, - endpoint=endpoint_shard_one, + endpoint = endpoint_shard_one, ) - assert isinstance(true_nonce, int) + assert isinstance( true_nonce, int ) -def test_get_transaction_history(setup_blockchain): +def test_get_transaction_history( setup_blockchain ): tx_history = _test_account_rpc( - account.get_transaction_history, local_test_address, endpoint=explorer_endpoint + account.get_transaction_history, + local_test_address, + endpoint = explorer_endpoint ) - assert isinstance(tx_history, list) - assert len(tx_history) >= 0 + assert isinstance( tx_history, list ) + assert len( tx_history ) >= 0 -def test_get_staking_transaction_history(setup_blockchain): +def test_get_staking_transaction_history( setup_blockchain ): staking_tx_history = _test_account_rpc( account.get_staking_transaction_history, test_validator_address, - endpoint=explorer_endpoint, + endpoint = explorer_endpoint, ) - assert isinstance(staking_tx_history, list) - assert len(staking_tx_history) > 0 + assert isinstance( staking_tx_history, list ) + assert len( staking_tx_history ) > 0 -def test_get_balance_on_all_shards(setup_blockchain): - balances = _test_account_rpc(account.get_balance_on_all_shards, local_test_address) - assert isinstance(balances, list) - assert len(balances) == 2 +def test_get_balance_on_all_shards( setup_blockchain ): + balances = _test_account_rpc( + account.get_balance_on_all_shards, + local_test_address + ) + assert isinstance( balances, list ) + assert len( balances ) == 2 -def test_get_total_balance(setup_blockchain): - total_balance = _test_account_rpc(account.get_total_balance, local_test_address) - assert isinstance(total_balance, int) +def test_get_total_balance( setup_blockchain ): + total_balance = _test_account_rpc( + account.get_total_balance, + local_test_address + ) + assert isinstance( total_balance, int ) assert total_balance > 0 def test_is_valid_address(): - assert account.is_valid_address("one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur") - assert not account.is_valid_address("one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0") + assert account.is_valid_address( + "one1zksj3evekayy90xt4psrz8h6j2v3hla4qwz4ur" + ) + assert not account.is_valid_address( + "one1wje75aedczmj4dwjs0812xcg7vx0dy231cajk0" + ) -def test_get_transaction_count(setup_blockchain): +def test_get_transaction_count( setup_blockchain ): tx_count = _test_account_rpc( - account.get_transaction_count, local_test_address, "latest", explorer_endpoint + account.get_transaction_count, + local_test_address, + "latest", + explorer_endpoint ) - assert isinstance(tx_count, int) + assert isinstance( tx_count, int ) assert tx_count > 0 -def test_get_transactions_count(setup_blockchain): +def test_get_transactions_count( setup_blockchain ): tx_count = _test_account_rpc( - account.get_transactions_count, local_test_address, "ALL", explorer_endpoint + account.get_transactions_count, + local_test_address, + "ALL", + explorer_endpoint ) -def test_get_staking_transactions_count(setup_blockchain): +def test_get_staking_transactions_count( setup_blockchain ): tx_count = _test_account_rpc( account.get_staking_transactions_count, local_test_address, "ALL", explorer_endpoint, ) - assert isinstance(tx_count, int) + assert isinstance( tx_count, int ) def test_errors(): - with pytest.raises(exceptions.RPCError): - account.get_balance("", fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_balance_by_block("", 1, fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_account_nonce("", 1, fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_transaction_count("", 1, fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_transactions_count("", 1, fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_transactions_count("", "ALL", fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_transaction_history("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_staking_transaction_history("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_balance_on_all_shards("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - account.get_total_balance("", endpoint=fake_shard) + with pytest.raises( exceptions.RPCError ): + account.get_balance( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_balance_by_block( "", 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_account_nonce( "", 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_transaction_count( "", 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_transactions_count( "", 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_transactions_count( "", "ALL", fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_transaction_history( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_staking_transaction_history( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_balance_on_all_shards( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + account.get_total_balance( "", endpoint = fake_shard ) diff --git a/tests/sdk-pyhmy/test_blockchain.py b/tests/sdk-pyhmy/test_blockchain.py index 327a97e..8601ef8 100644 --- a/tests/sdk-pyhmy/test_blockchain.py +++ b/tests/sdk-pyhmy/test_blockchain.py @@ -5,7 +5,6 @@ from pyhmy import blockchain from pyhmy.rpc import exceptions - test_epoch_number = 0 genesis_block_number = 0 test_block_number = 1 @@ -14,323 +13,357 @@ fake_shard = "http://example.com" address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" -def _test_blockchain_rpc(fn, *args, **kwargs): - if not callable(fn): - pytest.fail(f"Invalid function: {fn}") +def _test_blockchain_rpc( fn, *args, **kwargs ): + if not callable( fn ): + pytest.fail( f"Invalid function: {fn}" ) try: - response = fn(*args, **kwargs) + response = fn( *args, **kwargs ) except Exception as e: - if isinstance( - e, exceptions.RPCError - ) and "does not exist/is not available" in str(e): - pytest.skip(f"{str(e)}") - pytest.fail(f"Unexpected error: {e.__class__} {e}") + if isinstance( e, + exceptions.RPCError + ) and "does not exist/is not available" in str( e ): + pytest.skip( f"{str(e)}" ) + pytest.fail( f"Unexpected error: {e.__class__} {e}" ) return response -def test_get_node_metadata(setup_blockchain): - metadata = _test_blockchain_rpc(blockchain.get_node_metadata) - assert isinstance(metadata, dict) +def test_get_node_metadata( setup_blockchain ): + metadata = _test_blockchain_rpc( blockchain.get_node_metadata ) + assert isinstance( metadata, dict ) -def test_get_sharding_structure(setup_blockchain): - sharding_structure = _test_blockchain_rpc(blockchain.get_sharding_structure) - assert isinstance(sharding_structure, list) - assert len(sharding_structure) > 0 +def test_get_sharding_structure( setup_blockchain ): + sharding_structure = _test_blockchain_rpc( + blockchain.get_sharding_structure + ) + assert isinstance( sharding_structure, list ) + assert len( sharding_structure ) > 0 -def test_get_leader_address(setup_blockchain): - leader = _test_blockchain_rpc(blockchain.get_leader_address) - assert isinstance(leader, str) +def test_get_leader_address( setup_blockchain ): + leader = _test_blockchain_rpc( blockchain.get_leader_address ) + assert isinstance( leader, str ) assert "one1" in leader -def test_get_block_number(setup_blockchain): - current_block_number = _test_blockchain_rpc(blockchain.get_block_number) - assert isinstance(current_block_number, int) +def test_get_block_number( setup_blockchain ): + current_block_number = _test_blockchain_rpc( blockchain.get_block_number ) + assert isinstance( current_block_number, int ) -def test_get_current_epoch(setup_blockchain): - current_epoch = _test_blockchain_rpc(blockchain.get_current_epoch) - assert isinstance(current_epoch, int) +def test_get_current_epoch( setup_blockchain ): + current_epoch = _test_blockchain_rpc( blockchain.get_current_epoch ) + assert isinstance( current_epoch, int ) -def tset_get_gas_price(setup_blockchain): - gas = _test_blockchain_rpc(blockchain.get_gas_price) - assert isinstance(gas, int) +def tset_get_gas_price( setup_blockchain ): + gas = _test_blockchain_rpc( blockchain.get_gas_price ) + assert isinstance( gas, int ) -def test_get_num_peers(setup_blockchain): - peers = _test_blockchain_rpc(blockchain.get_num_peers) - assert isinstance(peers, int) +def test_get_num_peers( setup_blockchain ): + peers = _test_blockchain_rpc( blockchain.get_num_peers ) + assert isinstance( peers, int ) -def test_get_latest_header(setup_blockchain): - header = _test_blockchain_rpc(blockchain.get_latest_header) - assert isinstance(header, dict) +def test_get_latest_header( setup_blockchain ): + header = _test_blockchain_rpc( blockchain.get_latest_header ) + assert isinstance( header, dict ) -def test_get_latest_chain_headers(setup_blockchain): - header_pair = _test_blockchain_rpc(blockchain.get_latest_chain_headers) - assert isinstance(header_pair, dict) +def test_get_latest_chain_headers( setup_blockchain ): + header_pair = _test_blockchain_rpc( blockchain.get_latest_chain_headers ) + assert isinstance( header_pair, dict ) -def test_get_block_by_number(setup_blockchain): +def test_get_block_by_number( setup_blockchain ): global test_block_hash - block = _test_blockchain_rpc(blockchain.get_block_by_number, test_block_number) - assert isinstance(block, dict) + block = _test_blockchain_rpc( + blockchain.get_block_by_number, + test_block_number + ) + assert isinstance( block, dict ) assert "hash" in block.keys() - test_block_hash = block["hash"] + test_block_hash = block[ "hash" ] -def test_get_block_by_hash(setup_blockchain): +def test_get_block_by_hash( setup_blockchain ): if not test_block_hash: - pytest.skip("Failed to get reference block hash") - block = _test_blockchain_rpc(blockchain.get_block_by_hash, test_block_hash) - assert isinstance(block, dict) + pytest.skip( "Failed to get reference block hash" ) + block = _test_blockchain_rpc( + blockchain.get_block_by_hash, + test_block_hash + ) + assert isinstance( block, dict ) -def test_get_block_transaction_count_by_number(setup_blockchain): +def test_get_block_transaction_count_by_number( setup_blockchain ): tx_count = _test_blockchain_rpc( - blockchain.get_block_transaction_count_by_number, test_block_number + blockchain.get_block_transaction_count_by_number, + test_block_number ) - assert isinstance(tx_count, int) + assert isinstance( tx_count, int ) -def test_get_block_transaction_count_by_hash(setup_blockchain): +def test_get_block_transaction_count_by_hash( setup_blockchain ): if not test_block_hash: - pytest.skip("Failed to get reference block hash") + pytest.skip( "Failed to get reference block hash" ) tx_count = _test_blockchain_rpc( - blockchain.get_block_transaction_count_by_hash, test_block_hash + blockchain.get_block_transaction_count_by_hash, + test_block_hash ) - assert isinstance(tx_count, int) + assert isinstance( tx_count, int ) -def test_get_blocks(setup_blockchain): +def test_get_blocks( setup_blockchain ): blocks = _test_blockchain_rpc( - blockchain.get_blocks, genesis_block_number, test_block_number + blockchain.get_blocks, + genesis_block_number, + test_block_number ) - assert isinstance(blocks, list) - assert len(blocks) == (test_block_number - genesis_block_number + 1) + assert isinstance( blocks, list ) + assert len( blocks ) == ( test_block_number - genesis_block_number + 1 ) -def test_get_block_signers(setup_blockchain): +def test_get_block_signers( setup_blockchain ): block_signers = _test_blockchain_rpc( - blockchain.get_block_signers, test_block_number + blockchain.get_block_signers, + test_block_number ) - assert isinstance(block_signers, list) - assert len(block_signers) > 0 + assert isinstance( block_signers, list ) + assert len( block_signers ) > 0 -def test_get_validators(setup_blockchain): - validators = _test_blockchain_rpc(blockchain.get_validators, test_epoch_number) - assert isinstance(validators, dict) +def test_get_validators( setup_blockchain ): + validators = _test_blockchain_rpc( + blockchain.get_validators, + test_epoch_number + ) + assert isinstance( validators, dict ) assert "validators" in validators.keys() - assert len(validators["validators"]) > 0 + assert len( validators[ "validators" ] ) > 0 -def test_get_shard(setup_blockchain): - shard = _test_blockchain_rpc(blockchain.get_shard) - assert isinstance(shard, int) +def test_get_shard( setup_blockchain ): + shard = _test_blockchain_rpc( blockchain.get_shard ) + assert isinstance( shard, int ) assert shard == 0 -def test_get_staking_epoch(setup_blockchain): - staking_epoch = _test_blockchain_rpc(blockchain.get_staking_epoch) - assert isinstance(staking_epoch, int) +def test_get_staking_epoch( setup_blockchain ): + staking_epoch = _test_blockchain_rpc( blockchain.get_staking_epoch ) + assert isinstance( staking_epoch, int ) -def test_get_prestaking_epoch(setup_blockchain): - prestaking_epoch = _test_blockchain_rpc(blockchain.get_prestaking_epoch) - assert isinstance(prestaking_epoch, int) +def test_get_prestaking_epoch( setup_blockchain ): + prestaking_epoch = _test_blockchain_rpc( blockchain.get_prestaking_epoch ) + assert isinstance( prestaking_epoch, int ) -def test_get_bad_blocks(setup_blockchain): +def test_get_bad_blocks( setup_blockchain ): # TODO: Remove skip when RPC is fixed - pytest.skip("Known error with hmyv2_getCurrentBadBlocks") - bad_blocks = _test_blockchain_rpc(blockchain.get_bad_blocks) - assert isinstance(bad_blocks, list) + pytest.skip( "Known error with hmyv2_getCurrentBadBlocks" ) + bad_blocks = _test_blockchain_rpc( blockchain.get_bad_blocks ) + assert isinstance( bad_blocks, list ) -def test_get_validator_keys(setup_blockchain): - keys = _test_blockchain_rpc(blockchain.get_validator_keys, test_epoch_number) - assert isinstance(keys, list) - assert len(keys) > 0 +def test_get_validator_keys( setup_blockchain ): + keys = _test_blockchain_rpc( + blockchain.get_validator_keys, + test_epoch_number + ) + assert isinstance( keys, list ) + assert len( keys ) > 0 -def test_get_block_signers_keys(setup_blockchain): - keys = _test_blockchain_rpc(blockchain.get_block_signers_keys, test_block_number) - assert isinstance(keys, list) - assert len(keys) > 0 +def test_get_block_signers_keys( setup_blockchain ): + keys = _test_blockchain_rpc( + blockchain.get_block_signers_keys, + test_block_number + ) + assert isinstance( keys, list ) + assert len( keys ) > 0 -def test_chain_id(setup_blockchain): - chain_id = _test_blockchain_rpc(blockchain.chain_id) - assert isinstance(chain_id, int) +def test_chain_id( setup_blockchain ): + chain_id = _test_blockchain_rpc( blockchain.chain_id ) + assert isinstance( chain_id, int ) -def test_get_peer_info(setup_blockchain): - peer_info = _test_blockchain_rpc(blockchain.get_peer_info) - assert isinstance(peer_info, dict) +def test_get_peer_info( setup_blockchain ): + peer_info = _test_blockchain_rpc( blockchain.get_peer_info ) + assert isinstance( peer_info, dict ) -def test_protocol_version(setup_blockchain): - protocol_version = _test_blockchain_rpc(blockchain.protocol_version) - assert isinstance(protocol_version, int) +def test_protocol_version( setup_blockchain ): + protocol_version = _test_blockchain_rpc( blockchain.protocol_version ) + assert isinstance( protocol_version, int ) -def test_is_last_block(setup_blockchain): - is_last_block = _test_blockchain_rpc(blockchain.is_last_block, 0) - assert isinstance(is_last_block, bool) +def test_is_last_block( setup_blockchain ): + is_last_block = _test_blockchain_rpc( blockchain.is_last_block, 0 ) + assert isinstance( is_last_block, bool ) assert not is_last_block -def test_epoch_last_block(setup_blockchain): - epoch_last_block = _test_blockchain_rpc(blockchain.epoch_last_block, 0) - assert isinstance(epoch_last_block, int) +def test_epoch_last_block( setup_blockchain ): + epoch_last_block = _test_blockchain_rpc( blockchain.epoch_last_block, 0 ) + assert isinstance( epoch_last_block, int ) -def test_get_circulating_supply(setup_blockchain): - circulating_supply = _test_blockchain_rpc(blockchain.get_circulating_supply) - assert isinstance(circulating_supply, str) +def test_get_circulating_supply( setup_blockchain ): + circulating_supply = _test_blockchain_rpc( + blockchain.get_circulating_supply + ) + assert isinstance( circulating_supply, str ) -def test_get_total_supply(setup_blockchain): - total_supply = _test_blockchain_rpc(blockchain.get_total_supply) - assert isinstance(total_supply, str) or total_supply == None +def test_get_total_supply( setup_blockchain ): + total_supply = _test_blockchain_rpc( blockchain.get_total_supply ) + assert isinstance( total_supply, str ) or total_supply == None -def test_get_last_cross_links(setup_blockchain): - last_cross_links = _test_blockchain_rpc(blockchain.get_last_cross_links) - assert isinstance(last_cross_links, list) +def test_get_last_cross_links( setup_blockchain ): + last_cross_links = _test_blockchain_rpc( blockchain.get_last_cross_links ) + assert isinstance( last_cross_links, list ) -def test_get_gas_price(setup_blockchain): - gas_price = _test_blockchain_rpc(blockchain.get_gas_price) - assert isinstance(gas_price, int) +def test_get_gas_price( setup_blockchain ): + gas_price = _test_blockchain_rpc( blockchain.get_gas_price ) + assert isinstance( gas_price, int ) -def test_get_version(setup_blockchain): - version = _test_blockchain_rpc(blockchain.get_version) - assert isinstance(version, int) +def test_get_version( setup_blockchain ): + version = _test_blockchain_rpc( blockchain.get_version ) + assert isinstance( version, int ) -def test_get_header_by_number(setup_blockchain): - header_pair = _test_blockchain_rpc(blockchain.get_header_by_number, 0) - assert isinstance(header_pair, dict) +def test_get_header_by_number( setup_blockchain ): + header_pair = _test_blockchain_rpc( blockchain.get_header_by_number, 0 ) + assert isinstance( header_pair, dict ) -def test_get_block_staking_transaction_count_by_number(setup_blockchain): +def test_get_block_staking_transaction_count_by_number( setup_blockchain ): tx_count = _test_blockchain_rpc( - blockchain.get_block_staking_transaction_count_by_number, test_block_number + blockchain.get_block_staking_transaction_count_by_number, + test_block_number ) - assert isinstance(tx_count, int) + assert isinstance( tx_count, int ) -def test_get_block_staking_transaction_count_by_hash(setup_blockchain): +def test_get_block_staking_transaction_count_by_hash( setup_blockchain ): if not test_block_hash: - pytest.skip("Failed to get reference block hash") + pytest.skip( "Failed to get reference block hash" ) tx_count = _test_blockchain_rpc( - blockchain.get_block_staking_transaction_count_by_hash, test_block_hash + blockchain.get_block_staking_transaction_count_by_hash, + test_block_hash ) - assert isinstance(tx_count, int) + assert isinstance( tx_count, int ) -def test_is_block_signer(setup_blockchain): +def test_is_block_signer( setup_blockchain ): is_signer = _test_blockchain_rpc( - blockchain.is_block_signer, test_block_number, address + blockchain.is_block_signer, + test_block_number, + address ) - assert isinstance(is_signer, bool) + assert isinstance( is_signer, bool ) -def test_get_signed_blocks(setup_blockchain): - signed_blocks = _test_blockchain_rpc(blockchain.get_signed_blocks, address) - assert isinstance(signed_blocks, int) +def test_get_signed_blocks( setup_blockchain ): + signed_blocks = _test_blockchain_rpc( + blockchain.get_signed_blocks, + address + ) + assert isinstance( signed_blocks, int ) -def test_in_sync(setup_blockchain): - in_sync = _test_blockchain_rpc(blockchain.in_sync) - assert isinstance(in_sync, bool) +def test_in_sync( setup_blockchain ): + in_sync = _test_blockchain_rpc( blockchain.in_sync ) + assert isinstance( in_sync, bool ) -def test_beacon_in_sync(setup_blockchain): - beacon_in_sync = _test_blockchain_rpc(blockchain.beacon_in_sync) - assert isinstance(beacon_in_sync, bool) +def test_beacon_in_sync( setup_blockchain ): + beacon_in_sync = _test_blockchain_rpc( blockchain.beacon_in_sync ) + assert isinstance( beacon_in_sync, bool ) def test_errors(): - with pytest.raises(exceptions.RPCError): - blockchain.chain_id(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_node_metadata(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_peer_info(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.protocol_version(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_shard(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_staking_epoch(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_prestaking_epoch(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_sharding_structure(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_leader_address(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.is_last_block(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.epoch_last_block(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_circulating_supply(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_total_supply(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_number(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_current_epoch(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_last_cross_links(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_gas_price(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_num_peers(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_version(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_latest_header(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_header_by_number(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_latest_chain_headers(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_by_number(0, endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_by_hash("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_transaction_count_by_number(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_transaction_count_by_hash("", fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_staking_transaction_count_by_number(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_staking_transaction_count_by_hash("", fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_blocks(0, 1, endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_signers(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_block_signers_keys(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.is_block_signer(0, "", fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_signed_blocks("", fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_validators(1, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.get_validator_keys(0, fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.in_sync(fake_shard) - with pytest.raises(exceptions.RPCError): - blockchain.beacon_in_sync(fake_shard) + with pytest.raises( exceptions.RPCError ): + blockchain.chain_id( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_node_metadata( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_peer_info( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.protocol_version( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_shard( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_staking_epoch( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_prestaking_epoch( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_sharding_structure( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_leader_address( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.is_last_block( 0, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.epoch_last_block( 0, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_circulating_supply( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_total_supply( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_number( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_current_epoch( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_last_cross_links( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_gas_price( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_num_peers( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_version( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_latest_header( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_header_by_number( 0, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_latest_chain_headers( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_by_number( 0, endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_by_hash( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_transaction_count_by_number( 0, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_transaction_count_by_hash( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_staking_transaction_count_by_number( + 0, + fake_shard + ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_staking_transaction_count_by_hash( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_blocks( 0, 1, endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_signers( 0, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_block_signers_keys( 0, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.is_block_signer( 0, "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_signed_blocks( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_validators( 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.get_validator_keys( 0, fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.in_sync( fake_shard ) + with pytest.raises( exceptions.RPCError ): + blockchain.beacon_in_sync( fake_shard ) diff --git a/tests/sdk-pyhmy/test_contract.py b/tests/sdk-pyhmy/test_contract.py index 3739c5f..36b38fa 100644 --- a/tests/sdk-pyhmy/test_contract.py +++ b/tests/sdk-pyhmy/test_contract.py @@ -12,69 +12,75 @@ contract_address = None fake_shard = "http://example.com" -def _test_contract_rpc(fn, *args, **kwargs): - if not callable(fn): - pytest.fail(f"Invalid function: {fn}") +def _test_contract_rpc( fn, *args, **kwargs ): + if not callable( fn ): + pytest.fail( f"Invalid function: {fn}" ) try: - response = fn(*args, **kwargs) + response = fn( *args, **kwargs ) except Exception as e: - if isinstance( - e, exceptions.RPCError - ) and "does not exist/is not available" in str(e): - pytest.skip(f"{str(e)}") - elif isinstance(e, exceptions.RPCError) and "estimateGas returned" in str(e): - pytest.skip(f"{str(e)}") - pytest.fail(f"Unexpected error: {e.__class__} {e}") + if isinstance( e, + exceptions.RPCError + ) and "does not exist/is not available" in str( e ): + pytest.skip( f"{str(e)}" ) + elif isinstance( e, + exceptions.RPCError + ) and "estimateGas returned" in str( e ): + pytest.skip( f"{str(e)}" ) + pytest.fail( f"Unexpected error: {e.__class__} {e}" ) return response -def test_get_contract_address_from_hash(setup_blockchain): +def test_get_contract_address_from_hash( setup_blockchain ): global contract_address contract_address = _test_contract_rpc( - contract.get_contract_address_from_hash, contract_tx_hash + contract.get_contract_address_from_hash, + contract_tx_hash ) - assert isinstance(contract_address, str) + assert isinstance( contract_address, str ) -def test_call(setup_blockchain): +def test_call( setup_blockchain ): if not contract_address: - pytest.skip("Contract address not loaded yet") - called = _test_contract_rpc(contract.call, contract_address, "latest") - assert isinstance(called, str) and called.startswith("0x") + pytest.skip( "Contract address not loaded yet" ) + called = _test_contract_rpc( contract.call, contract_address, "latest" ) + assert isinstance( called, str ) and called.startswith( "0x" ) -def test_estimate_gas(setup_blockchain): +def test_estimate_gas( setup_blockchain ): if not contract_address: - pytest.skip("Contract address not loaded yet") - gas = _test_contract_rpc(contract.estimate_gas, contract_address) - assert isinstance(gas, int) + pytest.skip( "Contract address not loaded yet" ) + gas = _test_contract_rpc( contract.estimate_gas, contract_address ) + assert isinstance( gas, int ) -def test_get_code(setup_blockchain): +def test_get_code( setup_blockchain ): if not contract_address: - pytest.skip("Contract address not loaded yet") - code = _test_contract_rpc(contract.get_code, contract_address, "latest") + pytest.skip( "Contract address not loaded yet" ) + code = _test_contract_rpc( contract.get_code, contract_address, "latest" ) assert code == contract_code -def test_get_storage_at(setup_blockchain): +def test_get_storage_at( setup_blockchain ): if not contract_address: - pytest.skip("Contract address not loaded yet") + pytest.skip( "Contract address not loaded yet" ) storage = _test_contract_rpc( - contract.get_storage_at, contract_address, "0x0", "latest" + contract.get_storage_at, + contract_address, + "0x0", + "latest" ) - assert isinstance(storage, str) and storage.startswith("0x") + assert isinstance( storage, str ) and storage.startswith( "0x" ) def test_errors(): - with pytest.raises(exceptions.RPCError): - contract.get_contract_address_from_hash("", fake_shard) - with pytest.raises(exceptions.RPCError): - contract.call("", "", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - contract.estimate_gas("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - contract.get_code("", "latest", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - contract.get_storage_at("", 1, "latest", endpoint=fake_shard) + with pytest.raises( exceptions.RPCError ): + contract.get_contract_address_from_hash( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + contract.call( "", "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + contract.estimate_gas( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + contract.get_code( "", "latest", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + contract.get_storage_at( "", 1, "latest", endpoint = fake_shard ) diff --git a/tests/sdk-pyhmy/test_signing.py b/tests/sdk-pyhmy/test_signing.py index 7709e20..863fdd0 100644 --- a/tests/sdk-pyhmy/test_signing.py +++ b/tests/sdk-pyhmy/test_signing.py @@ -1,5 +1,4 @@ from pyhmy import signing - """ Test signature source (node.js) import { Transaction, RLPSign, TxStatus } from '@harmony-js/transaction'; @@ -43,8 +42,8 @@ def test_eth_transaction(): "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", ) assert ( - signed_tx.rawTransaction.hex() - == "0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1" + signed_tx.rawTransaction.hex() == + "0xf85d0201649414791697260e4c9a71f18484c9f997b308e5932505801ca0b364f4296bfd3231889d1b9ac94c68abbcb8ee6a6c7a5fa412ac82b5b7b0d5d1a02233864842ab28ee4f99c207940a867b0f8534ca362836190792816b48dde3b1" ) @@ -96,6 +95,6 @@ def test_hmy_transaction(): "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", ) assert ( - signed_tx.rawTransaction.hex() - == "0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d" + signed_tx.rawTransaction.hex() == + "0xf85f02016480019414791697260e4c9a71f18484c9f997b308e59325058026a02a203357ca6d7cdec981ad3d3692ad2c9e24536a9b6e7b486ce2f94f28c7563ea010d38cd0312a153af0aa7d8cd986040c36118bba373cb94e3e86fd4aedce904d" ) diff --git a/tests/sdk-pyhmy/test_staking.py b/tests/sdk-pyhmy/test_staking.py index 7fc0ed0..6a4fef7 100644 --- a/tests/sdk-pyhmy/test_staking.py +++ b/tests/sdk-pyhmy/test_staking.py @@ -5,222 +5,244 @@ from pyhmy import staking from pyhmy.rpc import exceptions - explorer_endpoint = "http://localhost:9700" test_validator_address = "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" fake_shard = "http://example.com" -def _test_staking_rpc(fn, *args, **kwargs): - if not callable(fn): - pytest.fail(f"Invalid function: {fn}") +def _test_staking_rpc( fn, *args, **kwargs ): + if not callable( fn ): + pytest.fail( f"Invalid function: {fn}" ) try: - response = fn(*args, **kwargs) + response = fn( *args, **kwargs ) except Exception as e: - if isinstance( - e, exceptions.RPCError - ) and "does not exist/is not available" in str(e): - pytest.skip(f"{str(e)}") - pytest.fail(f"Unexpected error: {e.__class__} {e}") + if isinstance( e, + exceptions.RPCError + ) and "does not exist/is not available" in str( e ): + pytest.skip( f"{str(e)}" ) + pytest.fail( f"Unexpected error: {e.__class__} {e}" ) return response -def test_get_all_validator_addresses(setup_blockchain): - validator_addresses = _test_staking_rpc(staking.get_all_validator_addresses) - assert isinstance(validator_addresses, list) - assert len(validator_addresses) > 0 +def test_get_all_validator_addresses( setup_blockchain ): + validator_addresses = _test_staking_rpc( + staking.get_all_validator_addresses + ) + assert isinstance( validator_addresses, list ) + assert len( validator_addresses ) > 0 assert test_validator_address in validator_addresses -def test_get_validator_information(setup_blockchain): - info = _test_staking_rpc(staking.get_validator_information, test_validator_address) - assert isinstance(info, dict) +def test_get_validator_information( setup_blockchain ): + info = _test_staking_rpc( + staking.get_validator_information, + test_validator_address + ) + assert isinstance( info, dict ) -def test_get_all_validator_information(setup_blockchain): - all_validator_information = _test_staking_rpc(staking.get_all_validator_information) - assert isinstance(all_validator_information, list) - assert len(all_validator_information) > 0 +def test_get_all_validator_information( setup_blockchain ): + all_validator_information = _test_staking_rpc( + staking.get_all_validator_information + ) + assert isinstance( all_validator_information, list ) + assert len( all_validator_information ) > 0 -def test_get_delegations_by_delegator(setup_blockchain): +def test_get_delegations_by_delegator( setup_blockchain ): delegations = _test_staking_rpc( - staking.get_delegations_by_delegator, test_validator_address + staking.get_delegations_by_delegator, + test_validator_address ) - assert isinstance(delegations, list) - assert len(delegations) > 0 + assert isinstance( delegations, list ) + assert len( delegations ) > 0 -def test_get_delegations_by_validator(setup_blockchain): +def test_get_delegations_by_validator( setup_blockchain ): delegations = _test_staking_rpc( - staking.get_delegations_by_validator, test_validator_address + staking.get_delegations_by_validator, + test_validator_address ) - assert isinstance(delegations, list) - assert len(delegations) > 0 + assert isinstance( delegations, list ) + assert len( delegations ) > 0 -def test_get_current_utility_metrics(setup_blockchain): - metrics = _test_staking_rpc(staking.get_current_utility_metrics) - assert isinstance(metrics, dict) +def test_get_current_utility_metrics( setup_blockchain ): + metrics = _test_staking_rpc( staking.get_current_utility_metrics ) + assert isinstance( metrics, dict ) -def test_get_staking_network_info(setup_blockchain): - info = _test_staking_rpc(staking.get_staking_network_info) - assert isinstance(info, dict) +def test_get_staking_network_info( setup_blockchain ): + info = _test_staking_rpc( staking.get_staking_network_info ) + assert isinstance( info, dict ) -def test_get_super_committees(setup_blockchain): - committee = _test_staking_rpc(staking.get_super_committees) - assert isinstance(committee, dict) +def test_get_super_committees( setup_blockchain ): + committee = _test_staking_rpc( staking.get_super_committees ) + assert isinstance( committee, dict ) -def test_get_raw_median_stake_snapshot(setup_blockchain): - median_stake = _test_staking_rpc(staking.get_raw_median_stake_snapshot) - assert isinstance(median_stake, dict) +def test_get_raw_median_stake_snapshot( setup_blockchain ): + median_stake = _test_staking_rpc( staking.get_raw_median_stake_snapshot ) + assert isinstance( median_stake, dict ) -def test_get_validator_information_by_block(setup_blockchain): +def test_get_validator_information_by_block( setup_blockchain ): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block info = _test_staking_rpc( staking.get_validator_information_by_block_number, test_validator_address, "latest", - endpoint=explorer_endpoint, + endpoint = explorer_endpoint, ) - assert isinstance(info, dict) + assert isinstance( info, dict ) -def test_get_validator_information_by_block(setup_blockchain): +def test_get_validator_information_by_block( setup_blockchain ): # Apparently validator information not created until block after create-validator transaction is accepted, so +1 block info = _test_staking_rpc( staking.get_all_validator_information_by_block_number, "latest", - endpoint=explorer_endpoint, + endpoint = explorer_endpoint, ) - assert isinstance(info, list) + assert isinstance( info, list ) -def test_get_delegations_by_delegator_by_block(setup_blockchain): +def test_get_delegations_by_delegator_by_block( setup_blockchain ): delegations = _test_staking_rpc( staking.get_delegations_by_delegator_by_block_number, test_validator_address, "latest", - endpoint=explorer_endpoint, + endpoint = explorer_endpoint, ) - assert isinstance(delegations, list) + assert isinstance( delegations, list ) -def test_get_elected_validator_addresses(setup_blockchain): - validator_addresses = _test_staking_rpc(staking.get_elected_validator_addresses) - assert isinstance(validator_addresses, list) - assert len(validator_addresses) > 0 +def test_get_elected_validator_addresses( setup_blockchain ): + validator_addresses = _test_staking_rpc( + staking.get_elected_validator_addresses + ) + assert isinstance( validator_addresses, list ) + assert len( validator_addresses ) > 0 -def test_get_validators(setup_blockchain): - validators = _test_staking_rpc(staking.get_validators, 2) - assert isinstance(validators, dict) - assert len(validators["validators"]) > 0 +def test_get_validators( setup_blockchain ): + validators = _test_staking_rpc( staking.get_validators, 2 ) + assert isinstance( validators, dict ) + assert len( validators[ "validators" ] ) > 0 -def test_get_validator_keys(setup_blockchain): - validators = _test_staking_rpc(staking.get_validator_keys, 2) - assert isinstance(validators, list) +def test_get_validator_keys( setup_blockchain ): + validators = _test_staking_rpc( staking.get_validator_keys, 2 ) + assert isinstance( validators, list ) -def test_get_validator_self_delegation(setup_blockchain): +def test_get_validator_self_delegation( setup_blockchain ): self_delegation = _test_staking_rpc( - staking.get_validator_self_delegation, test_validator_address + staking.get_validator_self_delegation, + test_validator_address ) - assert isinstance(self_delegation, int) + assert isinstance( self_delegation, int ) assert self_delegation > 0 -def test_get_validator_total_delegation(setup_blockchain): +def test_get_validator_total_delegation( setup_blockchain ): total_delegation = _test_staking_rpc( - staking.get_validator_total_delegation, test_validator_address + staking.get_validator_total_delegation, + test_validator_address ) - assert isinstance(total_delegation, int) + assert isinstance( total_delegation, int ) assert total_delegation > 0 -def test_get_all_delegation_information(setup_blockchain): +def test_get_all_delegation_information( setup_blockchain ): delegation_information = _test_staking_rpc( - staking.get_all_delegation_information, 0 + staking.get_all_delegation_information, + 0 ) - assert isinstance(delegation_information, list) - assert len(delegation_information) > 0 + assert isinstance( delegation_information, list ) + assert len( delegation_information ) > 0 -def test_get_delegation_by_delegator_and_validator(setup_blockchain): +def test_get_delegation_by_delegator_and_validator( setup_blockchain ): delegation_information = _test_staking_rpc( staking.get_delegation_by_delegator_and_validator, test_validator_address, test_validator_address, ) - assert isinstance(delegation_information, dict) + assert isinstance( delegation_information, dict ) -def test_get_available_redelegation_balance(setup_blockchain): +def test_get_available_redelegation_balance( setup_blockchain ): redelgation_balance = _test_staking_rpc( - staking.get_available_redelegation_balance, test_validator_address + staking.get_available_redelegation_balance, + test_validator_address ) - assert isinstance(redelgation_balance, int) + assert isinstance( redelgation_balance, int ) assert redelgation_balance == 0 -def test_get_total_staking(setup_blockchain): - total_staking = _test_staking_rpc(staking.get_total_staking) - assert isinstance(total_staking, int) +def test_get_total_staking( setup_blockchain ): + total_staking = _test_staking_rpc( staking.get_total_staking ) + assert isinstance( total_staking, int ) if ( - staking.get_validator_information(test_validator_address, explorer_endpoint)[ - "active-status" - ] - == "active" + staking.get_validator_information( + test_validator_address, + explorer_endpoint + )[ "active-status" ] == "active" ): assert total_staking > 0 def test_errors(): - with pytest.raises(exceptions.RPCError): - staking.get_all_validator_addresses(fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_validator_information("", fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_elected_validator_addresses(fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_validators(1, fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_validator_keys(1, fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_validator_information_by_block_number("", 1, fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_all_validator_information(-1, fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_validator_self_delegation("", fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_validator_total_delegation("", fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_all_validator_information_by_block_number(1, 1, fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_all_delegation_information(1, fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_delegator("", fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_delegator_by_block_number("", 1, fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_delegation_by_delegator_and_validator("", "", fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_available_redelegation_balance("", fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_delegations_by_validator("", fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_current_utility_metrics(fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_staking_network_info(fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_super_committees(fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_total_staking(fake_shard) - with pytest.raises(exceptions.RPCError): - staking.get_raw_median_stake_snapshot(fake_shard) + with pytest.raises( exceptions.RPCError ): + staking.get_all_validator_addresses( fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_validator_information( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_elected_validator_addresses( fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_validators( 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_validator_keys( 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_validator_information_by_block_number( "", 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_all_validator_information( -1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_validator_self_delegation( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_validator_total_delegation( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_all_validator_information_by_block_number( + 1, + 1, + fake_shard + ) + with pytest.raises( exceptions.RPCError ): + staking.get_all_delegation_information( 1, fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_delegations_by_delegator( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_delegations_by_delegator_by_block_number( + "", + 1, + fake_shard + ) + with pytest.raises( exceptions.RPCError ): + staking.get_delegation_by_delegator_and_validator( "", "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_available_redelegation_balance( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_delegations_by_validator( "", fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_current_utility_metrics( fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_staking_network_info( fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_super_committees( fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_total_staking( fake_shard ) + with pytest.raises( exceptions.RPCError ): + staking.get_raw_median_stake_snapshot( fake_shard ) diff --git a/tests/sdk-pyhmy/test_staking_signing.py b/tests/sdk-pyhmy/test_staking_signing.py index b947cbb..ede7aa6 100644 --- a/tests/sdk-pyhmy/test_staking_signing.py +++ b/tests/sdk-pyhmy/test_staking_signing.py @@ -5,7 +5,6 @@ from pyhmy.numbers import convert_one_to_atto # other transactions (create/edit validator) are in test_validator.py # test_delegate is the same as test_undelegate (except the directive) so it has been omitted # staking transactions without a chain id have been omitted as well, since the node does not accept them anyway - """ let stakingTx let stakeMsg3: CollectRewards = new CollectRewards( @@ -33,7 +32,6 @@ console.log(signed) # } # signed_tx = staking_signing.sign_staking_transaction(transaction_dict, '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48') # assert signed_tx.rawTransaction.hex() == '0xf85a04d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5823a0490e4ceb747563ba40da3e0db8a65133cf6f6ae4c48a24866cd6aa1f0d6c2414a06dbd51a67b35b5685e7b7420cba26e63b0e7d3c696fc6cb69d48e54fcad280e9' - """ let stakingTx let stakeMsg3: CollectRewards = new CollectRewards( @@ -67,8 +65,8 @@ def test_collect_rewards_chain_id(): "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", ) assert ( - signed_tx.rawTransaction.hex() - == "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099" + signed_tx.rawTransaction.hex() == + "0xf86504d594ebcd16e8c1d8f493ba04e99a56474122d81a9c5802880de0b6b3a76400006425a055d6c3c0d8e7a1e75152db361a2ed47f5ab54f6f19b0d8e549953dbdf13ba647a076e1367dfca38eae3bd0e8da296335acabbaeb87dc17e47ebe4942db29334099" ) @@ -100,7 +98,7 @@ def test_delegate(): "validatorAddress": "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9", "amount": 5, "nonce": 2, - "gasPrice": int(convert_one_to_atto(1)), + "gasPrice": int( convert_one_to_atto( 1 ) ), "gasLimit": 100, "chainId": 2, } @@ -109,6 +107,6 @@ def test_delegate(): "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", ) assert ( - signed_tx.rawTransaction.hex() - == "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6" + signed_tx.rawTransaction.hex() == + "0xf87b02eb94ebcd16e8c1d8f493ba04e99a56474122d81a9c5894ebcd16e8c1d8f493ba04e99a56474122d81a9c580502880de0b6b3a76400006428a0c856fd483a989ca4db4b5257f6996729527828fb21ec13cc65f0bffe6c015ab1a05e9d3c92742e8cb7450bebdfb7ad277ccbfc9fa0719db0b12a715a0a173cadd6" ) diff --git a/tests/sdk-pyhmy/test_transaction.py b/tests/sdk-pyhmy/test_transaction.py index 3194e5d..d00bc66 100644 --- a/tests/sdk-pyhmy/test_transaction.py +++ b/tests/sdk-pyhmy/test_transaction.py @@ -4,7 +4,6 @@ from pyhmy import transaction from pyhmy.rpc import exceptions - endpoint = "http://localhost:9500" endpoint_shard_one = "http://localhost:9502" fake_shard = "http://example.com" @@ -30,212 +29,238 @@ raw_stx = "0xf88302f494c9c6d47ee5f2e3e08d7367ad1a1373ba9dd1724194a5241513da9f446 raw_stx_hash = "0xe7d07ef6d9fca595a14ceb0ca917bece7bedb15efe662300e9334a32ac1da629" -def _test_transaction_rpc(fn, *args, **kwargs): - if not callable(fn): - pytest.fail(f"Invalid function: {fn}") +def _test_transaction_rpc( fn, *args, **kwargs ): + if not callable( fn ): + pytest.fail( f"Invalid function: {fn}" ) try: - response = fn(*args, **kwargs) + response = fn( *args, **kwargs ) except Exception as e: - if isinstance( - e, exceptions.RPCError - ) and "does not exist/is not available" in str(e): - pytest.skip(f"{str(e)}") - pytest.fail(f"Unexpected error: {e.__class__} {e}") + if isinstance( e, + exceptions.RPCError + ) and "does not exist/is not available" in str( e ): + pytest.skip( f"{str(e)}" ) + pytest.fail( f"Unexpected error: {e.__class__} {e}" ) return response -def test_get_pending_transactions(setup_blockchain): - pool = _test_transaction_rpc(transaction.get_pending_transactions) - assert isinstance(pool, list) +def test_get_pending_transactions( setup_blockchain ): + pool = _test_transaction_rpc( transaction.get_pending_transactions ) + assert isinstance( pool, list ) -def test_get_transaction_by_hash(setup_blockchain): +def test_get_transaction_by_hash( setup_blockchain ): tx = _test_transaction_rpc( - transaction.get_transaction_by_hash, tx_hash, endpoint=endpoint + transaction.get_transaction_by_hash, + tx_hash, + endpoint = endpoint ) assert tx - assert isinstance(tx, dict) + assert isinstance( tx, dict ) assert "blockNumber" in tx.keys() assert "blockHash" in tx.keys() global tx_block_num - tx_block_num = int(tx["blockNumber"]) + tx_block_num = int( tx[ "blockNumber" ] ) global tx_block_hash - tx_block_hash = tx["blockHash"] + tx_block_hash = tx[ "blockHash" ] global tx_index - tx_index = int(tx["transactionIndex"]) + tx_index = int( tx[ "transactionIndex" ] ) -def test_get_transaction_by_block_hash_and_index(setup_blockchain): +def test_get_transaction_by_block_hash_and_index( setup_blockchain ): if not tx_block_hash: - pytest.skip("Failed to get reference block hash") + pytest.skip( "Failed to get reference block hash" ) tx = _test_transaction_rpc( transaction.get_transaction_by_block_hash_and_index, tx_block_hash, tx_index, - endpoint=endpoint, + endpoint = endpoint, ) assert tx - assert isinstance(tx, dict) + assert isinstance( tx, dict ) -def test_get_transaction_by_block_number_and_index(setup_blockchain): +def test_get_transaction_by_block_number_and_index( setup_blockchain ): if not tx_block_num: - pytest.skip("Failed to get reference block num") + pytest.skip( "Failed to get reference block num" ) tx = _test_transaction_rpc( transaction.get_transaction_by_block_number_and_index, tx_block_num, tx_index, - endpoint=endpoint, + endpoint = endpoint, ) assert tx - assert isinstance(tx, dict) + assert isinstance( tx, dict ) -def test_get_transaction_receipt(setup_blockchain): +def test_get_transaction_receipt( setup_blockchain ): tx_receipt = _test_transaction_rpc( - transaction.get_transaction_receipt, tx_hash, endpoint=endpoint + transaction.get_transaction_receipt, + tx_hash, + endpoint = endpoint ) assert tx_receipt - assert isinstance(tx_receipt, dict) + assert isinstance( tx_receipt, dict ) -def test_get_transaction_error_sink(setup_blockchain): - errors = _test_transaction_rpc(transaction.get_transaction_error_sink) - assert isinstance(errors, list) +def test_get_transaction_error_sink( setup_blockchain ): + errors = _test_transaction_rpc( transaction.get_transaction_error_sink ) + assert isinstance( errors, list ) -def test_send_and_confirm_raw_transaction(setup_blockchain): +def test_send_and_confirm_raw_transaction( setup_blockchain ): # Note: this test is not yet idempotent since the localnet will reject transactions which were previously finalized. test_tx = _test_transaction_rpc( - transaction.send_and_confirm_raw_transaction, raw_tx + transaction.send_and_confirm_raw_transaction, + raw_tx ) - assert isinstance(test_tx, dict) - assert test_tx["hash"] == raw_tx_hash + assert isinstance( test_tx, dict ) + assert test_tx[ "hash" ] == raw_tx_hash -def test_get_pending_cx_receipts(setup_blockchain): - pending = _test_transaction_rpc(transaction.get_pending_cx_receipts) - assert isinstance(pending, list) +def test_get_pending_cx_receipts( setup_blockchain ): + pending = _test_transaction_rpc( transaction.get_pending_cx_receipts ) + assert isinstance( pending, list ) -def test_get_cx_receipt_by_hash(setup_blockchain): +def test_get_cx_receipt_by_hash( setup_blockchain ): cx = _test_transaction_rpc( - transaction.get_cx_receipt_by_hash, cx_hash, endpoint_shard_one + transaction.get_cx_receipt_by_hash, + cx_hash, + endpoint_shard_one ) assert cx - assert isinstance(cx, dict) + assert isinstance( cx, dict ) -def test_resend_cx_receipt(setup_blockchain): - sent = _test_transaction_rpc(transaction.resend_cx_receipt, cx_hash) - assert isinstance(sent, bool) +def test_resend_cx_receipt( setup_blockchain ): + sent = _test_transaction_rpc( transaction.resend_cx_receipt, cx_hash ) + assert isinstance( sent, bool ) assert sent -def test_get_staking_transaction_by_hash(setup_blockchain): +def test_get_staking_transaction_by_hash( setup_blockchain ): staking_tx = _test_transaction_rpc( - transaction.get_staking_transaction_by_hash, stx_hash + transaction.get_staking_transaction_by_hash, + stx_hash ) assert staking_tx - assert isinstance(staking_tx, dict) + assert isinstance( staking_tx, dict ) assert "blockNumber" in staking_tx.keys() assert "blockHash" in staking_tx.keys() global stx_block_num - stx_block_num = int(staking_tx["blockNumber"]) + stx_block_num = int( staking_tx[ "blockNumber" ] ) global stx_block_hash - stx_block_hash = staking_tx["blockHash"] + stx_block_hash = staking_tx[ "blockHash" ] global stx_index - stx_index = int(staking_tx["transactionIndex"]) + stx_index = int( staking_tx[ "transactionIndex" ] ) -def test_get_transaction_by_block_hash_and_index(setup_blockchain): +def test_get_transaction_by_block_hash_and_index( setup_blockchain ): if not stx_block_hash: - pytest.skip("Failed to get reference block hash") + pytest.skip( "Failed to get reference block hash" ) stx = _test_transaction_rpc( transaction.get_staking_transaction_by_block_hash_and_index, stx_block_hash, stx_index, ) assert stx - assert isinstance(stx, dict) + assert isinstance( stx, dict ) -def test_get_transaction_by_block_number_and_index(setup_blockchain): +def test_get_transaction_by_block_number_and_index( setup_blockchain ): if not stx_block_num: - pytest.skip("Failed to get reference block num") + pytest.skip( "Failed to get reference block num" ) stx = _test_transaction_rpc( transaction.get_staking_transaction_by_block_number_and_index, stx_block_num, stx_index, ) assert stx - assert isinstance(stx, dict) + assert isinstance( stx, dict ) -def test_get_staking_transaction_error_sink(setup_blockchain): - errors = _test_transaction_rpc(transaction.get_staking_transaction_error_sink) - assert isinstance(errors, list) +def test_get_staking_transaction_error_sink( setup_blockchain ): + errors = _test_transaction_rpc( + transaction.get_staking_transaction_error_sink + ) + assert isinstance( errors, list ) -def test_send_raw_staking_transaction(setup_blockchain): +def test_send_raw_staking_transaction( setup_blockchain ): test_stx = _test_transaction_rpc( - transaction.send_and_confirm_raw_staking_transaction, raw_stx, endpoint=endpoint + transaction.send_and_confirm_raw_staking_transaction, + raw_stx, + endpoint = endpoint ) - assert isinstance(test_stx, dict) - assert test_stx["hash"] == raw_stx_hash + assert isinstance( test_stx, dict ) + assert test_stx[ "hash" ] == raw_stx_hash -def test_get_pool_stats(setup_blockchain): +def test_get_pool_stats( setup_blockchain ): test_pool_stats = _test_transaction_rpc( - transaction.get_pool_stats, endpoint=endpoint + transaction.get_pool_stats, + endpoint = endpoint ) - assert isinstance(test_pool_stats, dict) + assert isinstance( test_pool_stats, dict ) -def test_get_pending_staking_transactions(setup_blockchain): +def test_get_pending_staking_transactions( setup_blockchain ): pending_staking_transactions = _test_transaction_rpc( - transaction.get_pending_staking_transactions, endpoint=endpoint + transaction.get_pending_staking_transactions, + endpoint = endpoint ) - assert isinstance(pending_staking_transactions, list) + assert isinstance( pending_staking_transactions, list ) def test_errors(): - with pytest.raises(exceptions.RPCError): - transaction.get_pending_transactions(fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_transaction_error_sink(fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_pool_stats(fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_transaction_by_hash("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_transaction_by_block_hash_and_index("", 1, endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_transaction_by_block_number_and_index(1, 1, endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_transaction_receipt("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.send_raw_transaction("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_pending_cx_receipts(fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_cx_receipt_by_hash("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.resend_cx_receipt("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_by_hash("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): + with pytest.raises( exceptions.RPCError ): + transaction.get_pending_transactions( fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_transaction_error_sink( fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_pool_stats( fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_transaction_by_hash( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_transaction_by_block_hash_and_index( + "", + 1, + endpoint = fake_shard + ) + with pytest.raises( exceptions.RPCError ): + transaction.get_transaction_by_block_number_and_index( + 1, + 1, + endpoint = fake_shard + ) + with pytest.raises( exceptions.RPCError ): + transaction.get_transaction_receipt( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.send_raw_transaction( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_pending_cx_receipts( fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_cx_receipt_by_hash( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.resend_cx_receipt( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_staking_transaction_by_hash( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): transaction.get_staking_transaction_by_block_hash_and_index( - "", 1, endpoint=fake_shard + "", + 1, + endpoint = fake_shard ) - with pytest.raises(exceptions.RPCError): + with pytest.raises( exceptions.RPCError ): transaction.get_staking_transaction_by_block_number_and_index( - 1, 1, endpoint=fake_shard + 1, + 1, + endpoint = fake_shard ) - with pytest.raises(exceptions.RPCError): - transaction.get_staking_transaction_error_sink(endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.send_raw_staking_transaction("", endpoint=fake_shard) - with pytest.raises(exceptions.RPCError): - transaction.get_pending_staking_transactions(endpoint=fake_shard) + with pytest.raises( exceptions.RPCError ): + transaction.get_staking_transaction_error_sink( endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.send_raw_staking_transaction( "", endpoint = fake_shard ) + with pytest.raises( exceptions.RPCError ): + transaction.get_pending_staking_transactions( endpoint = fake_shard ) diff --git a/tests/sdk-pyhmy/test_validator.py b/tests/sdk-pyhmy/test_validator.py index 21f33b7..f99eefc 100644 --- a/tests/sdk-pyhmy/test_validator.py +++ b/tests/sdk-pyhmy/test_validator.py @@ -14,25 +14,25 @@ test_validator_object = None test_validator_loaded = False -def test_instantiate_validator(setup_blockchain): +def test_instantiate_validator( setup_blockchain ): global test_validator_object test_validator_object = validator.Validator( "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" ) - assert isinstance(test_validator_object, validator.Validator) + assert isinstance( test_validator_object, validator.Validator ) -def test_load_validator(setup_blockchain): +def test_load_validator( setup_blockchain ): if not test_validator_object: - pytest.skip("Validator not instantiated yet") + pytest.skip( "Validator not instantiated yet" ) info = { "name": "Alice", "identity": "alice", "website": "alice.harmony.one", "details": "Don't mess with me!!!", "security-contact": "Bob", - "min-self-delegation": convert_one_to_atto(10000), - "amount": convert_one_to_atto(10001), + "min-self-delegation": convert_one_to_atto( 10000 ), + "amount": convert_one_to_atto( 10001 ), "max-rate": "0.9", "max-change-rate": "0.05", "rate": "0.01", @@ -42,9 +42,9 @@ def test_load_validator(setup_blockchain): "bls-key-sigs": [ "0x68f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b7062414" ], - "max-total-delegation": convert_one_to_atto(40000), + "max-total-delegation": convert_one_to_atto( 40000 ), } - test_validator_object.load(info) + test_validator_object.load( info ) global test_validator_loaded test_validator_loaded = True @@ -78,19 +78,19 @@ console.log(signed) """ -def test_create_validator_sign(setup_blockchain): - if not (test_validator_object or test_validator_loaded): - pytest.skip("Validator not instantiated yet") +def test_create_validator_sign( setup_blockchain ): + if not ( test_validator_object or test_validator_loaded ): + pytest.skip( "Validator not instantiated yet" ) signed_hash = test_validator_object.sign_create_validator_transaction( 2, - int(convert_one_to_atto(1)), + int( convert_one_to_atto( 1 ) ), 100, "4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48", 2, ).rawTransaction.hex() assert ( - signed_hash - == "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd" + signed_hash == + "0xf9017580f9012394ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121dcc8872386f26fc10000c9880c7d713b49da0000c887b1a2bc2ec500008a021e19e0c9bab24000008a0878678326eac9000000f1b030b2c38b1316da91e068ac3bd8751c0901ef6c02a1d58bc712104918302c6ed03d5894671d0c816dad2b4d303320f202f862b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b70624148a021e27c1806e59a4000002880de0b6b3a76400006428a0c6c7e62f02331df0afd4699ec514a2fc4548c920d77ad74d98caeec8c924c09aa02b27b999a724b1d341d6bbb0e877611d0047542cb7e380f9a6a272d204b450cd" ) @@ -137,9 +137,9 @@ console.log(signed) """ -def test_edit_validator_sign(setup_blockchain): - if not (test_validator_object or test_validator_loaded): - pytest.skip("Validator not instantiated yet") +def test_edit_validator_sign( setup_blockchain ): + if not ( test_validator_object or test_validator_loaded ): + pytest.skip( "Validator not instantiated yet" ) signed_hash = test_validator_object.sign_edit_validator_transaction( 2, int(convert_one_to_atto(1)), @@ -152,67 +152,73 @@ def test_edit_validator_sign(setup_blockchain): 2, ).rawTransaction.hex() assert ( - signed_hash - == "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995" + signed_hash == + "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995" ) -def test_invalid_validator(setup_blockchain): - if not (test_validator_object or test_validator_loaded): - pytest.skip("Validator not instantiated yet") - with pytest.raises(InvalidValidatorError): +def test_invalid_validator( setup_blockchain ): + if not ( test_validator_object or test_validator_loaded ): + pytest.skip( "Validator not instantiated yet" ) + with pytest.raises( InvalidValidatorError ): info = { "name": "Alice", } - test_validator_object.load(info) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_name("a" * 141) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_identity("a" * 141) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_website("a" * 141) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_security_contact("a" * 141) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_details("a" * 281) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_min_self_delegation(1) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_max_total_delegation(1) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_amount(1) - with pytest.raises(InvalidValidatorError): - test_validator_object.set_max_rate("2.0") - with pytest.raises(InvalidValidatorError): - test_validator_object.set_max_change_rate("-2.0") - with pytest.raises(InvalidValidatorError): - test_validator_object.set_rate("-2.0") - - -def test_validator_getters(setup_blockchain): - if not (test_validator_object or test_validator_loaded): - pytest.skip("Validator not instantiated yet") + test_validator_object.load( info ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_name( "a" * 141 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_identity( "a" * 141 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_website( "a" * 141 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_security_contact( "a" * 141 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_details( "a" * 281 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_min_self_delegation( 1 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_max_total_delegation( 1 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_amount( 1 ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_max_rate( "2.0" ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_max_change_rate( "-2.0" ) + with pytest.raises( InvalidValidatorError ): + test_validator_object.set_rate( "-2.0" ) + + +def test_validator_getters( setup_blockchain ): + if not ( test_validator_object or test_validator_loaded ): + pytest.skip( "Validator not instantiated yet" ) assert ( - test_validator_object.get_address() - == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" + test_validator_object.get_address() == + "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" ) - assert test_validator_object.add_bls_key("5") - assert test_validator_object.remove_bls_key("5") + assert test_validator_object.add_bls_key( "5" ) + assert test_validator_object.remove_bls_key( "5" ) assert test_validator_object.get_name() == "Alice" assert test_validator_object.get_identity() == "alice" assert test_validator_object.get_website() == "alice.harmony.one" assert test_validator_object.get_security_contact() == "Bob" assert test_validator_object.get_details() == "Don't mess with me!!!" - assert isinstance(test_validator_object.get_min_self_delegation(), Decimal) - assert isinstance(test_validator_object.get_max_total_delegation(), Decimal) - assert isinstance(test_validator_object.get_amount(), Decimal) - assert isinstance(test_validator_object.get_max_rate(), Decimal) - assert isinstance(test_validator_object.get_max_change_rate(), Decimal) - assert isinstance(test_validator_object.get_rate(), Decimal) - assert len(test_validator_object.get_bls_keys()) > 0 + assert isinstance( + test_validator_object.get_min_self_delegation(), + Decimal + ) + assert isinstance( + test_validator_object.get_max_total_delegation(), + Decimal + ) + assert isinstance( test_validator_object.get_amount(), Decimal ) + assert isinstance( test_validator_object.get_max_rate(), Decimal ) + assert isinstance( test_validator_object.get_max_change_rate(), Decimal ) + assert isinstance( test_validator_object.get_rate(), Decimal ) + assert len( test_validator_object.get_bls_keys() ) > 0 -def test_validator_load_from_blockchain(setup_blockchain): +def test_validator_load_from_blockchain( setup_blockchain ): test_validator_object2 = validator.Validator( "one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3" ) diff --git a/tests/util-pyhmy/test_util.py b/tests/util-pyhmy/test_util.py index 2988c47..adb7273 100644 --- a/tests/util-pyhmy/test_util.py +++ b/tests/util-pyhmy/test_util.py @@ -12,59 +12,64 @@ from pyhmy import util TEMP_DIR = "/tmp/pyhmy-testing/test-util" -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture( scope = "session", autouse = True ) def setup(): - shutil.rmtree(TEMP_DIR, ignore_errors=True) - os.makedirs(TEMP_DIR, exist_ok=True) + shutil.rmtree( TEMP_DIR, ignore_errors = True ) + os.makedirs( TEMP_DIR, exist_ok = True ) def test_json_load(): - dec = util.json_load("1.1", parse_float=decimal.Decimal) - assert isinstance(dec, decimal.Decimal) - assert float(dec) == 1.1 - ref_dict = {"test": "val", "arr": [1, 2, 3]} - loaded_dict = util.json_load(json.dumps(ref_dict)) - assert str(ref_dict) == str(loaded_dict) + dec = util.json_load( "1.1", parse_float = decimal.Decimal ) + assert isinstance( dec, decimal.Decimal ) + assert float( dec ) == 1.1 + ref_dict = { + "test": "val", + "arr": [ 1, + 2, + 3 ] + } + loaded_dict = util.json_load( json.dumps( ref_dict ) ) + assert str( ref_dict ) == str( loaded_dict ) def test_chain_id_to_int(): - assert util.chain_id_to_int(2) == 2 - assert util.chain_id_to_int("HmyMainnet") == 1 + assert util.chain_id_to_int( 2 ) == 2 + assert util.chain_id_to_int( "HmyMainnet" ) == 1 def test_get_gopath(): - assert isinstance(util.get_gopath(), str) + assert isinstance( util.get_gopath(), str ) def test_get_goversion(): - assert isinstance(util.get_goversion(), str) + assert isinstance( util.get_goversion(), str ) def test_convert_one_to_hex(): assert ( - util.convert_one_to_hex("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58") + util.convert_one_to_hex( "0xebcd16e8c1d8f493ba04e99a56474122d81a9c58" ) == "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58" ) assert ( - util.convert_one_to_hex("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") + util.convert_one_to_hex( "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" ) == "0xeBCD16e8c1D8f493bA04E99a56474122D81A9c58" ) def test_convert_hex_to_one(): assert ( - util.convert_hex_to_one("0xebcd16e8c1d8f493ba04e99a56474122d81a9c58") + util.convert_hex_to_one( "0xebcd16e8c1d8f493ba04e99a56474122d81a9c58" ) == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" ) assert ( - util.convert_hex_to_one("one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") + util.convert_hex_to_one( "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" ) == "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9" ) def test_get_bls_build_variables(): - assert isinstance(util.get_bls_build_variables(), dict) + assert isinstance( util.get_bls_build_variables(), dict ) def test_is_active_shard(): - assert isinstance(util.is_active_shard(""), bool) + assert isinstance( util.is_active_shard( "" ), bool ) From 1dba8b1066e0b8eb03a695229bcbc5ce3011f819 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:39:01 +0000 Subject: [PATCH 21/23] docs: update readme - update testing instructions to work with `make debug` instead of `make test-rpc` - add signatures to `create` and `edit` validators --- README.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4f5882f..e17023e 100644 --- a/README.md +++ b/README.md @@ -34,16 +34,16 @@ git clone https://github.com/harmony-one/mcl.git git clone https://github.com/harmony-one/bls.git git clone https://github.com/harmony-one/harmony.git cd harmony -make test-rpc +make debug ``` -Once the terminal displays `=== FINISHED RPC TESTS ===`, use another shell to run the following tests +Once the terminal displays a couple of `Started server` lines, use another shell to run the following tests ```bash make test ``` Or directly with `pytest` (reference [here](https://docs.pytest.org/en/latest/index.html) for more info): ```bash -py.test tests +pytest tests ``` ## Releasing @@ -61,6 +61,19 @@ test_address = 'one18t4yj4fuutj83uwqckkvxp9gfa0568uc48ggj7' main_net = 'https://rpc.s0.t.hmny.io' main_net_shard_1 = 'https://rpc.s1.t.hmny.io' ``` +#### utilities +##### Address conversion +```py +from pyhmy import util +hex_addr = util.convert_one_to_hex('one155jp2y76nazx8uw5sa94fr0m4s5aj8e5xm6fu3') +one_addr = util.convert_hex_to_one('0xA5241513DA9F4463F1d4874b548dFBAC29D91f34') +``` +##### Ether / Wei conversion +```py +from pyhmy import numbers +one_ether_in_wei = numbers.convert_one_to_atto(1) # as a decimal.Decimal +wei_to_ether = numbers.convert_atto_to_one(int(1e18)) +``` #### accounts ```py from pyhmy import account @@ -295,7 +308,10 @@ info = { 'max-rate': '0.9', 'max-change-rate': '0.05', 'rate': '0.01', - 'bls-public-keys': ['0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611'], + 'bls-public-keys': ['0xa20e70089664a874b00251c5e85d35a73871531306f3af43e02138339d294e6bb9c4eb82162199c6a852afeaa8d68712'], + "bls-key-sigs": [ + "0xef2c49a2f31fbbd23c21bc176eaf05cd0bebe6832033075d81fea7cff6f9bc1ab42f3b6895c5493fe645d8379d2eaa1413de55a9d3ce412a4f747cb57d52cc4da4754bfb2583ec9a41fe5dd48287f964f276336699959a5fcef3391dc24df00d", + ] 'max-total-delegation': convert_one_to_atto(40000) } validator.load(info) @@ -307,7 +323,7 @@ signed_create_tx_hash = validator.sign_create_validator_transaction( gas_price = 1, gas_limit = 100, private_key = '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', - chain_id = None).rawTransaction.hex() + chain_id = 2).rawTransaction.hex() ``` To edit validator, change its parameters using the `setter` functions, for example, `validator.set_details`, except the `rate`, `bls_keys_to_add` and `bls_keys_to_remove` which can be passed to the below function: ```py @@ -316,8 +332,9 @@ signed_edit_tx_hash = validator.sign_edit_validator_transaction( gas_price = 1, gas_limit = 100, rate = '0.06', - bls_keys_to_add = "0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611", - bls_keys_to_remove = '0xb9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612', + bls_key_to_add = "0xb8c3b3a0f1966c169ca73c348f4b8aee333a407125ab5c67f1d6e1e18ab052ed5fff0f1f7d4a7f789528b5ccd9c47b04", + bls_key_to_add_sig = "0x3de4dff17451fb76a9690efce34bced97dd87eccd371fcd25335826cb879ca21281e82e5c2c76d4ef0ab0fc16e462312628834cbc1f29008b28e16a757367808be85180945b991be3103f98c14c7e3b3e54796d34aab4d8e812d440aa251c419", + bls_keys_to_remove = '0xa20e70089664a874b00251c5e85d35a73871531306f3af43e02138339d294e6bb9c4eb82162199c6a852afeaa8d68712', private_key = '4edef2c24995d15b0e25cbd152fb0e2c05d3b79b9c2afd134e6f59f91bf99e48', chain_id = 2).rawTransaction.hex() ``` From c89976f2c0d3ca41ed193c21b9773d4415150302 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 21:42:33 +0000 Subject: [PATCH 22/23] test(validator): update edit validator sign for addition of bls key sig --- tests/sdk-pyhmy/test_validator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sdk-pyhmy/test_validator.py b/tests/sdk-pyhmy/test_validator.py index f99eefc..4dd3d1a 100644 --- a/tests/sdk-pyhmy/test_validator.py +++ b/tests/sdk-pyhmy/test_validator.py @@ -153,7 +153,7 @@ def test_edit_validator_sign( setup_blockchain ): ).rawTransaction.hex() assert ( signed_hash == - "0xf9012101f8d094ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b6224760861102880de0b6b3a76400006428a0782ed9f909b5c68eb050a917a274d9187a4c8f13799f21ba351bdcb11290c6c7a00a3b4ec8431290565acbbbb8231cafe32ed7c0b6211e7cf570b459cb733e0995" + "0xf9018401f9013294ebcd16e8c1d8f493ba04e99a56474122d81a9c58f83885416c69636585616c69636591616c6963652e6861726d6f6e792e6f6e6583426f6295446f6e2774206d6573732077697468206d65212121c887d529ae9e8600008a021e19e0c9bab24000008a0878678326eac9000000b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608612b0b9486167ab9087ab818dc4ce026edb5bf216863364c32e42df2af03c5ced1ad181e7d12f0e6dd5307a73b62247608611b86068f800b6adf657b674903e04708060912b893b7c7b500788808247550ab3e186e56a44ebf3ca488f8ed1a42f6cef3a04bd5d2b2b7eb5a767848d3135b362e668ce6bba42c7b9d5666d8e3a83be707b5708e722c58939fe9b07c170f3b706241402880de0b6b3a76400006427a0ecdae4a29d051f4f83dd54004858fbf0f7820e169b8e1846245835ceb686ee12a04b2336eb5830e30720137b2de539518fd5655467fef140ab31fde881a19f256a" ) From 1c90c914aa8772c286d166420c4824f610bfac59 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Tue, 23 Aug 2022 22:18:18 +0000 Subject: [PATCH 23/23] docs: remove -1 as page option --- README.md | 4 ++-- pyhmy/staking.py | 6 +++--- pyhmy/staking_signing.py | 12 ++++++------ tests/sdk-pyhmy/test_staking.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index e17023e..0771f93 100644 --- a/README.md +++ b/README.md @@ -258,7 +258,7 @@ delegator_addr = 'one1y2624lg0mpkxkcttaj0c85pp8pfmh2tt5zhdte' ```py all_validators = staking.get_all_validator_addresses(endpoint=test_net) # list of addresses validator_information = staking.get_validator_information(validator_addr, endpoint=test_net) # dict with all info -validator_information_100 = staking.get_all_validator_information(page=0, endpoint=test_net) # for all use page=-1 +validator_information_100 = staking.get_all_validator_information(page=0, endpoint=test_net) elected_validators = staking.get_elected_validator_addresses(endpoint=test_net) # list of addresses validators_for_epoch = staking.get_validators(epoch=73772, endpoint=test_net) # dict with list of validators and balance validators_information_100_for_block = staking.get_all_validator_information_by_block_number(block_num=9017724, page=0, endpoint=test_net) @@ -269,7 +269,7 @@ total_delegation = staking.get_validator_total_delegation(validator_addr, endpoi ``` ##### Delegation ```py -delegation_information = staking.get_all_delegation_information(page=-1, endpoint=test_net) +delegation_information = staking.get_all_delegation_information(page=0, endpoint=test_net) delegations_by_delegator = staking.get_delegations_by_delegator(delegator_addr, test_net) delegations_by_delegator_at_block = staking.get_delegations_by_delegator_by_block_number(delegator_addr, block_num=9017724, endpoint=test_net) delegation_by_delegator_and_validator = staking.get_delegation_by_delegator_and_validator(delegator_addr, validator_addr, test_net) diff --git a/pyhmy/staking.py b/pyhmy/staking.py index 3864182..78be5cc 100644 --- a/pyhmy/staking.py +++ b/pyhmy/staking.py @@ -316,7 +316,7 @@ def get_all_validator_information( Parameters ---------- page: :obj:`int`, optional - Page to request (-1 for all validators), page size is 100 otherwise + Page to request, page size is 100 otherwise endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional @@ -449,7 +449,7 @@ def get_all_validator_information_by_block_number( block_num: int Block number to get validator information for page: :obj:`int`, optional - Page to request (-1 for all validators), page size is 100 + Page to request, page size is 100 endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional @@ -495,7 +495,7 @@ def get_all_delegation_information( Parameters ---------- page: :obj:`int`, optional - Page to request (-1 for all validators), page size is 100 + Page to request, page size is 100 endpoint: :obj:`str`, optional Endpoint to send request to timeout: :obj:`int`, optional diff --git a/pyhmy/staking_signing.py b/pyhmy/staking_signing.py index baf98ed..bbf421f 100644 --- a/pyhmy/staking_signing.py +++ b/pyhmy/staking_signing.py @@ -27,7 +27,7 @@ from eth_utils.curried import ( to_int, apply_formatters_to_sequence, apply_formatter_to_array, - ) +) from .constants import PRECISION, MAX_DECIMAL @@ -40,7 +40,7 @@ from .staking_structures import ( EditValidator, DelegateOrUndelegate, CollectRewards, - ) +) from .util import convert_one_to_hex @@ -135,7 +135,7 @@ def _sign_transaction_generic( account, sanitized_transaction, parent_serializer - ): +): """Sign a generic staking transaction, given the serializer base class and account. @@ -186,11 +186,11 @@ def _sign_transaction_generic( } ) # drop extras silently # sign the unsigned transaction - if "v" in unsigned_transaction.as_dict( ): + if "v" in unsigned_transaction.as_dict(): chain_id = unsigned_transaction.v else: chain_id = None - transaction_hash = unsigned_transaction.hash( ) + transaction_hash = unsigned_transaction.hash() ( v, r, s @@ -198,7 +198,7 @@ def _sign_transaction_generic( transaction_hash, chain_id ) chain_naive_transaction = dissoc( - unsigned_transaction.as_dict( ), + unsigned_transaction.as_dict(), "v", "r", "s" diff --git a/tests/sdk-pyhmy/test_staking.py b/tests/sdk-pyhmy/test_staking.py index 6a4fef7..ca1b11a 100644 --- a/tests/sdk-pyhmy/test_staking.py +++ b/tests/sdk-pyhmy/test_staking.py @@ -209,7 +209,7 @@ def test_errors(): with pytest.raises( exceptions.RPCError ): staking.get_validator_information_by_block_number( "", 1, fake_shard ) with pytest.raises( exceptions.RPCError ): - staking.get_all_validator_information( -1, fake_shard ) + staking.get_all_validator_information( 0, fake_shard ) with pytest.raises( exceptions.RPCError ): staking.get_validator_self_delegation( "", fake_shard ) with pytest.raises( exceptions.RPCError ):