Files
pezkuwi-sdk/vendor/pezkuwi-subxt/subxt/examples/zagros_sudo.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

185 lines
6.3 KiB
Rust

//! Zagros Testnet: Generic sudo call sender
//!
//! Run with:
//! SUDO_MNEMONIC="..." RPC_URL="ws://..." CALL=setValidatorCount|forceNewEra \
//! cargo run --release --example zagros_sudo
#![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>> {
let url = std::env::var("RPC_URL").unwrap_or_else(|_| "ws://217.77.6.126:9948".to_string());
let call_name = std::env::var("CALL").unwrap_or_else(|_| "setValidatorCount".to_string());
println!("RPC: {}", url);
println!("Call: {}", call_name);
let api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&url).await?;
println!("Connected!");
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: {}", sudo_keypair.public_key().to_account_id());
let inner_call = match call_name.as_str() {
"setValidatorCount" => {
let count: u32 = std::env::var("COUNT")
.unwrap_or_else(|_| "4".to_string())
.parse()?;
println!("Setting validator count to {}", count);
pezkuwi_subxt::dynamic::tx("Staking", "set_validator_count", vec![Value::u128(
count as u128,
)])
},
"forceNewEra" => {
println!("Forcing new era");
pezkuwi_subxt::dynamic::tx("Staking", "force_new_era", Vec::<Value>::new())
},
"forceNewEraAlways" => {
println!("Forcing new era always");
pezkuwi_subxt::dynamic::tx("Staking", "force_new_era_always", Vec::<Value>::new())
},
"setStakingConfigs" => {
// Set min_validator_count to 1 via set_staking_configs
let min_count: u32 = std::env::var("MIN_COUNT")
.unwrap_or_else(|_| "1".to_string())
.parse().unwrap();
println!("Setting staking configs: min_nominator_bond=Noop, min_validator_bond=Noop, max_nominator_count=Noop, max_validator_count=Noop, chill_threshold=Noop, min_commission=Noop");
// Actually we need to set min_validator_count directly
// Let's use a different approach - call set_staking_configs with all Noop except what we need
// ConfigOp enum: 0=Noop, 1=Set(value), 2=Remove
println!("Using setMinValidatorCount instead...");
// Fallthrough to unknown
eprintln!("Use setMinValidatorCount instead");
std::process::exit(1);
},
"setMinValidatorCount" => {
let min_count: u32 = std::env::var("MIN_COUNT")
.unwrap_or_else(|_| "1".to_string())
.parse().unwrap();
println!("Setting minimum validator count to {}", min_count);
// Staking::set_staking_configs sets all params at once
// Instead we should check if there's a direct setter
// In substrate, there's no direct set_minimum_validator_count
// We need to use set_staking_configs with ConfigOp
// ConfigOp: Noop=unnamed_variant("Noop",[]), Set=unnamed_variant("Set",[Value::u128(x)])
let noop = Value::unnamed_variant("Noop", Vec::<Value>::new());
let set_val = Value::unnamed_variant("Set", vec![Value::u128(min_count as u128)]);
pezkuwi_subxt::dynamic::tx("Staking", "set_staking_configs", vec![
noop.clone(), // min_nominator_bond
noop.clone(), // min_validator_bond
noop.clone(), // max_nominator_count
noop.clone(), // max_validator_count
noop.clone(), // chill_threshold
noop.clone(), // min_commission
noop.clone(), // max_staked_rewards (if exists)
])
},
"setStorage" => {
// Set arbitrary storage via sudo(system.setStorage)
let key_hex =
std::env::var("STORAGE_KEY").expect("STORAGE_KEY env var required");
let value_hex =
std::env::var("STORAGE_VALUE").expect("STORAGE_VALUE env var required");
println!("Setting storage key={} value={}", key_hex, value_hex);
let key_bytes = hex::decode(key_hex.trim_start_matches("0x")).unwrap();
let value_bytes = hex::decode(value_hex.trim_start_matches("0x")).unwrap();
// system.setStorage takes Vec<(Key, Value)>
let item = Value::unnamed_composite([
Value::from_bytes(&key_bytes),
Value::from_bytes(&value_bytes),
]);
pezkuwi_subxt::dynamic::tx("System", "set_storage", vec![
Value::unnamed_composite([item]),
])
},
_ => {
eprintln!("Unknown call: {}", call_name);
std::process::exit(1);
},
};
let sudo_tx = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![inner_call.into_value()]);
println!("\nSubmitting...");
// Use sign_and_submit_then_watch to see TX lifecycle
let tx_progress = api
.tx()
.sign_and_submit_then_watch_default(&sudo_tx, &sudo_keypair)
.await?;
println!("TX hash: 0x{}", hex::encode(tx_progress.extrinsic_hash().as_ref()));
println!("Watching TX status (Ctrl+C to abort)...");
// Don't wait for finalization - just wait for in_block
use pezkuwi_subxt::tx::TxStatus;
let mut progress = tx_progress;
loop {
let status = progress.next().await;
match status {
Some(Ok(TxStatus::Validated)) => println!(" Status: Validated (in tx pool)"),
Some(Ok(TxStatus::Broadcasted)) => println!(" Status: Broadcasted"),
Some(Ok(TxStatus::InBestBlock(details))) => {
println!(" Status: InBestBlock {:?}", details.block_hash());
match details.wait_for_success().await {
Ok(events) => {
println!(" TX SUCCESS!");
for event in events.iter() {
if let Ok(ev) = event {
println!(
" Event: {}::{}",
ev.pallet_name(),
ev.variant_name()
);
}
}
},
Err(e) => println!(" TX dispatch error: {}", e),
}
break;
},
Some(Ok(TxStatus::InFinalizedBlock(details))) => {
println!(" Status: Finalized {:?}", details.block_hash());
break;
},
Some(Ok(TxStatus::Error { message })) => {
println!(" Status: ERROR - {}", message);
break;
},
Some(Ok(TxStatus::Invalid { message })) => {
println!(" Status: INVALID - {}", message);
break;
},
Some(Ok(TxStatus::Dropped { message })) => {
println!(" Status: DROPPED - {}", message);
break;
},
Some(Ok(TxStatus::NoLongerInBestBlock)) => {
println!(" Status: No longer in best block");
},
Some(Err(e)) => {
println!(" Stream error: {}", e);
break;
},
None => {
println!(" Stream ended");
break;
},
}
}
println!("\nDone.");
Ok(())
}