1use crate::{chain::quantus_subxt, error::QuantusError, log_error, log_print, log_verbose};
3use clap::Subcommand;
4use colored::Colorize;
5use std::str::FromStr;
6use subxt::utils::H256;
7
8#[derive(Subcommand, Debug)]
10pub enum PreimageCommands {
11 #[command(name = "status")]
13 Status {
14 #[arg(long)]
16 hash: String,
17 },
18 #[command(name = "get")]
20 Get {
21 #[arg(long)]
23 hash: String,
24 #[arg(long)]
26 len: u32,
27 },
28 #[command(name = "list")]
30 List,
31 #[command(name = "request")]
33 Request {
34 #[arg(long)]
36 hash: String,
37 #[arg(long)]
39 from: String,
40 },
41 #[command(name = "note")]
43 Note {
44 #[arg(long)]
46 content: String,
47 #[arg(long)]
49 from: String,
50 },
51 #[command(name = "create")]
53 Create {
54 #[arg(long)]
56 wasm_file: std::path::PathBuf,
57 #[arg(long)]
59 from: String,
60 #[arg(long)]
62 password: Option<String>,
63 #[arg(long)]
65 password_file: Option<String>,
66 },
67}
68
69pub async fn handle_preimage_command(
71 command: PreimageCommands,
72 node_url: &str,
73 finalized: bool,
74) -> crate::error::Result<()> {
75 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
76
77 match command {
78 PreimageCommands::Status { hash } => {
79 check_preimage_status(&quantus_client, &hash).await?;
80 },
81 PreimageCommands::Get { hash, len } => {
82 get_preimage_content(&quantus_client, &hash, len).await?;
83 },
84 PreimageCommands::List => {
85 list_preimages(&quantus_client).await?;
86 },
87 PreimageCommands::Request { hash, from } => {
88 request_preimage(&quantus_client, &hash, &from, finalized).await?;
89 },
90 PreimageCommands::Note { content, from } => {
91 note_preimage(&quantus_client, &content, &from, finalized).await?;
92 },
93 PreimageCommands::Create { wasm_file, from, password, password_file } => {
94 create_preimage(&quantus_client, wasm_file, &from, password, password_file, finalized)
95 .await?;
96 },
97 }
98
99 Ok(())
100}
101
102async fn check_preimage_status(
104 quantus_client: &crate::chain::client::QuantusClient,
105 hash_str: &str,
106) -> crate::error::Result<()> {
107 let preimage_hash = parse_hash(hash_str)?;
108
109 log_print!("🔍 Checking preimage status for hash: {}", hash_str.bright_cyan());
110
111 let latest_block_hash = quantus_client.get_latest_block().await?;
112 let storage_at = quantus_client.client().storage().at(latest_block_hash);
113
114 let status_addr = quantus_subxt::api::storage().preimage().status_for(preimage_hash);
116 let status_result = storage_at.fetch(&status_addr).await;
117
118 let request_status_addr =
120 quantus_subxt::api::storage().preimage().request_status_for(preimage_hash);
121 let request_status_result = storage_at.fetch(&request_status_addr).await;
122
123 log_print!("📊 Preimage Status Results:");
124 log_print!(" 🔗 Hash: {}", hash_str.bright_yellow());
125
126 match status_result {
127 Ok(Some(status)) => {
128 log_print!(" 📋 StatusFor (Old): {:?}", status);
129 },
130 Ok(None) => {
131 log_print!(" 📋 StatusFor (Old): Not found");
132 },
133 Err(e) => {
134 log_print!(" 📋 StatusFor (Old): Error - {:?}", e);
135 },
136 }
137
138 match request_status_result {
139 Ok(Some(request_status)) => {
140 log_print!(" 📋 RequestStatusFor (New): {:?}", request_status);
141 },
142 Ok(None) => {
143 log_print!(" 📋 RequestStatusFor (New): Not found");
144 },
145 Err(e) => {
146 log_print!(" 📋 RequestStatusFor (New): Error - {:?}", e);
147 },
148 }
149
150 let preimage_addr =
153 quantus_subxt::api::storage().preimage().preimage_for((preimage_hash, 0u32));
154 let preimage_result = storage_at.fetch(&preimage_addr).await;
155
156 match preimage_result {
157 Ok(Some(_)) => {
158 log_print!(" 📦 PreimageFor: Content exists (length 0)");
159 },
160 Ok(None) => {
161 log_print!(" 📦 PreimageFor: No content found (length 0)");
162 },
163 Err(e) => {
164 log_print!(" 📦 PreimageFor: Error - {:?}", e);
165 },
166 }
167
168 Ok(())
169}
170
171async fn get_preimage_content(
173 quantus_client: &crate::chain::client::QuantusClient,
174 hash_str: &str,
175 len: u32,
176) -> crate::error::Result<()> {
177 let preimage_hash = parse_hash(hash_str)?;
178
179 log_print!("📦 Getting preimage content for hash: {}", hash_str.bright_cyan());
180 log_print!(" 📏 Length: {} bytes", len);
181
182 let latest_block_hash = quantus_client.get_latest_block().await?;
183 let storage_at = quantus_client.client().storage().at(latest_block_hash);
184
185 let preimage_addr = quantus_subxt::api::storage().preimage().preimage_for((preimage_hash, len));
186 let preimage_result = storage_at.fetch(&preimage_addr).await;
187
188 match preimage_result {
189 Ok(Some(bounded_vec)) => {
190 log_print!("✅ Preimage content found!");
191 log_print!(" 📏 Actual length: {} bytes", bounded_vec.0.len());
192
193 let content: Vec<u8> = bounded_vec.0;
195
196 let preview_len = std::cmp::min(100, content.len());
198 let preview = &content[..preview_len];
199 log_print!(" 🔍 Preview (first {} bytes):", preview_len);
200 log_print!(" {}", hex::encode(preview).bright_green());
201
202 if content.len() > preview_len {
203 log_print!(" ... ({} more bytes)", content.len() - preview_len);
204 }
205
206 log_verbose!(" 🔧 Attempting to decode as call data...");
208 log_print!(" 📝 Raw content preview (first 100 bytes):");
209 log_print!(
210 " {}",
211 hex::encode(&content[..std::cmp::min(100, content.len())]).bright_green()
212 );
213 },
214 Ok(None) => {
215 log_error!("❌ Preimage content not found for hash {} with length {}", hash_str, len);
216 },
217 Err(e) => {
218 log_error!("❌ Error fetching preimage content: {:?}", e);
219 },
220 }
221
222 Ok(())
223}
224
225async fn list_preimages(
227 quantus_client: &crate::chain::client::QuantusClient,
228) -> crate::error::Result<()> {
229 log_print!("📋 Listing all preimages...");
230
231 let latest_block_hash = quantus_client.get_latest_block().await?;
232 let storage_at = quantus_client.client().storage().at(latest_block_hash);
233
234 let mut preimage_count = 0;
235 let mut unrequested_count = 0;
236 let mut requested_count = 0;
237
238 let preimage_for_addr = quantus_subxt::api::storage().preimage().preimage_for_iter();
240 let mut image_stream = storage_at.iter(preimage_for_addr).await.map_err(|e| {
241 QuantusError::Generic(format!("Failed to iterate preimage contents: {:?}", e))
242 })?;
243
244 while let Some(result) = image_stream.next().await {
245 match result {
246 Ok(entry) => {
247 let key = entry.key_bytes;
248 if key.len() >= 36 {
249 let len_le = &key[key.len() - 4..];
250 let len = u32::from_le_bytes([len_le[0], len_le[1], len_le[2], len_le[3]]);
251 let hash = sp_core::H256::from_slice(&key[key.len() - 36..key.len() - 4]);
252
253 let status = storage_at
254 .fetch(&quantus_subxt::api::storage().preimage().request_status_for(hash))
255 .await
256 .ok()
257 .flatten();
258
259 preimage_count += 1;
260 match status {
261 Some(quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Unrequested { ticket: _, len: status_len }) => {
262 unrequested_count += 1;
263 log_print!(" 🔗 {} (Unrequested, {} bytes)", hash, status_len);
264 },
265 Some(quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Requested { maybe_ticket: _, count, maybe_len }) => {
266 requested_count += 1;
267 let len_str = match maybe_len { Some(l) => format!("{} bytes", l), None => format!("{} bytes (from key)", len) };
268 log_print!(" 🔗 {} (Requested, count: {}, {})", hash, count, len_str);
269 },
270 None => {
271 log_print!(" 🔗 {} (Unknown status, {} bytes)", hash, len);
272 },
273 }
274 }
275 },
276 Err(e) => log_verbose!("⚠️ Error reading preimage content entry: {:?}", e),
277 }
278 }
279
280 log_print!("");
281 log_print!("📊 Preimage Summary:");
282 log_print!(" 📋 Total preimages: {}", preimage_count);
283 log_print!(" 📝 Unrequested: {}", unrequested_count);
284 log_print!(" 📋 Requested: {}", requested_count);
285
286 if preimage_count == 0 {
287 log_print!(" 💡 No preimages found on chain");
288 }
289
290 Ok(())
291}
292
293async fn request_preimage(
295 quantus_client: &crate::chain::client::QuantusClient,
296 hash_str: &str,
297 from_str: &str,
298 finalized: bool,
299) -> crate::error::Result<()> {
300 let preimage_hash = parse_hash(hash_str)?;
301
302 log_print!("🚀 Requesting preimage for hash: {}", hash_str.bright_cyan());
303 log_print!(" 👤 From: {}", from_str.bright_yellow());
304
305 let keypair = crate::wallet::load_keypair_from_wallet(from_str, None, None)?;
307
308 let request_call = quantus_subxt::api::tx().preimage().request_preimage(preimage_hash);
310
311 let tx_hash = crate::cli::common::submit_transaction(
313 quantus_client,
314 &keypair,
315 request_call,
316 None,
317 finalized,
318 )
319 .await?;
320 log_print!("✅ Preimage request transaction submitted: {:?}", tx_hash);
321
322 log_print!("⏳ Waiting for preimage request confirmation...");
324 log_print!("✅ Preimage request confirmed!");
325
326 Ok(())
327}
328
329async fn note_preimage(
331 quantus_client: &crate::chain::client::QuantusClient,
332 content_str: &str,
333 from_str: &str,
334 finalized: bool,
335) -> crate::error::Result<()> {
336 let content = hex::decode(content_str.trim_start_matches("0x"))
337 .map_err(|e| QuantusError::Generic(format!("Invalid hex content: {}", e)))?;
338
339 log_print!("📝 Noting preimage for content length: {} bytes", content.len());
340 log_print!(" 👤 From: {}", from_str.bright_yellow());
341
342 let keypair = crate::wallet::load_keypair_from_wallet(from_str, None, None)?;
344
345 let note_call = quantus_subxt::api::tx().preimage().note_preimage(content);
347
348 let tx_hash = crate::cli::common::submit_transaction(
350 quantus_client,
351 &keypair,
352 note_call,
353 None,
354 finalized,
355 )
356 .await?;
357 log_print!("✅ Preimage note transaction submitted: {:?}", tx_hash);
358
359 log_print!("⏳ Waiting for preimage note confirmation...");
361 log_print!("✅ Preimage note confirmed!");
362
363 Ok(())
364}
365
366async fn create_preimage(
368 quantus_client: &crate::chain::client::QuantusClient,
369 wasm_file: std::path::PathBuf,
370 from_str: &str,
371 password: Option<String>,
372 password_file: Option<String>,
373 finalized: bool,
374) -> crate::error::Result<()> {
375 use qp_poseidon::PoseidonHasher;
376
377 log_print!("📦 Creating preimage from WASM file: {}", wasm_file.display());
378 log_print!(" 👤 From: {}", from_str.bright_yellow());
379
380 if !wasm_file.exists() {
381 return Err(QuantusError::Generic(format!("WASM file not found: {}", wasm_file.display())));
382 }
383
384 let wasm_code = std::fs::read(&wasm_file)
386 .map_err(|e| QuantusError::Generic(format!("Failed to read WASM file: {}", e)))?;
387
388 log_print!("📊 WASM file size: {} bytes", wasm_code.len());
389
390 let keypair = crate::wallet::load_keypair_from_wallet(from_str, password, password_file)?;
392
393 let set_code_payload = quantus_subxt::api::tx().system().set_code(wasm_code.clone());
395 let metadata = quantus_client.client().metadata();
396 let encoded_call = <_ as subxt::tx::Payload>::encode_call_data(&set_code_payload, &metadata)
397 .map_err(|e| QuantusError::Generic(format!("Failed to encode call data: {:?}", e)))?;
398
399 log_verbose!("📝 Encoded call size: {} bytes", encoded_call.len());
400
401 let preimage_hash: sp_core::H256 =
403 <PoseidonHasher as sp_runtime::traits::Hash>::hash(&encoded_call);
404
405 log_print!("🔗 Preimage hash: {:?}", preimage_hash);
406
407 type PreimageBytes = quantus_subxt::api::preimage::calls::types::note_preimage::Bytes;
409 let bounded_bytes: PreimageBytes = encoded_call.clone();
410
411 log_print!("📝 Submitting preimage...");
412 let note_preimage_tx = quantus_subxt::api::tx().preimage().note_preimage(bounded_bytes);
413 let preimage_tx_hash = crate::cli::common::submit_transaction(
414 quantus_client,
415 &keypair,
416 note_preimage_tx,
417 None,
418 finalized,
419 )
420 .await?;
421 log_print!("✅ Preimage transaction submitted: {:?}", preimage_tx_hash);
422
423 log_print!("⏳ Waiting for preimage transaction confirmation...");
425 log_print!("✅ Preimage transaction confirmed!");
426
427 log_print!("🎯 Preimage created successfully!");
428 log_print!(" 🔗 Hash: {:?}", preimage_hash);
429 log_print!(" 📏 Size: {} bytes", encoded_call.len());
430
431 Ok(())
432}
433
434fn parse_hash(hash_str: &str) -> crate::error::Result<H256> {
436 let hash_str = hash_str.trim_start_matches("0x");
437 H256::from_str(hash_str).map_err(|e| {
438 QuantusError::Generic(format!("Invalid hash format: {}. Expected 64 hex characters", e))
439 })
440}