use crate::{chain::quantus_subxt, error::QuantusError, log_error, log_print, log_verbose};
use clap::Subcommand;
use colored::Colorize;
use std::str::FromStr;
use subxt::utils::H256;
#[derive(Subcommand, Debug)]
pub enum PreimageCommands {
#[command(name = "status")]
Status {
#[arg(long)]
hash: String,
},
#[command(name = "get")]
Get {
#[arg(long)]
hash: String,
#[arg(long)]
len: u32,
},
#[command(name = "list")]
List,
#[command(name = "request")]
Request {
#[arg(long)]
hash: String,
#[arg(long)]
from: String,
},
#[command(name = "note")]
Note {
#[arg(long)]
content: String,
#[arg(long)]
from: String,
},
#[command(name = "create")]
Create {
#[arg(long)]
wasm_file: std::path::PathBuf,
#[arg(long)]
from: String,
#[arg(long)]
password: Option<String>,
#[arg(long)]
password_file: Option<String>,
},
}
pub async fn handle_preimage_command(
command: PreimageCommands,
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 {
PreimageCommands::Status { hash } => {
check_preimage_status(&quantus_client, &hash).await?;
},
PreimageCommands::Get { hash, len } => {
get_preimage_content(&quantus_client, &hash, len).await?;
},
PreimageCommands::List => {
list_preimages(&quantus_client).await?;
},
PreimageCommands::Request { hash, from } => {
request_preimage(&quantus_client, &hash, &from, execution_mode).await?;
},
PreimageCommands::Note { content, from } => {
note_preimage(&quantus_client, &content, &from, execution_mode).await?;
},
PreimageCommands::Create { wasm_file, from, password, password_file } => {
create_preimage(
&quantus_client,
wasm_file,
&from,
password,
password_file,
execution_mode,
)
.await?;
},
}
Ok(())
}
async fn check_preimage_status(
quantus_client: &crate::chain::client::QuantusClient,
hash_str: &str,
) -> crate::error::Result<()> {
let preimage_hash = parse_hash(hash_str)?;
log_print!("🔍 Checking preimage status for hash: {}", hash_str.bright_cyan());
let latest_block_hash = quantus_client.get_latest_block().await?;
let storage_at = quantus_client.client().storage().at(latest_block_hash);
let status_addr = quantus_subxt::api::storage().preimage().status_for(preimage_hash);
let status_result = storage_at.fetch(&status_addr).await;
let request_status_addr =
quantus_subxt::api::storage().preimage().request_status_for(preimage_hash);
let request_status_result = storage_at.fetch(&request_status_addr).await;
log_print!("📊 Preimage Status Results:");
log_print!(" 🔗 Hash: {}", hash_str.bright_yellow());
match status_result {
Ok(Some(status)) => {
log_print!(" 📋 StatusFor (Old): {:?}", status);
},
Ok(None) => {
log_print!(" 📋 StatusFor (Old): Not found");
},
Err(e) => {
log_print!(" 📋 StatusFor (Old): Error - {:?}", e);
},
}
match request_status_result {
Ok(Some(request_status)) => {
log_print!(" 📋 RequestStatusFor (New): {:?}", request_status);
},
Ok(None) => {
log_print!(" 📋 RequestStatusFor (New): Not found");
},
Err(e) => {
log_print!(" 📋 RequestStatusFor (New): Error - {:?}", e);
},
}
let preimage_addr =
quantus_subxt::api::storage().preimage().preimage_for((preimage_hash, 0u32));
let preimage_result = storage_at.fetch(&preimage_addr).await;
match preimage_result {
Ok(Some(_)) => {
log_print!(" 📦 PreimageFor: Content exists (length 0)");
},
Ok(None) => {
log_print!(" 📦 PreimageFor: No content found (length 0)");
},
Err(e) => {
log_print!(" 📦 PreimageFor: Error - {:?}", e);
},
}
Ok(())
}
async fn get_preimage_content(
quantus_client: &crate::chain::client::QuantusClient,
hash_str: &str,
len: u32,
) -> crate::error::Result<()> {
let preimage_hash = parse_hash(hash_str)?;
log_print!("📦 Getting preimage content for hash: {}", hash_str.bright_cyan());
log_print!(" 📏 Length: {} bytes", len);
let latest_block_hash = quantus_client.get_latest_block().await?;
let storage_at = quantus_client.client().storage().at(latest_block_hash);
let preimage_addr = quantus_subxt::api::storage().preimage().preimage_for((preimage_hash, len));
let preimage_result = storage_at.fetch(&preimage_addr).await;
match preimage_result {
Ok(Some(bounded_vec)) => {
log_print!("✅ Preimage content found!");
log_print!(" 📏 Actual length: {} bytes", bounded_vec.0.len());
let content: Vec<u8> = bounded_vec.0;
let preview_len = std::cmp::min(100, content.len());
let preview = &content[..preview_len];
log_print!(" 🔍 Preview (first {} bytes):", preview_len);
log_print!(" {}", hex::encode(preview).bright_green());
if content.len() > preview_len {
log_print!(" ... ({} more bytes)", content.len() - preview_len);
}
log_verbose!(" 🔧 Attempting to decode as call data...");
log_print!(" 📝 Raw content preview (first 100 bytes):");
log_print!(
" {}",
hex::encode(&content[..std::cmp::min(100, content.len())]).bright_green()
);
},
Ok(None) => {
log_error!("❌ Preimage content not found for hash {} with length {}", hash_str, len);
},
Err(e) => {
log_error!("❌ Error fetching preimage content: {:?}", e);
},
}
Ok(())
}
async fn list_preimages(
quantus_client: &crate::chain::client::QuantusClient,
) -> crate::error::Result<()> {
log_print!("📋 Listing all preimages...");
let latest_block_hash = quantus_client.get_latest_block().await?;
let storage_at = quantus_client.client().storage().at(latest_block_hash);
let mut preimage_count = 0;
let mut unrequested_count = 0;
let mut requested_count = 0;
let preimage_for_addr = quantus_subxt::api::storage().preimage().preimage_for_iter();
let mut image_stream = storage_at.iter(preimage_for_addr).await.map_err(|e| {
QuantusError::Generic(format!("Failed to iterate preimage contents: {:?}", e))
})?;
while let Some(result) = image_stream.next().await {
match result {
Ok(entry) => {
let key = entry.key_bytes;
if key.len() >= 36 {
let len_le = &key[key.len() - 4..];
let len = u32::from_le_bytes([len_le[0], len_le[1], len_le[2], len_le[3]]);
let hash = sp_core::H256::from_slice(&key[key.len() - 36..key.len() - 4]);
let status = storage_at
.fetch(&quantus_subxt::api::storage().preimage().request_status_for(hash))
.await
.ok()
.flatten();
preimage_count += 1;
match status {
Some(quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Unrequested { ticket: _, len: status_len }) => {
unrequested_count += 1;
log_print!(" 🔗 {} (Unrequested, {} bytes)", hash, status_len);
},
Some(quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Requested { maybe_ticket: _, count, maybe_len }) => {
requested_count += 1;
let len_str = match maybe_len { Some(l) => format!("{} bytes", l), None => format!("{} bytes (from key)", len) };
log_print!(" 🔗 {} (Requested, count: {}, {})", hash, count, len_str);
},
None => {
log_print!(" 🔗 {} (Unknown status, {} bytes)", hash, len);
},
}
}
},
Err(e) => log_verbose!("⚠️ Error reading preimage content entry: {:?}", e),
}
}
log_print!("");
log_print!("📊 Preimage Summary:");
log_print!(" 📋 Total preimages: {}", preimage_count);
log_print!(" 📝 Unrequested: {}", unrequested_count);
log_print!(" 📋 Requested: {}", requested_count);
if preimage_count == 0 {
log_print!(" 💡 No preimages found on chain");
}
Ok(())
}
async fn request_preimage(
quantus_client: &crate::chain::client::QuantusClient,
hash_str: &str,
from_str: &str,
execution_mode: crate::cli::common::ExecutionMode,
) -> crate::error::Result<()> {
let preimage_hash = parse_hash(hash_str)?;
log_print!("🚀 Requesting preimage for hash: {}", hash_str.bright_cyan());
log_print!(" 👤 From: {}", from_str.bright_yellow());
let keypair = crate::wallet::load_keypair_from_wallet(from_str, None, None)?;
let request_call = quantus_subxt::api::tx().preimage().request_preimage(preimage_hash);
let tx_hash = crate::cli::common::submit_transaction(
quantus_client,
&keypair,
request_call,
None,
execution_mode,
)
.await?;
log_print!("✅ Preimage request transaction submitted: {:?}", tx_hash);
log_print!("⏳ Waiting for preimage request confirmation...");
log_print!("✅ Preimage request confirmed!");
Ok(())
}
async fn note_preimage(
quantus_client: &crate::chain::client::QuantusClient,
content_str: &str,
from_str: &str,
execution_mode: crate::cli::common::ExecutionMode,
) -> crate::error::Result<()> {
let content = hex::decode(content_str.trim_start_matches("0x"))
.map_err(|e| QuantusError::Generic(format!("Invalid hex content: {}", e)))?;
log_print!("📝 Noting preimage for content length: {} bytes", content.len());
log_print!(" 👤 From: {}", from_str.bright_yellow());
let keypair = crate::wallet::load_keypair_from_wallet(from_str, None, None)?;
let note_call = quantus_subxt::api::tx().preimage().note_preimage(content);
let tx_hash = crate::cli::common::submit_transaction(
quantus_client,
&keypair,
note_call,
None,
execution_mode,
)
.await?;
log_print!("✅ Preimage note transaction submitted: {:?}", tx_hash);
log_print!("⏳ Waiting for preimage note confirmation...");
log_print!("✅ Preimage note confirmed!");
Ok(())
}
async fn create_preimage(
quantus_client: &crate::chain::client::QuantusClient,
wasm_file: std::path::PathBuf,
from_str: &str,
password: Option<String>,
password_file: Option<String>,
execution_mode: crate::cli::common::ExecutionMode,
) -> crate::error::Result<()> {
log_print!("📦 Creating preimage from WASM file: {}", wasm_file.display());
log_print!(" 👤 From: {}", from_str.bright_yellow());
if !wasm_file.exists() {
return Err(QuantusError::Generic(format!("WASM file not found: {}", wasm_file.display())));
}
let wasm_code = std::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());
let keypair = crate::wallet::load_keypair_from_wallet(from_str, password, password_file)?;
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_verbose!("📝 Encoded call size: {} bytes", encoded_call.len());
let preimage_hash: sp_core::H256 =
<sp_runtime::traits::BlakeTwo256 as sp_runtime::traits::Hash>::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,
&keypair,
note_preimage_tx,
None,
execution_mode,
)
.await?;
log_print!("✅ Preimage transaction submitted: {:?}", preimage_tx_hash);
log_print!("⏳ Waiting for preimage transaction confirmation...");
log_print!("✅ Preimage transaction confirmed!");
log_print!("🎯 Preimage created successfully!");
log_print!(" 🔗 Hash: {:?}", preimage_hash);
log_print!(" 📏 Size: {} bytes", encoded_call.len());
Ok(())
}
fn parse_hash(hash_str: &str) -> crate::error::Result<H256> {
let hash_str = hash_str.trim_start_matches("0x");
H256::from_str(hash_str).map_err(|e| {
QuantusError::Generic(format!("Invalid hash format: {}. Expected 64 hex characters", e))
})
}