From 70ecfcedd3159b32950fd1f68adc4958936aee19 Mon Sep 17 00:00:00 2001 From: Ashraf Fouda Date: Sun, 23 Aug 2026 22:12:08 +0300 Subject: [PATCH] feat(grid_client): add node_call + create_node_contract low-level helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - node_call(node_twin_id, command, payload): generic RMB escape hatch for zos RPCs with no dedicated typed helper (e.g. zos.deployment.get/transfer/prepare, zos.statistics.get). Signs + routes exactly like the built-in deployment helpers. - create_node_contract(node_id, deployment_data, deployment_hash_hex, public_ips): submit a node contract for a deployment that already carries the given md5 hash — used when relocating an existing deployment to another node (contract id is excluded from the hash, so the source signature stays valid). Returns the new contract id via grid-proxy lookup. Both are needed by the within-farm VM migration control plane (my_migrator). --- src/grid_client/mod.rs | 55 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/grid_client/mod.rs b/src/grid_client/mod.rs index b6c752c..bcc9abb 100644 --- a/src/grid_client/mod.rs +++ b/src/grid_client/mod.rs @@ -1847,6 +1847,61 @@ impl GridClient { } } + /// Create a node contract on-chain for a deployment that already carries the given + /// md5 hash and metadata — e.g. an existing deployment being **relocated** to a new + /// node (the contract id is excluded from the deployment hash, so the source + /// signature stays valid). Submits the `create_node_contract` extrinsic and returns + /// the new contract id (looked up via grid-proxy once the contract is on-chain). + /// Low-level companion to [`GridClient::node_call`] for migration/relocation. + pub async fn create_node_contract( + &self, + node_id: u32, + deployment_data: &str, + deployment_hash_hex: &str, + public_ips: u32, + ) -> Result { + submit_create_node_contract( + &self.chain, + &self.signer, + node_id, + deployment_data, + deployment_hash_hex, + public_ips, + ) + .await?; + self.wait_for_contract(node_id, deployment_hash_hex).await + } + + /// Invoke an arbitrary node RMB command — a low-level escape hatch for zos RPCs + /// that have no dedicated typed helper on [`GridClient`]. + /// + /// `command` is a zos method name such as `"zos.deployment.get"`; `payload` is + /// JSON-serialized as the request body and the JSON reply is deserialized into + /// `R` (use [`serde_json::Value`] for dynamic/empty replies). The call is signed + /// with this client's identity and routed through the configured relay, exactly + /// like the built-in deployment helpers. + /// + /// ```no_run + /// # async fn demo(client: &zos_sdk_rust::GridClient) -> Result<(), zos_sdk_rust::GridError> { + /// let deployment: serde_json::Value = client + /// .node_call(node_twin_id, "zos.deployment.get", &serde_json::json!({ "contract_id": 42u64 })) + /// .await?; + /// # let node_twin_id = 0u32; let _ = deployment; Ok(()) + /// # } + /// ``` + pub async fn node_call( + &self, + node_twin_id: u32, + command: &str, + payload: &T, + ) -> Result + where + T: Serialize, + R: for<'de> Deserialize<'de> + 'static, + { + self.rmb_call(node_twin_id, command, payload, None).await + } + async fn rmb_call( &self, destination_twin_id: u32,