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):