use crate::{
chain::quantus_subxt, error::QuantusError, log_print, log_success, log_verbose,
wallet::QuantumKeyPair,
};
use clap::Subcommand;
use colored::Colorize;
use crate::chain::client::ChainConfig;
use std::{fs, path::PathBuf};
use subxt::OnlineClient;
#[derive(Subcommand, Debug)]
pub enum RuntimeCommands {
Update {
#[arg(short, long)]
wasm_file: PathBuf,
#[arg(short, long)]
from: String,
#[arg(short, long)]
password: Option<String>,
#[arg(long)]
password_file: Option<String>,
#[arg(long)]
force: bool,
},
Compare {
#[arg(short, long)]
wasm_file: PathBuf,
},
}
pub async fn update_runtime(
quantus_client: &crate::chain::client::QuantusClient,
wasm_code: Vec<u8>,
from_keypair: &QuantumKeyPair,
force: bool,
execution_mode: crate::cli::common::ExecutionMode,
) -> crate::error::Result<subxt::utils::H256> {
log_verbose!("🔄 Updating runtime...");
log_print!("📋 Current runtime version:");
log_print!(" • Use 'quantus system --runtime' to see current version");
log_print!("📋 Upgrade path:");
log_print!(" • This submits a Tech Referendum with Root origin (not an immediate root call)");
if !force {
log_print!("");
log_print!(
"⚠️ {} {}",
"WARNING:".bright_red().bold(),
"Runtime update is a critical operation!"
);
log_print!(" • This will submit a governance proposal to upgrade the runtime");
log_print!(" • If approved and enacted, all nodes will need to upgrade to stay in sync");
log_print!(" • Governance operations cannot be easily reversed");
log_print!("");
print!("Do you want to proceed with the runtime update? (yes/no): ");
use std::io::{self, Write};
io::stdout().flush().unwrap();
let mut input = String::new();
io::stdin().read_line(&mut input).unwrap();
if input.trim().to_lowercase() != "yes" {
log_print!("❌ Runtime update cancelled");
return Err(QuantusError::Generic("Runtime update cancelled".to_string()));
}
}
use sp_runtime::traits::{BlakeTwo256, Hash};
let set_code_payload = quantus_subxt::api::tx().system().set_code(wasm_code.clone());
let metadata = quantus_client.client().metadata();
let encoded_call = <_ as subxt::tx::Payload>::encode_call_data(&set_code_payload, &metadata)
.map_err(|e| QuantusError::Generic(format!("Failed to encode call data: {:?}", e)))?;
log_print!("📡 Submitting runtime upgrade proposal (preimage + referendum)...");
log_print!("⏳ This may take longer than usual due to WASM size...");
log_verbose!("📝 Encoded call size: {} bytes", encoded_call.len());
let preimage_hash: sp_core::H256 = BlakeTwo256::hash(&encoded_call);
log_print!("🔗 Preimage hash: {:?}", preimage_hash);
type PreimageBytes = quantus_subxt::api::preimage::calls::types::note_preimage::Bytes;
let bounded_bytes: PreimageBytes = encoded_call.clone();
log_print!("📝 Submitting preimage...");
let note_preimage_tx = quantus_subxt::api::tx().preimage().note_preimage(bounded_bytes);
let preimage_tx_hash = crate::cli::common::submit_transaction(
quantus_client,
from_keypair,
note_preimage_tx,
None,
execution_mode,
)
.await?;
log_success!("✅ Preimage transaction submitted: {:?}", preimage_tx_hash);
type ProposalBounded =
quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded<
quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall,
quantus_subxt::api::runtime_types::sp_runtime::traits::BlakeTwo256,
>;
let preimage_hash_subxt: subxt::utils::H256 = preimage_hash;
let proposal: ProposalBounded =
ProposalBounded::Lookup { hash: preimage_hash_subxt, len: encoded_call.len() as u32 };
let raw_origin_root =
quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Root;
let origin_caller =
quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin_root);
let enactment =
quantus_subxt::api::runtime_types::frame_support::traits::schedule::DispatchTime::After(
0u32,
);
log_print!("🔧 Creating TechReferenda::submit call...");
let submit_call =
quantus_subxt::api::tx()
.tech_referenda()
.submit(origin_caller, proposal, enactment);
if !execution_mode.finalized {
log_print!(
"💡 Note: Waiting for best block (not finalized) due to PoW chain characteristics"
);
}
let tx_hash = crate::cli::common::submit_transaction(
quantus_client,
from_keypair,
submit_call,
None,
execution_mode,
)
.await?;
log_success!("✅ SUCCESS Runtime upgrade proposal submitted! Hash: 0x{}", hex::encode(tx_hash));
Ok(tx_hash)
}
#[derive(Debug, Clone)]
pub struct RuntimeVersionInfo {
pub spec_version: u32,
pub impl_version: u32,
pub transaction_version: u32,
}
pub async fn get_runtime_version(
client: &OnlineClient<ChainConfig>,
) -> crate::error::Result<RuntimeVersionInfo> {
log_verbose!("🔍 Getting runtime version...");
let runtime_version = client.runtime_version();
Ok(RuntimeVersionInfo {
spec_version: runtime_version.spec_version,
impl_version: 1, transaction_version: runtime_version.transaction_version,
})
}
pub async fn calculate_wasm_hash(wasm_code: &[u8]) -> crate::error::Result<String> {
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(wasm_code);
let local_hash = hasher.finalize();
Ok(format!("0x{}", hex::encode(local_hash)))
}
pub async fn handle_runtime_command(
command: RuntimeCommands,
node_url: &str,
execution_mode: crate::cli::common::ExecutionMode,
) -> crate::error::Result<()> {
let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
match command {
RuntimeCommands::Update { wasm_file, from, password, password_file, force } => {
log_print!("🚀 Runtime Management");
log_print!("🔄 Runtime Update");
log_print!(" 📂 WASM file: {}", wasm_file.display().to_string().bright_cyan());
log_print!(" 🔑 Signed by: {}", from.bright_yellow());
if !wasm_file.exists() {
return Err(QuantusError::Generic(format!(
"WASM file not found: {}",
wasm_file.display()
)));
}
if let Some(ext) = wasm_file.extension() {
if ext != "wasm" {
log_print!("⚠️ Warning: File doesn't have .wasm extension");
}
}
let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
log_verbose!("📖 Reading WASM file...");
let wasm_code = fs::read(&wasm_file)
.map_err(|e| QuantusError::Generic(format!("Failed to read WASM file: {e}")))?;
log_print!("📊 WASM file size: {} bytes", wasm_code.len());
update_runtime(&quantus_client, wasm_code, &keypair, force, execution_mode).await?;
log_success!("🎉 Runtime update completed!");
log_print!(
"💡 Note: It may take a few moments for the new runtime version to be reflected."
);
log_print!("💡 Use 'quantus runtime check-version' to verify the new version.");
Ok(())
},
RuntimeCommands::Compare { wasm_file } => {
log_print!("🚀 Runtime Management");
log_print!("🔍 Comparing WASM file with current runtime...");
log_print!(" 📂 Local file: {}", wasm_file.display().to_string().bright_cyan());
if !wasm_file.exists() {
return Err(QuantusError::Generic(format!(
"WASM file not found: {}",
wasm_file.display()
)));
}
let local_wasm = fs::read(&wasm_file)
.map_err(|e| QuantusError::Generic(format!("Failed to read WASM file: {e}")))?;
log_print!("📊 Local WASM size: {} bytes", local_wasm.len());
let current_version = get_runtime_version(quantus_client.client()).await?;
log_print!("📋 Current chain runtime:");
log_print!(" • Spec version: {}", current_version.spec_version);
log_print!(" • Impl version: {}", current_version.impl_version);
log_print!(" • Transaction version: {}", current_version.transaction_version);
let local_hash = calculate_wasm_hash(&local_wasm).await?;
log_print!("🔐 Local WASM SHA256: {}", local_hash.bright_blue());
if let Ok(Some(chain_runtime_hash)) = quantus_client.get_runtime_hash().await {
log_print!("🔐 Chain runtime hash: {}", chain_runtime_hash.bright_yellow());
if local_hash == chain_runtime_hash {
log_success!("✅ Runtime hashes match! The WASM file is identical to the current runtime.");
} else {
log_print!("⚠️ Runtime hashes differ. The WASM file is different from the current runtime.");
}
} else {
log_print!("💡 Chain runtime hash not available for comparison");
}
let filename = wasm_file.file_name().unwrap().to_string_lossy();
log_verbose!("🔍 Parsing filename: {}", filename);
if let Some(version_str) = filename.split('-').nth(2) {
log_verbose!("🔍 Version part: {}", version_str);
if let Some(version_num) = version_str.split('.').next() {
log_verbose!("🔍 Version number: {}", version_num);
let clean_version = version_num.trim_start_matches('v');
log_verbose!("🔍 Clean version: {}", clean_version);
if let Ok(wasm_version) = clean_version.parse::<u32>() {
log_print!("📋 Version comparison:");
log_print!(
" • Local WASM version: {}",
wasm_version.to_string().bright_green()
);
log_print!(
" • Chain runtime version: {}",
current_version.spec_version.to_string().bright_yellow()
);
match wasm_version.cmp(¤t_version.spec_version) {
std::cmp::Ordering::Equal => {
log_success!("✅ Versions match! The WASM file is compatible with the current runtime.");
},
std::cmp::Ordering::Greater => {
log_print!("🔄 The WASM file is newer than the current runtime.");
log_print!(" • This would be an upgrade");
},
std::cmp::Ordering::Less => {
log_print!("⚠️ The WASM file is older than the current runtime.");
log_print!(" • This would be a downgrade");
},
}
} else {
log_print!("⚠️ Could not parse version number from filename");
}
} else {
log_print!("⚠️ Could not extract version number from filename");
}
} else {
log_print!("⚠️ Could not extract version from filename format");
}
log_print!("💡 Use 'quantus system --runtime' for detailed runtime information");
Ok(())
},
}
}