diff --git a/E2E-tests/config/api_config.py b/E2E-tests/config/api_config.py index 39a0dca5..c98ab3aa 100644 --- a/E2E-tests/config/api_config.py +++ b/E2E-tests/config/api_config.py @@ -1,6 +1,7 @@ from dataclasses import dataclass from omegaconf import MISSING, SI from typing import Optional +from src.partner_chain_rpc import DParam @dataclass @@ -116,6 +117,8 @@ class NodesApiConfig: selected_node: str = MISSING node: Node = MISSING token_policy_id: str = MISSING + d_param_min: Optional[DParam] = None + d_param_max: Optional[DParam] = None active_transfer_account: TransferAccount = MISSING passive_transfer_account: TransferAccount = MISSING negative_test_transfer_account: TransferAccount = MISSING diff --git a/E2E-tests/config/substrate/local_nodes.json b/E2E-tests/config/substrate/local_nodes.json index a08b988b..685affcf 100644 --- a/E2E-tests/config/substrate/local_nodes.json +++ b/E2E-tests/config/substrate/local_nodes.json @@ -96,6 +96,14 @@ "lock": 817414140, "send": 298945143 } + }, + "d_param_min": { + "permissioned_candidates_number": 2, + "trustless_candidates_number": 2 + }, + "d_param_max": { + "permissioned_candidates_number": 5, + "trustless_candidates_number": 5 } }, "block_encoding_suffix_grandpa": "3903", diff --git a/E2E-tests/config/substrate/staging_nodes.json b/E2E-tests/config/substrate/staging_nodes.json index da1afc07..daf53101 100644 --- a/E2E-tests/config/substrate/staging_nodes.json +++ b/E2E-tests/config/substrate/staging_nodes.json @@ -141,6 +141,14 @@ "lock": 817414141, "send": 298945143 } + }, + "d_param_min": { + "permissioned_candidates_number": 6, + "trustless_candidates_number": 4 + }, + "d_param_max": { + "permissioned_candidates_number": 6, + "trustless_candidates_number": 4 } } } diff --git a/E2E-tests/src/blockchain_api.py b/E2E-tests/src/blockchain_api.py index 2bdce881..e37cae62 100644 --- a/E2E-tests/src/blockchain_api.py +++ b/E2E-tests/src/blockchain_api.py @@ -120,6 +120,18 @@ def get_authorities(self) -> list: def get_status(self): pass + @abstractmethod + def update_d_param(self, permissioned_candidates_count: int, trustless_candidates_count: int) -> dict: + """ + Update D parameter configuration for the sidechain + Arguments: + permissioned_candidates_count {int} -- Number of permissioned candidates + trustless_candidates_count {int} -- Number of trustless candidates + Returns: + (bool, json response) - True/False, and a json response from the sidechain main cli + """ + pass + @abstractmethod def register_candidate(self, candidate_name: str) -> (bool, int): """ diff --git a/E2E-tests/src/sidechain_main_cli.py b/E2E-tests/src/sidechain_main_cli.py index 92bcbb4e..2335be26 100644 --- a/E2E-tests/src/sidechain_main_cli.py +++ b/E2E-tests/src/sidechain_main_cli.py @@ -77,6 +77,31 @@ def get_signatures( raise e return signatures + def update_d_param( + self, + permissioned_candidates_count, + registered_candidates_count, + payment_key, + ): + update_d_param_cmd = ( + f"{self.cli} update-d-parameter " + f"--genesis-utxo {self.config.genesis_utxo} " + f"--d-parameter-permissioned-candidates-count {permissioned_candidates_count} " + f"--d-parameter-registered-candidates-count {registered_candidates_count} " + f"--payment-signing-key-file {payment_key} " + f"--ogmios-host {self.config.stack_config.ogmios_host} " + f"--ogmios-port {self.config.stack_config.ogmios_port} " + f"--kupo-host {self.config.stack_config.kupo_host} " + f"--kupo-port {self.config.stack_config.kupo_port} " + f"--network {self.config.nodes_config.network}" + ) + + result = self.run_command.run(update_d_param_cmd) + + response = self.handle_response(result) + + return response + def register_candidate(self, signatures: RegistrationSignatures, payment_key, spo_public_key, registration_utxo): register_cmd = ( f"{self.cli} register " diff --git a/E2E-tests/src/substrate_api.py b/E2E-tests/src/substrate_api.py index b8733269..2d98c396 100644 --- a/E2E-tests/src/substrate_api.py +++ b/E2E-tests/src/substrate_api.py @@ -283,8 +283,25 @@ def _read_cardano_key_file(self, filepath): logger.error(f"Could not parse cardano key file: {e}") return key.strip() - ################# + def update_d_param(self, permissioned_candidates_count, registered_candidates_count): + signing_key = self.config.nodes_config.governance_authority.mainchain_key + + result = self.sidechain_main_cli.update_d_param( + permissioned_candidates_count, + registered_candidates_count, + signing_key, + ) + if result: + logger.info( + f"Update of D Param of P: {permissioned_candidates_count} and R: {registered_candidates_count} " + f" was successful and will take effect in 2 epochs " + ) + return True, result + else: + return False, None + + ################# def register_candidate(self, candidate_name): keys_files = self.config.nodes_config.nodes[candidate_name].keys_files # Get a UTxO from payment account diff --git a/E2E-tests/tests/committee/test_committee.py b/E2E-tests/tests/committee/test_committee.py index 8765a208..fe99a16c 100644 --- a/E2E-tests/tests/committee/test_committee.py +++ b/E2E-tests/tests/committee/test_committee.py @@ -35,6 +35,54 @@ def calculate_d_param_tolerance(pc_epochs_in_mc_epoch, d_param_p, d_param_t): class TestCommitteeDistribution: + @mark.committee_distribution + @mark.ariadne + @mark.xdist_group("governance_action") + def test_update_d_param( + self, + api: BlockchainApi, + config: ApiConfig, + current_mc_epoch, + ): + """ + * get DParam for n + 2 mc epoch + * generate new DParam and update it + * confirm that DParam was updated + """ + if not config.nodes_config.d_param_min or not config.nodes_config.d_param_max: + skip("Cannot test d-param update when min/max parameters are not set") + + p_floor = config.nodes_config.d_param_min.permissioned_candidates_number + p_ceil = config.nodes_config.d_param_max.permissioned_candidates_number + t_floor = config.nodes_config.d_param_min.trustless_candidates_number + t_ceil = config.nodes_config.d_param_max.trustless_candidates_number + + current_d_param = api.get_d_param(current_mc_epoch + 2) + + if ( + p_floor == p_ceil == current_d_param.permissioned_candidates_number + and t_floor == t_ceil == current_d_param.trustless_candidates_number + ): + skip("Cannot generate new d-param when min and max are equal to current d-param") + + new_d_param = current_d_param + while new_d_param == current_d_param: + new_d_param = DParam(np.random.randint(p_floor, p_ceil + 1), np.random.randint(t_floor, t_ceil + 1)) + + logging.info(f"Updating d-param to {new_d_param}") + result = api.update_d_param(new_d_param.permissioned_candidates_number, new_d_param.trustless_candidates_number) + assert result, "D-param update failed" + + # FIXME: ETCM-8945 - create and use wait_for_transaction function instead of wait_for_next_pc_block + api.wait_for_next_pc_block() + actual_d_param = api.get_d_param(current_mc_epoch + 2) + actual_p = actual_d_param.permissioned_candidates_number + actual_t = actual_d_param.trustless_candidates_number + assert ( + new_d_param.permissioned_candidates_number == actual_p + and new_d_param.trustless_candidates_number == actual_t + ), "D-param update did not take effect" + @mark.test_key('ETCM-7150') @mark.committee_distribution @mark.ariadne