From b2ee66adb763e38f96177a7ea70713182ada85b9 Mon Sep 17 00:00:00 2001 From: BulatSaif Date: Thu, 13 Oct 2022 13:37:58 +0300 Subject: [PATCH 1/4] Fix setup_pos_validator --- app/lib/network_utils.py | 2 +- app/lib/validator_manager.py | 59 ++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/app/lib/network_utils.py b/app/lib/network_utils.py index 726fba2..d1ecf0f 100644 --- a/app/lib/network_utils.py +++ b/app/lib/network_utils.py @@ -309,7 +309,7 @@ async def register_validator_pods(pods): node_session_key = rotate_node_session_keys(node_http_endpoint(node)) log.info(f'Registering PoS Validator: {node}') - status = setup_pos_validator(ws_endpoint, node_session_key, validator_stash_mnemonic) + status = setup_pos_validator(ws_endpoint, validator_stash_mnemonic, node_session_key) if status: log.info(f'Successfully set up PoS Validator: {node}') else: diff --git a/app/lib/validator_manager.py b/app/lib/validator_manager.py index d1deacc..f6dec98 100644 --- a/app/lib/validator_manager.py +++ b/app/lib/validator_manager.py @@ -1,6 +1,6 @@ import logging from substrateinterface import Keypair -from app.lib.substrate import substrate_query_url, substrate_sudo_call, get_substrate_client, substrate_call,\ +from app.lib.substrate import substrate_query_url, substrate_sudo_call, get_substrate_client, substrate_call, \ substrate_batchall_call from app.config.network_configuration import network_consensus from app.lib.session_keys import decode_session_key @@ -58,34 +58,44 @@ def get_validators_to_retire(ws_endpoint): return list(set(active_validators) - set(staking_validators)) -def setup_pos_validator(ws_endpoint, session_key, stash_seed, controller_seed=None): +def setup_pos_validator(ws_endpoint, stash_seed, session_key=None, controller_seed=None): batch_call = [] substrate_client = get_substrate_client(ws_endpoint) stash_keypair = Keypair.create_from_uri(stash_seed) - batch_call.append(substrate_client.compose_call( - call_module='Staking', - call_function='bond', - call_params={ - 'controller': stash_keypair.ss58_address, - 'value': 1 * 10 ** 11, - 'payee': "Staked" - } + # 1. Bond + bonded = substrate_client.query('Staking', 'Bonded', params=[stash_keypair.ss58_address]).value + # If we already bond, skip this step + if not bonded or not (bonded in [stash_keypair.ss58_address, controller_seed]): + batch_call.append( + substrate_client.compose_call( + call_module='Staking', + call_function='bond', + call_params={ + 'controller': stash_keypair.ss58_address, + 'value': 1 * 10 ** 11, + 'payee': "Staked" + } + ) ) - ) - if type(session_key) == str: - session_key = decode_session_key(substrate_client,session_key) - batch_call.append(substrate_client.compose_call( - call_module='Session', - call_function='set_keys', - call_params={ - 'keys': session_key, - 'proof': '' - } + # 2. Set session key. If a session key is not provided we assume what session key is already set ( Example: we are + # registering deregistered validator) + if session_key: + if type(session_key) == str: + session_key = decode_session_key(substrate_client, session_key) + batch_call.append( + substrate_client.compose_call( + call_module='Session', + call_function='set_keys', + call_params={ + 'keys': session_key, + 'proof': '' + } + ) ) - ) + # 3. Validate batch_call.append(substrate_client.compose_call( call_module='Staking', call_function='validate', @@ -98,6 +108,7 @@ def setup_pos_validator(ws_endpoint, session_key, stash_seed, controller_seed=N ) ) + # 4. Set collator account. if controller_seed: controller_keypair = Keypair.create_from_uri(controller_seed) batch_call.append(substrate_client.compose_call( @@ -111,7 +122,11 @@ def setup_pos_validator(ws_endpoint, session_key, stash_seed, controller_seed=N result = substrate_batchall_call(substrate_client, stash_keypair, batch_call, wait=True) if result and result.is_success: - return True + if any(e.value['event_id'] == "BatchInterrupted" for e in result.triggered_events): + log.error("Batch call failed: BatchInterrupted for stash {}".format(stash_keypair.ss58_address)) + return False + else: + return True else: return False From 6a021ed24a29e6a2b925fc273d321764578fd86a Mon Sep 17 00:00:00 2001 From: BulatSaif Date: Thu, 13 Oct 2022 14:16:16 +0300 Subject: [PATCH 2/4] Update error message --- app/lib/validator_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/lib/validator_manager.py b/app/lib/validator_manager.py index f6dec98..b3a1be9 100644 --- a/app/lib/validator_manager.py +++ b/app/lib/validator_manager.py @@ -123,7 +123,8 @@ def setup_pos_validator(ws_endpoint, stash_seed, session_key=None, controller_se result = substrate_batchall_call(substrate_client, stash_keypair, batch_call, wait=True) if result and result.is_success: if any(e.value['event_id'] == "BatchInterrupted" for e in result.triggered_events): - log.error("Batch call failed: BatchInterrupted for stash {}".format(stash_keypair.ss58_address)) + log.error("Batch call failed: BatchInterrupted for stash {}, extrinsic_hash: {}, block_hash: {}".format( + stash_keypair.ss58_address, result.extrinsic_hash, result.block_hash)) return False else: return True From a3bf37b9245ab15d76c5cfc66940f7999dd1f296 Mon Sep 17 00:00:00 2001 From: BulatSaif Date: Thu, 13 Oct 2022 19:04:02 +0300 Subject: [PATCH 3/4] Added test --- tests/validator_manager_pos_test.py | 68 +++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 tests/validator_manager_pos_test.py diff --git a/tests/validator_manager_pos_test.py b/tests/validator_manager_pos_test.py new file mode 100644 index 0000000..f94c6cc --- /dev/null +++ b/tests/validator_manager_pos_test.py @@ -0,0 +1,68 @@ +import unittest, os + +from testcontainers.core.container import DockerContainer +from substrateinterface import Keypair +from app.lib.substrate import get_substrate_client +from app.lib.validator_manager import get_validator_set, setup_pos_validator, get_validators_pending_addition, staking_chill, \ + get_validators_pending_deletion +from tests.test_utils import wait_for_http_ready +from unittest import mock + + +@mock.patch.dict(os.environ, {"TESTNET_MANAGER_CONSENSUS": "pos"}) +class ValidatorManagerTestPoS(unittest.TestCase): + + def setUp(self): + # Start Alice validator + self.alice_validator = DockerContainer('parity/polkadot:latest') + self.alice_validator.with_command('--chain westend-local --validator --alice --unsafe-ws-external --rpc-cors=all') + self.alice_validator.with_exposed_ports(9944, 10333) + self.alice_validator.start() + + self.alice_validator_rpc_ws_url = 'ws://{}:{}'.format(self.alice_validator.get_container_host_ip(), + self.alice_validator.get_exposed_port(9944)) + + # Start Bob validator and connect it to Alice + self.bob_validator = DockerContainer('parity/polkadot:latest') + self.bob_validator.with_command(""" + --chain westend-local --validator --bob --unsafe-ws-external --rpc-cors=all \ + --bootnodes /ip4/127.0.0.1/tcp/{}/p2p/12D3KooWAvdwXzjmRpkHpz8PzUTaX1o23SdpgAWVyTGMSQ68QXK6 + """.format(self.alice_validator.get_exposed_port(10333))) + self.bob_validator.with_exposed_ports(9933, 9944) + self.bob_validator.start() + self.bob_validator_http_url = 'http://{}:{}'.format(self.bob_validator.get_container_host_ip(), + self.bob_validator.get_exposed_port(9933)) + self.bob_validator_rpc_ws_url = 'ws://{}:{}'.format(self.bob_validator.get_container_host_ip(), + self.bob_validator.get_exposed_port(9944)) + self.alice_key = '0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a' + + wait_for_http_ready(self.bob_validator_http_url + '/health') + + def tearDown(self): + self.alice_validator.stop() + self.bob_validator.stop() + + def test_get_validator_set(self): + validator_set = get_validator_set(self.bob_validator_rpc_ws_url) + print(validator_set) + self.assertTrue(validator_set, 'Validator set is present on the chain') + + def test_register_validator(self): + charlie_stash = Keypair.create_from_uri('//Charlie//stash', ss58_format=42) + substrate_client = get_substrate_client(self.alice_validator_rpc_ws_url) + session_key = substrate_client.rpc_request(method="author_rotateKeys", params=[])['result'] + setup_pos_validator(self.alice_validator_rpc_ws_url, "//Charlie//stash", session_key, "//Charlie") + validators_to_add = get_validators_pending_addition(self.alice_validator_rpc_ws_url) + print(validators_to_add) + self.assertEqual(validators_to_add, [charlie_stash.ss58_address], "Registered validator address successfully added to validators_to_add") + + def test_deregister_validator(self): + bob_stash = Keypair.create_from_uri("//Bob//stash", ss58_format=42) + staking_chill(self.alice_validator_rpc_ws_url, "//Bob") # //Bob - is controller + validators_to_retire = get_validators_pending_deletion(self.alice_validator_rpc_ws_url) + print(validators_to_retire) + self.assertEqual(validators_to_retire, [bob_stash.ss58_address], "Deregistered validator address successfully added to validators_to_retire") + + +if __name__ == '__main__': + unittest.main() From 413390c153ec628cb847f389aefa5747a25554cb Mon Sep 17 00:00:00 2001 From: BulatSaif Date: Thu, 13 Oct 2022 19:29:52 +0300 Subject: [PATCH 4/4] rename function names --- tests/validator_manager_pos_test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/validator_manager_pos_test.py b/tests/validator_manager_pos_test.py index f94c6cc..140b329 100644 --- a/tests/validator_manager_pos_test.py +++ b/tests/validator_manager_pos_test.py @@ -42,12 +42,12 @@ def tearDown(self): self.alice_validator.stop() self.bob_validator.stop() - def test_get_validator_set(self): + def test_get_pos_validator_set(self): validator_set = get_validator_set(self.bob_validator_rpc_ws_url) print(validator_set) self.assertTrue(validator_set, 'Validator set is present on the chain') - def test_register_validator(self): + def test_register_pos_validator(self): charlie_stash = Keypair.create_from_uri('//Charlie//stash', ss58_format=42) substrate_client = get_substrate_client(self.alice_validator_rpc_ws_url) session_key = substrate_client.rpc_request(method="author_rotateKeys", params=[])['result'] @@ -56,7 +56,7 @@ def test_register_validator(self): print(validators_to_add) self.assertEqual(validators_to_add, [charlie_stash.ss58_address], "Registered validator address successfully added to validators_to_add") - def test_deregister_validator(self): + def test_deregister_pos_validator(self): bob_stash = Keypair.create_from_uri("//Bob//stash", ss58_format=42) staking_chill(self.alice_validator_rpc_ws_url, "//Bob") # //Bob - is controller validators_to_retire = get_validators_pending_deletion(self.alice_validator_rpc_ws_url)