From 7c23bf96b731f350b6bbf101f0068076267cb6c8 Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Thu, 2 Nov 2023 20:33:33 +0100 Subject: [PATCH 1/2] Add new endpoint for managing invulnerable --- app/lib/collator_manager.py | 40 +++++++++++++++++++ app/lib/network_utils.py | 42 ++++++++++---------- app/routers/apis.py | 35 ++++++++++------ local-kubernetes/Makefile | 10 ++--- local-kubernetes/charts/helmfile-rococo.yaml | 2 +- 5 files changed, 91 insertions(+), 38 deletions(-) diff --git a/app/lib/collator_manager.py b/app/lib/collator_manager.py index 80d696d..04aa877 100644 --- a/app/lib/collator_manager.py +++ b/app/lib/collator_manager.py @@ -115,3 +115,43 @@ async def set_collator_selection_invulnerables(para_id, invulnerables): else: log.error(f'Failed to run xcm call para_id {para_id}, message: {encoded_call}, err: {invulnerables}') return None + +async def add_collator_selection_invulnerable(para_id, invulnerable): + log.info(f'Add the collator as Invulnerable for for para #{para_id}') + node_client = get_parachain_rpc_client(para_id) + call = node_client.compose_call( + call_module='CollatorSelection', + call_function='add_invulnerable', + call_params={ + 'who': invulnerable + } + ) + encoded_call = call.encode() + weight = get_query_weight(node_client, call) + log.info("call: {}, weight: {}".format(call, weight)) + receipt = substrate_sudo_relay_xcm_call(para_id, encoded_call, weight) + if receipt and receipt.is_success: + log.info(f'✅ Success: Add a new invulnerable via XCM to para #{para_id}') + else: + log.error(f'Failed to run xcm call para_id {para_id}, message: {encoded_call}, err: {invulnerable}') + return None + +async def remove_collator_selection_invulnerable(para_id, invulnerable): + log.info(f'Remove the collator as Invulnerable for para #{para_id}') + node_client = get_parachain_rpc_client(para_id) + call = node_client.compose_call( + call_module='CollatorSelection', + call_function='remove_invulnerable', + call_params={ + 'who': invulnerable + } + ) + encoded_call = call.encode() + weight = get_query_weight(node_client, call) + log.info("call: {}, weight: {}".format(call, weight)) + receipt = substrate_sudo_relay_xcm_call(para_id, encoded_call, weight) + if receipt and receipt.is_success: + log.info(f'✅ Success: Remove invulnerable via XCM to para #{para_id}') + else: + log.error(f'Failed to run xcm call para_id {para_id}, message: {encoded_call}, err: {invulnerable}') + return None diff --git a/app/lib/network_utils.py b/app/lib/network_utils.py index 868e25d..375039c 100644 --- a/app/lib/network_utils.py +++ b/app/lib/network_utils.py @@ -9,7 +9,8 @@ get_derived_collator_session_keys from app.lib.collator_manager import get_collator_status, \ collator_register, collator_deregister, get_moon_collator_status, \ - set_collator_selection_invulnerables, get_collator_selection_invulnerables + add_collator_selection_invulnerable, remove_collator_selection_invulnerable, \ + get_collator_selection_invulnerables, set_collator_selection_invulnerables from app.lib.collator_mint import collator_set_keys from app.lib.kubernetes_client import list_validator_pods, get_external_validators_from_configmap, \ list_collator_pods, get_pod, list_substrate_node_pods @@ -648,9 +649,8 @@ async def deregister_statefulset_collators(para_id: str, stateful_set_name: str) await deregister_collator_nodes(chain, collator_node_names, ss58_format) -async def add_invulnerable_collators(para_id, nodes=[], addresses=[]): - log.info(f'Adding invulnerables collators to parachain #{para_id}; nodes={nodes} and addresses={addresses}') - current_invulnerables = get_collator_selection_invulnerables(para_id) +async def set_invulnerable_collators(para_id, nodes=[], addresses=[]): + log.info(f'Setting invulnerables collators to parachain #{para_id}; nodes={nodes} and addresses={addresses}') invulnerables_to_add = [] for node_name in nodes: node_collator_account = get_substrate_node(node_name).get("collator_account") @@ -658,22 +658,24 @@ async def add_invulnerable_collators(para_id, nodes=[], addresses=[]): invulnerables_to_add.append(node_collator_account) for account_address in addresses: invulnerables_to_add.append(account_address) - invulnerables = list(set(current_invulnerables).union(set(invulnerables_to_add))) - await set_collator_selection_invulnerables(para_id, invulnerables) - - -async def remove_invulnerable_collators(para_id, nodes=[], addresses=[]): - log.info(f'Removing invulnerables collators to parachain #{para_id}; nodes={nodes} and addresses={addresses}') - current_invulnerables = get_collator_selection_invulnerables(para_id) - invulnerables_to_remove = [] - for node_name in nodes: - node_collator_account = get_substrate_node(node_name).get("collator_account") - if node_collator_account: - invulnerables_to_remove.append(node_collator_account) - for account_address in addresses: - invulnerables_to_remove.append(account_address) - invulnerables = list(set(current_invulnerables).difference(set(invulnerables_to_remove))) - await set_collator_selection_invulnerables(para_id, invulnerables) + await set_collator_selection_invulnerables(para_id, invulnerables_to_add) + +async def add_invulnerable_collator(para_id, node, address): + log.info(f'Adding invulnerable collator of parachain #{para_id}; node={node} and address={address}') + if node: + invulnerable = get_substrate_node(node).get("collator_account") + elif address: + invulnerable = address + await add_collator_selection_invulnerable(para_id, invulnerable) + + +async def remove_invulnerable_collator(para_id, node, address): + log.info(f'Removing invulnerable collator of parachain #{para_id}; node={node} and address={address}') + if node: + invulnerable = get_substrate_node(node).get("collator_account") + elif address: + invulnerable = address + await remove_collator_selection_invulnerable(para_id, invulnerable) async def set_collator_nodes_keys_on_chain(para_id, nodes=[], statefulset=''): diff --git a/app/routers/apis.py b/app/routers/apis.py index 3076d24..2c94b95 100644 --- a/app/routers/apis.py +++ b/app/routers/apis.py @@ -16,8 +16,8 @@ rotate_nodes_session_keys, register_statefulset_collators, onboard_parachain_by_id, \ offboard_parachain_by_id, deregister_statefulset_collators, get_substrate_node, \ register_validator_nodes, register_validator_addresses, deregister_validator_nodes, register_collator_nodes, \ - deregister_collator_nodes, add_invulnerable_collators, remove_invulnerable_collators, \ - set_collator_nodes_keys_on_chain + deregister_collator_nodes, add_invulnerable_collator, remove_invulnerable_collator, \ + set_collator_nodes_keys_on_chain, set_invulnerable_collators from app.lib.parachain_manager import parachain_runtime_upgrade from app.lib.runtime_utils import get_relay_runtime, get_relay_active_configuration, update_relay_configuration, \ get_parachain_runtime, runtime_upgrade, get_relaychain_metadata, get_parachain_metadata @@ -244,25 +244,36 @@ async def deregister_collators( return PlainTextResponse('OK') -@router.post("/collators/{para_id}/add_invulnerables") -async def add_invulnerables( +@router.post("/collators/{para_id}/set_invulnerables") +async def set_invulnerables( para_id: str = Path(description="Parachain ID"), - address: list[str] = Query(default=[], description="Address(es) to be added as invulnerables"), - node: list[str] = Query(default=[], description="Collator node(s) be added as invulnerables on the parachain") + address: list[str] = Query(default=[], description="Addresses to be added as invulnerables"), + node: list[str] = Query(default=[], description="Collator nodes to be added as invulnerables on the parachain") ): if node or address: - asyncio.create_task(add_invulnerable_collators(para_id, node, address)) + asyncio.create_task(set_invulnerable_collators(para_id, node, address)) return PlainTextResponse('OK') -@router.post("/collators/{para_id}/remove_invulnerables") -async def remove_invulnerables( +@router.post("/collators/{para_id}/add_invulnerable") +async def add_invulnerable( para_id: str = Path(description="Parachain ID"), - address: list[str] = Query(default=[], description="Address(es) to removed from invulnerables"), - node: list[str] = Query(default=[], description="Collator node(s) to be removed as invulnerables on the parachain") + address: str = Query(default="", description="Address to be added as invulnerable"), + node: str = Query(default="", description="Collator node to be added as invulnerable on the parachain") ): if node or address: - asyncio.create_task(remove_invulnerable_collators(para_id, node, address)) + asyncio.create_task(add_invulnerable_collator(para_id, node, address)) + return PlainTextResponse('OK') + + +@router.post("/collators/{para_id}/remove_invulnerable") +async def remove_invulnerable( + para_id: str = Path(description="Parachain ID"), + address:str = Query(default="", description="Address to removed from invulnerable"), + node: str = Query(default="", description="Collator node to be removed as invulnerable on the parachain") +): + if node or address: + asyncio.create_task(remove_invulnerable_collator(para_id, node, address)) return PlainTextResponse('OK') diff --git a/local-kubernetes/Makefile b/local-kubernetes/Makefile index 2446147..d71fd46 100644 --- a/local-kubernetes/Makefile +++ b/local-kubernetes/Makefile @@ -53,23 +53,23 @@ build_rancher-desktop: rpc: @xdg-open 'https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9944#/explorer' - @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local${CHAIN_NAMESPACE}-bootnode 9944:9944 -n ${CHAIN_NAMESPACE} + @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local-${CHAIN_NAMESPACE}-bootnode 9944:9944 -n ${CHAIN_NAMESPACE} para-shell: @xdg-open 'https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9948#/explorer' - @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local${CHAIN_NAMESPACE}-shell-collator-node 9948:9944 -n ${CHAIN_NAMESPACE} + @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local-${CHAIN_NAMESPACE}-shell-collator-node 9948:9944 -n ${CHAIN_NAMESPACE} para-moon: @xdg-open 'https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9949#/explorer' - @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local${CHAIN_NAMESPACE}-moonbase-alice-node 9949:9944 -n ${CHAIN_NAMESPACE} + @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local-${CHAIN_NAMESPACE}-moonbase-alice-node 9949:9944 -n ${CHAIN_NAMESPACE} para-mint: @xdg-open 'https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9950#/explorer' - @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local${CHAIN_NAMESPACE}-statemint-alice-node 9950:9944 -n ${CHAIN_NAMESPACE} + @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local-${CHAIN_NAMESPACE}-statemint-alice-node 9950:9944 -n ${CHAIN_NAMESPACE} para-tick: @xdg-open 'https://polkadot.js.org/apps/?rpc=ws%3A%2F%2F127.0.0.1%3A9951#/explorer' - @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local${CHAIN_NAMESPACE}-tick-collator-node 9951:9944 -n ${CHAIN_NAMESPACE} + @kubectl --context ${KUBERNETES_CONTEXT} port-forward service/local-${CHAIN_NAMESPACE}-tick-collator-node 9951:9944 -n ${CHAIN_NAMESPACE} web: @xdg-open 'http://localhost:8080/' diff --git a/local-kubernetes/charts/helmfile-rococo.yaml b/local-kubernetes/charts/helmfile-rococo.yaml index 8269e06..e910b1b 100644 --- a/local-kubernetes/charts/helmfile-rococo.yaml +++ b/local-kubernetes/charts/helmfile-rococo.yaml @@ -19,7 +19,7 @@ releases: - name: local-rococo-bootnode namespace: rococo chart: parity/node - version: &node_version 5.1.7 + version: &node_version 5.4.0 values: - ./values-local-rococo-bootnode.yaml - node: From 8a76aa257efe87a675bbe3f4947cd3aa2866677e Mon Sep 17 00:00:00 2001 From: Anthony Lazam Date: Mon, 6 Nov 2023 11:07:13 +0100 Subject: [PATCH 2/2] Added back old implementation for backwards compatibility --- app/lib/network_utils.py | 24 ++++++++++++++++++++---- app/routers/apis.py | 27 +++++++++++++++++++-------- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/app/lib/network_utils.py b/app/lib/network_utils.py index cd1abb0..a402a5b 100644 --- a/app/lib/network_utils.py +++ b/app/lib/network_utils.py @@ -652,9 +652,10 @@ async def deregister_statefulset_collators(para_id: str, stateful_set_name: str) collator_node_names = list(map(lambda pod: pod.metadata.name, collators_pods)) await deregister_collator_nodes(chain, collator_node_names, ss58_format) - -async def set_invulnerable_collators(para_id, nodes=[], addresses=[]): - log.info(f'Setting invulnerables collators to parachain #{para_id}; nodes={nodes} and addresses={addresses}') +# To be deprecated next version +async def add_invulnerable_collators(para_id, nodes=[], addresses=[]): + log.info(f'Adding invulnerables collators to parachain #{para_id}; nodes={nodes} and addresses={addresses}') + current_invulnerables = get_collator_selection_invulnerables(para_id) invulnerables_to_add = [] for node_name in nodes: node_collator_account = get_substrate_node(node_name).get("collator_account") @@ -662,7 +663,22 @@ async def set_invulnerable_collators(para_id, nodes=[], addresses=[]): invulnerables_to_add.append(node_collator_account) for account_address in addresses: invulnerables_to_add.append(account_address) - await set_collator_selection_invulnerables(para_id, invulnerables_to_add) + invulnerables = list(set(current_invulnerables).union(set(invulnerables_to_add))) + await set_collator_selection_invulnerables(para_id, invulnerables) + +# To be deprecated next version +async def remove_invulnerable_collators(para_id, nodes=[], addresses=[]): + log.info(f'Removing invulnerables collators to parachain #{para_id}; nodes={nodes} and addresses={addresses}') + current_invulnerables = get_collator_selection_invulnerables(para_id) + invulnerables_to_remove = [] + for node_name in nodes: + node_collator_account = get_substrate_node(node_name).get("collator_account") + if node_collator_account: + invulnerables_to_remove.append(node_collator_account) + for account_address in addresses: + invulnerables_to_remove.append(account_address) + invulnerables = list(set(current_invulnerables).difference(set(invulnerables_to_remove))) + await set_collator_selection_invulnerables(para_id, invulnerables) async def add_invulnerable_collator(para_id, node, address): log.info(f'Adding invulnerable collator of parachain #{para_id}; node={node} and address={address}') diff --git a/app/routers/apis.py b/app/routers/apis.py index 2c94b95..16c3999 100644 --- a/app/routers/apis.py +++ b/app/routers/apis.py @@ -17,7 +17,7 @@ offboard_parachain_by_id, deregister_statefulset_collators, get_substrate_node, \ register_validator_nodes, register_validator_addresses, deregister_validator_nodes, register_collator_nodes, \ deregister_collator_nodes, add_invulnerable_collator, remove_invulnerable_collator, \ - set_collator_nodes_keys_on_chain, set_invulnerable_collators + set_collator_nodes_keys_on_chain, add_invulnerable_collators, remove_invulnerable_collators from app.lib.parachain_manager import parachain_runtime_upgrade from app.lib.runtime_utils import get_relay_runtime, get_relay_active_configuration, update_relay_configuration, \ get_parachain_runtime, runtime_upgrade, get_relaychain_metadata, get_parachain_metadata @@ -244,18 +244,29 @@ async def deregister_collators( return PlainTextResponse('OK') -@router.post("/collators/{para_id}/set_invulnerables") -async def set_invulnerables( +@router.post("/collators/{para_id}/add_invulnerables", deprecated=True) +async def add_invulnerables( para_id: str = Path(description="Parachain ID"), - address: list[str] = Query(default=[], description="Addresses to be added as invulnerables"), - node: list[str] = Query(default=[], description="Collator nodes to be added as invulnerables on the parachain") + address: list[str] = Query(default=[], description="Address(es) to be added as invulnerables"), + node: list[str] = Query(default=[], description="Collator node(s) be added as invulnerables on the parachain") ): if node or address: - asyncio.create_task(set_invulnerable_collators(para_id, node, address)) + asyncio.create_task(add_invulnerable_collators(para_id, node, address)) return PlainTextResponse('OK') -@router.post("/collators/{para_id}/add_invulnerable") +@router.post("/collators/{para_id}/remove_invulnerables", deprecated=True) +async def remove_invulnerables( + para_id: str = Path(description="Parachain ID"), + address: list[str] = Query(default=[], description="Address(es) to removed from invulnerables"), + node: list[str] = Query(default=[], description="Collator node(s) to be removed as invulnerables on the parachain") +): + if node or address: + asyncio.create_task(remove_invulnerable_collators(para_id, node, address)) + return PlainTextResponse('OK') + + +@router.post("/collators/{para_id}/invulnerable/add") async def add_invulnerable( para_id: str = Path(description="Parachain ID"), address: str = Query(default="", description="Address to be added as invulnerable"), @@ -266,7 +277,7 @@ async def add_invulnerable( return PlainTextResponse('OK') -@router.post("/collators/{para_id}/remove_invulnerable") +@router.post("/collators/{para_id}/invulnerable/remove") async def remove_invulnerable( para_id: str = Path(description="Parachain ID"), address:str = Query(default="", description="Address to removed from invulnerable"),