Files
pezkuwi-sdk/vendor/pezkuwi-subxt/subxt/examples/zagros_diagnose.rs
T
pezkuwichain 0e809c3a74 sec: remove hardcoded mnemonics, add mainnet tools and subxt examples
- Replace all hardcoded wallet mnemonics with env variable reads
- Add comprehensive e2e test suite (tools/e2e-test/)
- Add zagros validator management tools
- Add subxt examples for mainnet operations
- Update CRITICAL_STATE with zagros testnet and mainnet status
- Fix people chain spec ID and chainspec build script
2026-02-16 08:18:26 +03:00

254 lines
7.6 KiB
Rust

//! Zagros: Diagnose ValidatorsToRetire storage
//!
//! This script:
//! 1. Reads QueuedKeys to get validator #5 (the first one to remove if keeping 4)
//! 2. Deregisters just that ONE validator via ValidatorManager
//! 3. Immediately reads ValidatorsToRetire to verify it was populated
//!
//! Run with:
//! SUDO_MNEMONIC="..." RPC_URL="ws://217.77.6.126:9948" \
//! cargo run --release --example zagros_diagnose -p pezkuwi-subxt
#![allow(missing_docs)]
use pezkuwi_subxt::dynamic::Value;
use pezkuwi_subxt::{OnlineClient, PezkuwiConfig};
use pezkuwi_subxt_signer::bip39::Mnemonic;
use pezkuwi_subxt_signer::sr25519::Keypair;
use std::str::FromStr;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== ZAGROS DEREGISTER DIAGNOSTIC ===\n");
let url = std::env::var("RPC_URL").unwrap_or_else(|_| "ws://217.77.6.126:9948".to_string());
let api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&url).await?;
println!("Connected! specVersion: {}\n", api.runtime_version().spec_version);
// Storage keys
let queued_keys_key =
hex::decode("cec5070d609dd3497f72bde07fc96ba088dcde934c658227ee1dfafcd6e16903")
.unwrap();
let validators_to_retire_key =
hex::decode("084e7f70a295a190e2e33fd3f8cdfcc2b664fa73499821e43a617aa0e82b17b1")
.unwrap();
// Step 1: Check ValidatorsToRetire BEFORE
println!("=== STEP 1: Check ValidatorsToRetire BEFORE deregister ===");
let retire_before = api
.storage()
.at_latest()
.await?
.fetch_raw(validators_to_retire_key.clone())
.await?;
if retire_before.is_empty() {
println!(" ValidatorsToRetire: EMPTY (as expected)\n");
} else {
println!(
" ValidatorsToRetire: {} bytes (already has data!)\n",
retire_before.len()
);
}
// Step 2: Get validator #5 from QueuedKeys
println!("=== STEP 2: Get test validator from QueuedKeys ===");
let raw_data = api
.storage()
.at_latest()
.await?
.fetch_raw(queued_keys_key)
.await?;
let count = (raw_data[0] >> 2) as usize;
let remaining = raw_data.len() - 1;
let entry_size = remaining / count;
println!(" QueuedKeys: {} entries, {} bytes/entry", count, entry_size);
if count <= 4 {
println!(" Only {} validators, nothing to deregister", count);
return Ok(());
}
// Get validator #5 (index 4, the first one to remove)
let test_offset = 1 + 4 * entry_size;
let test_validator = raw_data[test_offset..test_offset + 32].to_vec();
println!(
" Test validator (index 5): 0x{}\n",
hex::encode(&test_validator)
);
// Step 3: Load sudo key and submit deregister for ONE validator
println!("=== STEP 3: Submit deregister for ONE validator ===");
let mnemonic_str =
std::env::var("SUDO_MNEMONIC").expect("SUDO_MNEMONIC environment variable required");
let mnemonic = Mnemonic::from_str(&mnemonic_str)?;
let sudo_keypair = Keypair::from_phrase(&mnemonic, None)?;
println!(
" Sudo account: {}",
sudo_keypair.public_key().to_account_id()
);
// Try TWO different encoding approaches
// Approach A: Value::from_bytes (what we used before)
println!("\n --- Approach A: Value::from_bytes ---");
let val_a = Value::from_bytes(&test_validator);
println!(" Value type: {:?}", val_a);
// Approach B: Value::unnamed_composite with raw bytes
println!("\n --- Approach B: Try AccountId32 from subxt ---");
// In subxt, AccountId32 can be created from [u8; 32]
let mut arr = [0u8; 32];
arr.copy_from_slice(&test_validator);
// Use approach A (same as before) to see if storage gets populated
let validators_value = vec![Value::from_bytes(&test_validator)];
let deregister_call = pezkuwi_subxt::dynamic::tx(
"ValidatorManager",
"deregister_validators",
vec![Value::unnamed_composite(validators_value)],
);
// Print the encoded call data to debug
println!("\n Deregister call value: {:?}", deregister_call.call_data());
let sudo_call =
pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![deregister_call.into_value()]);
println!("\n Submitting sudo(validatorManager.deregister_validators([1 validator]))...");
use pezkuwi_subxt::tx::TxStatus;
let tx_progress = api
.tx()
.sign_and_submit_then_watch_default(&sudo_call, &sudo_keypair)
.await?;
println!(
" TX: 0x{}",
hex::encode(tx_progress.extrinsic_hash().as_ref())
);
let mut progress = tx_progress;
let mut success = false;
loop {
let status = progress.next().await;
match status {
Some(Ok(TxStatus::InBestBlock(details))) => {
match details.wait_for_success().await {
Ok(events) => {
println!(" In best block! Events:");
for event in events.iter() {
if let Ok(ev) = event {
println!(" {}::{}", ev.pallet_name(), ev.variant_name());
if ev.pallet_name() == "Sudo" && ev.variant_name() == "Sudid" {
success = true;
}
if ev.pallet_name() == "ValidatorManager"
&& ev.variant_name() == "ValidatorsDeregistered"
{
// Try to decode the event data
println!(
" >>> ValidatorsDeregistered event!"
);
let bytes = ev.field_bytes();
println!(" >>> Event field bytes ({} bytes): 0x{}", bytes.len(), hex::encode(&bytes[..std::cmp::min(bytes.len(), 128)]));
}
}
}
},
Err(e) => println!(" DISPATCH ERROR: {}", e),
}
break;
},
Some(Ok(TxStatus::Error { message })) => {
println!(" TX ERROR: {}", message);
break;
},
Some(Ok(TxStatus::Invalid { message })) => {
println!(" TX INVALID: {}", message);
break;
},
Some(Ok(TxStatus::Dropped { message })) => {
println!(" TX DROPPED: {}", message);
break;
},
Some(Err(e)) => {
println!(" STREAM ERROR: {}", e);
break;
},
None => {
println!(" STREAM ENDED");
break;
},
_ => {},
}
}
if !success {
println!("\n TX FAILED!");
return Ok(());
}
// Step 4: IMMEDIATELY check ValidatorsToRetire AFTER
println!("\n=== STEP 4: Check ValidatorsToRetire AFTER deregister ===");
// Small delay to ensure state is updated
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
let retire_after = api
.storage()
.at_latest()
.await?
.fetch_raw(validators_to_retire_key.clone())
.await?;
if retire_after.is_empty() {
println!(" ValidatorsToRetire: EMPTY !!! (deregister didn't populate storage!)");
println!(" THIS IS THE BUG!");
} else {
println!(
" ValidatorsToRetire: {} bytes",
retire_after.len()
);
println!(" Raw hex: 0x{}", hex::encode(&retire_after));
// Decode it
let count = (retire_after[0] >> 2) as usize;
println!(" Decoded count: {}", count);
let mut offset = 1;
for i in 0..count {
if offset + 32 <= retire_after.len() {
let account = &retire_after[offset..offset + 32];
println!(" [{}] 0x{}", i + 1, hex::encode(account));
offset += 32;
}
}
// Check if the stored AccountId matches what we sent
if count > 0 && retire_after.len() >= 33 {
let stored = &retire_after[1..33];
if stored == test_validator.as_slice() {
println!("\n MATCH! Stored AccountId matches sent AccountId.");
} else {
println!("\n MISMATCH! Stored AccountId does NOT match!");
println!(" Sent: 0x{}", hex::encode(&test_validator));
println!(" Stored: 0x{}", hex::encode(stored));
}
}
}
// Step 5: Re-read raw storage one more time to triple-check
println!("\n=== STEP 5: Final raw storage check ===");
let retire_final = api
.storage()
.at_latest()
.await?
.fetch_raw(validators_to_retire_key.clone())
.await?;
println!(" ValidatorsToRetire final: {} bytes", retire_final.len());
if !retire_final.is_empty() {
println!(" Raw: 0x{}", hex::encode(&retire_final));
}
println!("\n=== DIAGNOSTIC COMPLETE ===");
Ok(())
}