quantus_cli/cli/
runtime.rs1use crate::{
3 chain::quantus_subxt, error::QuantusError, log_print, log_success, log_verbose,
4 wallet::QuantumKeyPair,
5};
6use clap::Subcommand;
7use colored::Colorize;
8
9use crate::chain::client::ChainConfig;
10use std::{fs, path::PathBuf};
11use subxt::OnlineClient;
12
13#[derive(Subcommand, Debug)]
14pub enum RuntimeCommands {
15 Update {
17 #[arg(short, long)]
19 wasm_file: PathBuf,
20
21 #[arg(short, long)]
23 from: String,
24
25 #[arg(short, long)]
27 password: Option<String>,
28
29 #[arg(long)]
31 password_file: Option<String>,
32
33 #[arg(long)]
35 force: bool,
36 },
37
38 Compare {
40 #[arg(short, long)]
42 wasm_file: PathBuf,
43 },
44}
45
46pub async fn update_runtime(
48 quantus_client: &crate::chain::client::QuantusClient,
49 wasm_code: Vec<u8>,
50 from_keypair: &QuantumKeyPair,
51 force: bool,
52 finalized: bool,
53) -> crate::error::Result<subxt::utils::H256> {
54 log_verbose!("🔄 Updating runtime...");
55
56 log_print!("📋 Current runtime version:");
57 log_print!(" • Use 'quantus system --runtime' to see current version");
58
59 if !force {
61 log_print!("");
62 log_print!(
63 "⚠️ {} {}",
64 "WARNING:".bright_red().bold(),
65 "Runtime update is a critical operation!"
66 );
67 log_print!(" • This will update the blockchain runtime immediately");
68 log_print!(" • All nodes will need to upgrade to stay in sync");
69 log_print!(" • This operation cannot be easily reversed");
70 log_print!("");
71
72 print!("Do you want to proceed with the runtime update? (yes/no): ");
74 use std::io::{self, Write};
75 io::stdout().flush().unwrap();
76
77 let mut input = String::new();
78 io::stdin().read_line(&mut input).unwrap();
79
80 if input.trim().to_lowercase() != "yes" {
81 log_print!("❌ Runtime update cancelled");
82 return Err(QuantusError::Generic("Runtime update cancelled".to_string()));
83 }
84 }
85
86 let set_code_call =
88 quantus_subxt::api::Call::System(quantus_subxt::api::system::Call::set_code {
89 code: wasm_code,
90 });
91
92 let sudo_call = quantus_subxt::api::tx().sudo().sudo(set_code_call);
94
95 log_print!("📡 Submitting runtime update transaction...");
97 log_print!("⏳ This may take longer than usual due to WASM size...");
98
99 if !finalized {
100 log_print!(
101 "💡 Note: Waiting for best block (not finalized) due to PoW chain characteristics"
102 );
103 }
104
105 let tx_hash = crate::cli::common::submit_transaction(
106 quantus_client,
107 from_keypair,
108 sudo_call,
109 None,
110 finalized,
111 )
112 .await?;
113
114 log_success!(
115 "✅ SUCCESS Runtime update transaction submitted! Hash: 0x{}",
116 hex::encode(tx_hash)
117 );
118
119 Ok(tx_hash)
120}
121
122#[derive(Debug, Clone)]
124pub struct RuntimeVersionInfo {
125 pub spec_version: u32,
126 pub impl_version: u32,
127 pub transaction_version: u32,
128}
129
130pub async fn get_runtime_version(
132 client: &OnlineClient<ChainConfig>,
133) -> crate::error::Result<RuntimeVersionInfo> {
134 log_verbose!("🔍 Getting runtime version...");
135
136 let runtime_version = client.runtime_version();
137
138 Ok(RuntimeVersionInfo {
141 spec_version: runtime_version.spec_version,
142 impl_version: 1, transaction_version: runtime_version.transaction_version,
144 })
145}
146
147pub async fn calculate_wasm_hash(wasm_code: &[u8]) -> crate::error::Result<String> {
149 use sha2::{Digest, Sha256};
150 let mut hasher = Sha256::new();
151 hasher.update(wasm_code);
152 let local_hash = hasher.finalize();
153
154 Ok(format!("0x{}", hex::encode(local_hash)))
155}
156
157pub async fn handle_runtime_command(
159 command: RuntimeCommands,
160 node_url: &str,
161 finalized: bool,
162) -> crate::error::Result<()> {
163 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
164
165 match command {
166 RuntimeCommands::Update { wasm_file, from, password, password_file, force } => {
167 log_print!("🚀 Runtime Management");
168 log_print!("🔄 Runtime Update");
169 log_print!(" 📂 WASM file: {}", wasm_file.display().to_string().bright_cyan());
170 log_print!(" 🔑 Signed by: {}", from.bright_yellow());
171
172 if !wasm_file.exists() {
174 return Err(QuantusError::Generic(format!(
175 "WASM file not found: {}",
176 wasm_file.display()
177 )));
178 }
179
180 if let Some(ext) = wasm_file.extension() {
182 if ext != "wasm" {
183 log_print!("⚠️ Warning: File doesn't have .wasm extension");
184 }
185 }
186
187 let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
189
190 log_verbose!("📖 Reading WASM file...");
192 let wasm_code = fs::read(&wasm_file)
193 .map_err(|e| QuantusError::Generic(format!("Failed to read WASM file: {e}")))?;
194
195 log_print!("📊 WASM file size: {} bytes", wasm_code.len());
196
197 update_runtime(&quantus_client, wasm_code, &keypair, force, finalized).await?;
199
200 log_success!("🎉 Runtime update completed!");
201 log_print!(
202 "💡 Note: It may take a few moments for the new runtime version to be reflected."
203 );
204 log_print!("💡 Use 'quantus runtime check-version' to verify the new version.");
205
206 Ok(())
207 },
208
209 RuntimeCommands::Compare { wasm_file } => {
210 log_print!("🚀 Runtime Management");
211 log_print!("🔍 Comparing WASM file with current runtime...");
212 log_print!(" 📂 Local file: {}", wasm_file.display().to_string().bright_cyan());
213
214 if !wasm_file.exists() {
216 return Err(QuantusError::Generic(format!(
217 "WASM file not found: {}",
218 wasm_file.display()
219 )));
220 }
221
222 let local_wasm = fs::read(&wasm_file)
224 .map_err(|e| QuantusError::Generic(format!("Failed to read WASM file: {e}")))?;
225
226 log_print!("📊 Local WASM size: {} bytes", local_wasm.len());
227
228 let current_version = get_runtime_version(quantus_client.client()).await?;
230 log_print!("📋 Current chain runtime:");
231 log_print!(" • Spec version: {}", current_version.spec_version);
232 log_print!(" • Impl version: {}", current_version.impl_version);
233 log_print!(" • Transaction version: {}", current_version.transaction_version);
234
235 let local_hash = calculate_wasm_hash(&local_wasm).await?;
237 log_print!("🔐 Local WASM SHA256: {}", local_hash.bright_blue());
238
239 if let Ok(Some(chain_runtime_hash)) = quantus_client.get_runtime_hash().await {
241 log_print!("🔐 Chain runtime hash: {}", chain_runtime_hash.bright_yellow());
242
243 if local_hash == chain_runtime_hash {
245 log_success!("✅ Runtime hashes match! The WASM file is identical to the current runtime.");
246 } else {
247 log_print!("⚠️ Runtime hashes differ. The WASM file is different from the current runtime.");
248 }
249 } else {
250 log_print!("💡 Chain runtime hash not available for comparison");
251 }
252
253 let filename = wasm_file.file_name().unwrap().to_string_lossy();
255 log_verbose!("🔍 Parsing filename: {}", filename);
256
257 if let Some(version_str) = filename.split('-').nth(2) {
258 log_verbose!("🔍 Version part: {}", version_str);
259 if let Some(version_num) = version_str.split('.').next() {
260 log_verbose!("🔍 Version number: {}", version_num);
261 let clean_version = version_num.trim_start_matches('v');
263 log_verbose!("🔍 Clean version: {}", clean_version);
264 if let Ok(wasm_version) = clean_version.parse::<u32>() {
265 log_print!("📋 Version comparison:");
266 log_print!(
267 " • Local WASM version: {}",
268 wasm_version.to_string().bright_green()
269 );
270 log_print!(
271 " • Chain runtime version: {}",
272 current_version.spec_version.to_string().bright_yellow()
273 );
274
275 match wasm_version.cmp(¤t_version.spec_version) {
276 std::cmp::Ordering::Equal => {
277 log_success!("✅ Versions match! The WASM file is compatible with the current runtime.");
278 },
279 std::cmp::Ordering::Greater => {
280 log_print!("🔄 The WASM file is newer than the current runtime.");
281 log_print!(" • This would be an upgrade");
282 },
283 std::cmp::Ordering::Less => {
284 log_print!("⚠️ The WASM file is older than the current runtime.");
285 log_print!(" • This would be a downgrade");
286 },
287 }
288 } else {
289 log_print!("⚠️ Could not parse version number from filename");
290 }
291 } else {
292 log_print!("⚠️ Could not extract version number from filename");
293 }
294 } else {
295 log_print!("⚠️ Could not extract version from filename format");
296 }
297
298 log_print!("💡 Use 'quantus system --runtime' for detailed runtime information");
299
300 Ok(())
301 },
302 }
303}