From 665e48f47f0609c5972510d57969c6e90e45f553 Mon Sep 17 00:00:00 2001 From: Kurdistan Tech Ministry Date: Thu, 19 Feb 2026 23:00:56 +0300 Subject: [PATCH] chore: gitignore simulation scripts and data Untrack simulation-specific scripts (sim_*, local_sim_*, fix_*, init_ah_staking). Files remain on disk for local use but won't be pushed to repo. Mainnet tools (ah_upgrade, rc_upgrade, assign_coretime, set_ah_client_active) stay tracked. --- .gitignore | 6 + .../subxt/examples/fix_miner_pages.rs | 199 ---- .../subxt/examples/init_ah_staking.rs | 453 ---------- .../subxt/examples/local_sim_setup.rs | 341 ------- .../subxt/examples/sim_fix_stuck_era_v2.rs | 269 ------ .../subxt/examples/sim_full_setup.rs | 850 ------------------ .../subxt/examples/sim_query_state.rs | 87 -- .../subxt/examples/sim_reset_election.rs | 270 ------ 8 files changed, 6 insertions(+), 2469 deletions(-) delete mode 100644 vendor/pezkuwi-subxt/subxt/examples/fix_miner_pages.rs delete mode 100644 vendor/pezkuwi-subxt/subxt/examples/init_ah_staking.rs delete mode 100644 vendor/pezkuwi-subxt/subxt/examples/local_sim_setup.rs delete mode 100644 vendor/pezkuwi-subxt/subxt/examples/sim_fix_stuck_era_v2.rs delete mode 100644 vendor/pezkuwi-subxt/subxt/examples/sim_full_setup.rs delete mode 100644 vendor/pezkuwi-subxt/subxt/examples/sim_query_state.rs delete mode 100644 vendor/pezkuwi-subxt/subxt/examples/sim_reset_election.rs diff --git a/.gitignore b/.gitignore index 0076c37f..04ab3a34 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,12 @@ .DS_Store .env* chainspecs/ + +# Simulation scripts and data (local mainnet sim) +vendor/pezkuwi-subxt/subxt/examples/sim_* +vendor/pezkuwi-subxt/subxt/examples/local_sim_* +vendor/pezkuwi-subxt/subxt/examples/fix_* +vendor/pezkuwi-subxt/subxt/examples/init_ah_staking.rs .idea .local .lycheecache diff --git a/vendor/pezkuwi-subxt/subxt/examples/fix_miner_pages.rs b/vendor/pezkuwi-subxt/subxt/examples/fix_miner_pages.rs deleted file mode 100644 index 0c86cf66..00000000 --- a/vendor/pezkuwi-subxt/subxt/examples/fix_miner_pages.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! Fix MinerPages storage on AH (2 → 32) via sudo XCM -//! -//! MinerPages=2 causes OCW to mine only the last 2 of 32 snapshot pages, -//! missing all voter data in page 0, resulting in WrongWinnerCount. -//! -//! Run: -//! SUDO_MNEMONIC="..." cargo run --release -p pezkuwi-subxt --example fix_miner_pages - -#![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> { - println!("=== FIX MinerPages: 2 → 32 on AH ===\n"); - - // Connect to RC - let rc_url = - std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string()); - let rc_api = OnlineClient::::from_url(&rc_url).await?; - println!("RC connected: spec {}", rc_api.runtime_version().spec_version); - - // Connect to AH (for verification) - let ah_url = - std::env::var("AH_RPC").unwrap_or_else(|_| "ws://127.0.0.1:40944".to_string()); - let ah_api = OnlineClient::::from_url(&ah_url).await?; - println!("AH connected: spec {}", ah_api.runtime_version().spec_version); - - // Sudo keypair - 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: {}\n", sudo_keypair.public_key().to_account_id()); - - // Storage key for MinerPages: twox_128(":MinerPages:") - // parameter_types! { pub storage MinerPages: u32 = 2; } - // Key = twox_128(b":MinerPages:") = 16 bytes - let miner_pages_key: Vec = { - let data = b":MinerPages:"; - // Use xxhash via pezsp_crypto_hashing - pezsp_crypto_hashing::twox_128(data).to_vec() - }; - println!("MinerPages storage key: 0x{}", hex::encode(&miner_pages_key)); - - // Check current value on AH - let current_val = match ah_api - .storage() - .at_latest() - .await? - .fetch_raw(miner_pages_key.clone()) - .await - { - Ok(data) => data, - Err(_) => vec![], - }; - if current_val.is_empty() { - println!("Current MinerPages: not set (default = 2)"); - } else if current_val.len() >= 4 { - let val = u32::from_le_bytes(current_val[..4].try_into().unwrap()); - println!("Current MinerPages: {}", val); - if val == 32 { - println!("Already set to 32 — nothing to do."); - return Ok(()); - } - } - - // Encode System.set_storage call for AH - // System pallet index = 0 (always first) - // set_storage call index = 1 (System::set_storage) - // items: Vec<(Vec, Vec)> - // - // SCALE encoding: - // [pallet_idx: u8][call_idx: u8][compact_len: compact] - // [compact_key_len][key_bytes][compact_val_len][val_bytes] - let new_value: u32 = 32; - let value_bytes = new_value.to_le_bytes(); // [0x20, 0x00, 0x00, 0x00] - - let mut encoded_call: Vec = Vec::new(); - // Pallet System = index 0 - encoded_call.push(0u8); - // Call set_storage = call_index 4 - encoded_call.push(4u8); - // Vec length = 1 item (compact encoding: 1 << 2 | 0 = 4) - encoded_call.push(4u8); // compact(1) - // Key: compact length (16 bytes) = 16 << 2 | 0 = 64 - encoded_call.push(64u8); // compact(16) - encoded_call.extend_from_slice(&miner_pages_key); - // Value: compact length (4 bytes) = 4 << 2 | 0 = 16 - encoded_call.push(16u8); // compact(4) - encoded_call.extend_from_slice(&value_bytes); - - println!( - "\nEncoded call ({} bytes): 0x{}", - encoded_call.len(), - hex::encode(&encoded_call) - ); - - // Build XCM message - let message = Value::unnamed_variant( - "V3", - vec![Value::unnamed_composite(vec![ - Value::named_variant( - "UnpaidExecution", - [ - ("weight_limit", Value::unnamed_variant("Unlimited", vec![])), - ("check_origin", Value::unnamed_variant("None", vec![])), - ], - ), - Value::named_variant( - "Transact", - [ - ("origin_kind", Value::unnamed_variant("Superuser", vec![])), - ( - "require_weight_at_most", - Value::named_composite([ - ("ref_time", Value::u128(5_000_000_000u128)), - ("proof_size", Value::u128(500_000u128)), - ]), - ), - ("call", Value::from_bytes(&encoded_call)), - ], - ), - ])], - ); - - // Destination: V3 { parents: 0, interior: X1(Teyrchain(1000)) } - let dest_val = Value::unnamed_variant( - "V3", - vec![Value::named_composite([ - ("parents", Value::u128(0)), - ( - "interior", - Value::unnamed_variant( - "X1", - vec![Value::unnamed_variant( - "Teyrchain", - vec![Value::u128(1000)], - )], - ), - ), - ])], - ); - - let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest_val, message]); - let sudo_tx = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![xcm_send.into_value()]); - - println!("Submitting sudo(XcmPallet.send(AH, Transact(system.set_storage)))..."); - let progress = rc_api - .tx() - .sign_and_submit_then_watch_default(&sudo_tx, &sudo_keypair) - .await?; - let events = progress.wait_for_finalized_success().await?; - - let sent = events - .iter() - .flatten() - .any(|e| e.pallet_name() == "XcmPallet" && e.variant_name() == "Sent"); - if sent { - println!(" [OK] XCM Sent"); - } else { - println!(" [WARN] No XcmPallet::Sent event found"); - for ev in events.iter().flatten() { - println!(" Event: {}::{}", ev.pallet_name(), ev.variant_name()); - } - } - - // Wait for DMP processing - println!("\nWaiting 30s for DMP processing..."); - tokio::time::sleep(std::time::Duration::from_secs(30)).await; - - // Verify new value - let new_val = match ah_api - .storage() - .at_latest() - .await? - .fetch_raw(miner_pages_key) - .await - { - Ok(data) => data, - Err(_) => vec![], - }; - if new_val.len() >= 4 { - let val = u32::from_le_bytes(new_val[..4].try_into().unwrap()); - println!("New MinerPages value: {}", val); - if val == 32 { - println!("\n✅ SUCCESS: MinerPages = 32"); - } else { - println!("\n❌ UNEXPECTED: MinerPages = {} (expected 32)", val); - } - } else { - println!("❌ FAIL: MinerPages still not set (empty storage)"); - } - - Ok(()) -} diff --git a/vendor/pezkuwi-subxt/subxt/examples/init_ah_staking.rs b/vendor/pezkuwi-subxt/subxt/examples/init_ah_staking.rs deleted file mode 100644 index 43150cd6..00000000 --- a/vendor/pezkuwi-subxt/subxt/examples/init_ah_staking.rs +++ /dev/null @@ -1,453 +0,0 @@ -//! Initialize AH Staking-Async with Validator Data -//! -//! This script populates the AH staking-async pallet with validator data -//! for the local mainnet simulation. Without this, AH cannot plan new eras -//! or send validator sets back to RC. -//! -//! Steps: -//! 1. Give Bob balance on AH (via XCM from RC) -//! 2. Alice + Bob: bond on AH directly -//! 3. Alice + Bob: validate on AH directly -//! 4. set_validator_count(2) via XCM Transact (root on AH) -//! 5. force_new_era() via XCM Transact (root on AH) -//! -//! Run: -//! SUDO_MNEMONIC="..." cargo run --release -p pezkuwi-subxt --example init_ah_staking -//! -//! Optional: -//! RC_RPC="ws://127.0.0.1:9944" (default) -//! AH_RPC="ws://127.0.0.1:40944" (default) - -#![allow(missing_docs)] -use pezkuwi_subxt::dynamic::Value; -use pezkuwi_subxt::tx::TxStatus; -use pezkuwi_subxt::{OnlineClient, PezkuwiConfig}; -use pezkuwi_subxt_signer::bip39::Mnemonic; -use pezkuwi_subxt_signer::sr25519::Keypair; -use std::str::FromStr; - -const HEZ: u128 = 1_000_000_000_000_000_000; // 10^18 - -const AH_PARA_ID: u32 = 1000; - -const ALICE_PUBKEY: [u8; 32] = [ - 0xd4, 0x35, 0x93, 0xc7, 0x15, 0xfd, 0xd3, 0x1c, 0x61, 0x14, 0x1a, 0xbd, 0x04, 0xa9, 0x9f, - 0xd6, 0x82, 0x2c, 0x85, 0x58, 0x85, 0x4c, 0xcd, 0xe3, 0x9a, 0x56, 0x84, 0xe7, 0xa5, 0x6d, - 0xa2, 0x7d, -]; -const BOB_PUBKEY: [u8; 32] = [ - 0x8e, 0xaf, 0x04, 0x15, 0x16, 0x87, 0x73, 0x63, 0x26, 0xc9, 0xfe, 0xa1, 0x7e, 0x25, 0xfc, - 0x52, 0x87, 0x61, 0x36, 0x93, 0xc9, 0x12, 0x90, 0x9c, 0xb2, 0x26, 0xaa, 0x47, 0x94, 0xf2, - 0x6a, 0x48, -]; - -/// AH pallet indices -const AH_BALANCES: u8 = 10; -const AH_STAKING: u8 = 80; - -/// SCALE Compact encoding -fn encode_compact_u128(buf: &mut Vec, val: u128) { - if val < 64 { - buf.push((val as u8) << 2); - } else if val < 16384 { - let v = ((val as u16) << 2) | 0x01; - buf.extend_from_slice(&v.to_le_bytes()); - } else if val < (1u128 << 30) { - let v = ((val as u32) << 2) | 0x02; - buf.extend_from_slice(&v.to_le_bytes()); - } else { - let bytes = val.to_le_bytes(); - let len = 16 - val.leading_zeros() as usize / 8; - let len = if len == 0 { 1 } else { len }; - buf.push(((len as u8 - 4) << 2) | 0x03); - buf.extend_from_slice(&bytes[..len]); - } -} - -/// Encode Balances::force_set_balance(who, #[compact] new_free) -fn encode_force_set_balance(account: &[u8; 32], amount: u128) -> Vec { - let mut data = Vec::new(); - data.push(AH_BALANCES); - data.push(8); // call_index for force_set_balance - // MultiAddress::Id - data.push(0x00); - data.extend_from_slice(account); - encode_compact_u128(&mut data, amount); - data -} - -/// Encode Staking::set_validator_count(#[compact] new) -fn encode_set_validator_count(count: u32) -> Vec { - let mut data = Vec::new(); - data.push(AH_STAKING); - data.push(9); // call_index for set_validator_count - encode_compact_u128(&mut data, count as u128); - data -} - -/// Encode Staking::force_new_era() -fn encode_force_new_era() -> Vec { - let mut data = Vec::new(); - data.push(AH_STAKING); - data.push(13); // call_index for force_new_era - data -} - -/// Build XCM V3 dest + message for a teyrchain transact -fn build_xcm_transact(para_id: u32, encoded_call: &[u8]) -> (Value, Value) { - let dest = Value::unnamed_variant( - "V3", - vec![Value::named_composite([ - ("parents", Value::u128(0)), - ( - "interior", - Value::unnamed_variant( - "X1", - vec![Value::unnamed_variant( - "Teyrchain", - vec![Value::u128(para_id as u128)], - )], - ), - ), - ])], - ); - - let message = Value::unnamed_variant( - "V3", - vec![Value::unnamed_composite(vec![ - Value::named_variant( - "UnpaidExecution", - [ - ("weight_limit", Value::unnamed_variant("Unlimited", vec![])), - ("check_origin", Value::unnamed_variant("None", vec![])), - ], - ), - Value::named_variant( - "Transact", - [ - ("origin_kind", Value::unnamed_variant("Superuser", vec![])), - ( - "require_weight_at_most", - Value::named_composite([ - ("ref_time", Value::u128(5_000_000_000u128)), - ("proof_size", Value::u128(500_000u128)), - ]), - ), - ("call", Value::from_bytes(encoded_call)), - ], - ), - ])], - ); - - (dest, message) -} - -/// Send XCM via sudo on relay chain -async fn sudo_xcm_send( - api: &OnlineClient, - sudo: &Keypair, - dest: Value, - message: Value, - label: &str, -) -> Result> { - let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, message]); - let sudo_tx = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![xcm_send.into_value()]); - - let progress = api - .tx() - .sign_and_submit_then_watch_default(&sudo_tx, sudo) - .await?; - let events = progress.wait_for_finalized_success().await?; - - let mut sent = false; - for event in events.iter() { - let event = event?; - if event.pallet_name() == "XcmPallet" && event.variant_name() == "Sent" { - sent = true; - } - } - if sent { - println!(" [OK] {}", label); - } else { - println!(" [WARN] {} - no XcmPallet::Sent event", label); - } - Ok(sent) -} - -/// Wait for a tx on AH -async fn wait_ah_tx( - mut progress: pezkuwi_subxt::tx::TxProgress>, - label: &str, -) -> Result> { - loop { - let status = progress.next().await; - match status { - Some(Ok(TxStatus::InBestBlock(details))) => { - match details.wait_for_success().await { - Ok(events) => { - let mut ok = false; - for ev in events.iter().flatten() { - if ev.pallet_name() == "System" && ev.variant_name() == "ExtrinsicSuccess" { - ok = true; - } - } - if ok { - println!(" [OK] {}", label); - } else { - println!(" [WARN] {} - no ExtrinsicSuccess", label); - } - return Ok(ok); - }, - Err(e) => { - println!(" [FAIL] {} - dispatch error: {}", label, e); - return Ok(false); - }, - } - }, - Some(Ok(TxStatus::Error { message })) => { - println!(" [FAIL] {} - TX error: {}", label, message); - return Ok(false); - }, - Some(Ok(TxStatus::Invalid { message })) => { - println!(" [FAIL] {} - TX invalid: {}", label, message); - return Ok(false); - }, - Some(Ok(TxStatus::Dropped { message })) => { - println!(" [FAIL] {} - TX dropped: {}", label, message); - return Ok(false); - }, - Some(Err(e)) => { - println!(" [FAIL] {} - stream error: {}", label, e); - return Ok(false); - }, - None => { - println!(" [FAIL] {} - stream ended", label); - return Ok(false); - }, - _ => {}, - } - } -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - println!("=== INITIALIZE AH STAKING-ASYNC ===\n"); - - let rc_url = - std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string()); - let ah_url = - std::env::var("AH_RPC").unwrap_or_else(|_| "ws://127.0.0.1:40944".to_string()); - - // Connect to RC - let rc_api = OnlineClient::::from_url(&rc_url).await?; - println!("RC connected: {}", rc_url); - - // Connect to AH - let ah_api = OnlineClient::::from_url(&ah_url).await?; - println!("AH connected: {}\n", ah_url); - - // Load sudo key - let mnemonic_str = - std::env::var("SUDO_MNEMONIC").expect("SUDO_MNEMONIC environment variable required"); - let mnemonic = Mnemonic::from_str(&mnemonic_str)?; - let sudo = Keypair::from_phrase(&mnemonic, None)?; - - // Alice and Bob keypairs (dev accounts) - let alice = - Keypair::from_uri(&pezkuwi_subxt_signer::SecretUri::from_str("//Alice").unwrap()) - .unwrap(); - let bob = Keypair::from_uri(&pezkuwi_subxt_signer::SecretUri::from_str("//Bob").unwrap()) - .unwrap(); - - println!("Sudo: {}", sudo.public_key().to_account_id()); - println!("Alice: {}", alice.public_key().to_account_id()); - println!("Bob: {}\n", bob.public_key().to_account_id()); - - // ========================================================= - // STEP 1: Give Bob balance on AH (Alice already has from local_sim_setup) - // ========================================================= - println!("--- Step 1: Set Bob balance on AH via XCM ---"); - - let bob_balance_call = encode_force_set_balance(&BOB_PUBKEY, 100_000 * HEZ); - let (dest, msg) = build_xcm_transact(AH_PARA_ID, &bob_balance_call); - sudo_xcm_send(&rc_api, &sudo, dest, msg, "Bob 100K HEZ on AH").await?; - - // Wait for XCM to be processed - println!(" Waiting 24s for DMP processing..."); - tokio::time::sleep(std::time::Duration::from_secs(24)).await; - - // ========================================================= - // STEP 2: Alice bonds on AH - // ========================================================= - println!("\n--- Step 2: Alice bonds 10K HEZ on AH ---"); - - let bond_amount = 10_000 * HEZ; - let alice_bond = pezkuwi_subxt::dynamic::tx( - "Staking", - "bond", - vec![ - Value::u128(bond_amount), - // RewardDestination::Staked = 0 - Value::unnamed_variant("Staked", vec![]), - ], - ); - - let progress = ah_api - .tx() - .sign_and_submit_then_watch_default(&alice_bond, &alice) - .await?; - wait_ah_tx(progress, "Alice bond").await?; - - // ========================================================= - // STEP 3: Bob bonds on AH - // ========================================================= - println!("\n--- Step 3: Bob bonds 10K HEZ on AH ---"); - - let bob_bond = pezkuwi_subxt::dynamic::tx( - "Staking", - "bond", - vec![ - Value::u128(bond_amount), - Value::unnamed_variant("Staked", vec![]), - ], - ); - - let progress = ah_api - .tx() - .sign_and_submit_then_watch_default(&bob_bond, &bob) - .await?; - wait_ah_tx(progress, "Bob bond").await?; - - // ========================================================= - // STEP 4: Alice validates on AH - // ========================================================= - println!("\n--- Step 4: Alice validates on AH ---"); - - let alice_validate = pezkuwi_subxt::dynamic::tx( - "Staking", - "validate", - vec![Value::named_composite([ - // ValidatorPrefs { commission: Perbill(0), blocked: false } - ("commission", Value::u128(0)), - ("blocked", Value::bool(false)), - ])], - ); - - let progress = ah_api - .tx() - .sign_and_submit_then_watch_default(&alice_validate, &alice) - .await?; - wait_ah_tx(progress, "Alice validate").await?; - - // ========================================================= - // STEP 5: Bob validates on AH - // ========================================================= - println!("\n--- Step 5: Bob validates on AH ---"); - - let bob_validate = pezkuwi_subxt::dynamic::tx( - "Staking", - "validate", - vec![Value::named_composite([ - ("commission", Value::u128(0)), - ("blocked", Value::bool(false)), - ])], - ); - - let progress = ah_api - .tx() - .sign_and_submit_then_watch_default(&bob_validate, &bob) - .await?; - wait_ah_tx(progress, "Bob validate").await?; - - // ========================================================= - // STEP 6: set_validator_count(2) via XCM - // ========================================================= - println!("\n--- Step 6: set_validator_count(2) via XCM ---"); - - let svc_call = encode_set_validator_count(2); - let (dest, msg) = build_xcm_transact(AH_PARA_ID, &svc_call); - sudo_xcm_send(&rc_api, &sudo, dest, msg, "ValidatorCount = 2").await?; - - // ========================================================= - // STEP 7: force_new_era via XCM - // ========================================================= - println!("\n--- Step 7: force_new_era via XCM ---"); - - let fne_call = encode_force_new_era(); - let (dest, msg) = build_xcm_transact(AH_PARA_ID, &fne_call); - sudo_xcm_send(&rc_api, &sudo, dest, msg, "ForceEra = ForceNew").await?; - - // Wait for XCM processing - println!("\n Waiting 24s for XCM processing..."); - tokio::time::sleep(std::time::Duration::from_secs(24)).await; - - // ========================================================= - // VERIFY - // ========================================================= - println!("\n--- Verification ---"); - - // Check AH Staking::ValidatorCount - let vc_key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"ValidatorCount").iter()) - .copied() - .collect(); - let vc = ah_api - .storage() - .at_latest() - .await? - .fetch_raw(vc_key) - .await?; - if !vc.is_empty() { - let count = u32::from_le_bytes(vc[..4].try_into().unwrap_or([0; 4])); - println!(" ValidatorCount: {}", count); - } else { - println!(" ValidatorCount: None (NOT SET!)"); - } - - // Check ForceEra - let fe_key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"ForceEra").iter()) - .copied() - .collect(); - let fe = ah_api - .storage() - .at_latest() - .await? - .fetch_raw(fe_key) - .await?; - if !fe.is_empty() { - let modes = ["NotForcing", "ForceNew", "ForceNone", "ForceAlways"]; - let mode = fe[0] as usize; - println!( - " ForceEra: {} ({})", - modes.get(mode).unwrap_or(&"Unknown"), - mode - ); - } - - // Check Bonded (Alice) - let bonded_prefix: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"Bonded").iter()) - .copied() - .collect(); - let alice_hash = pezsp_crypto_hashing::blake2_128(&ALICE_PUBKEY); - let mut alice_bonded_key = bonded_prefix.clone(); - alice_bonded_key.extend_from_slice(&alice_hash); - alice_bonded_key.extend_from_slice(&ALICE_PUBKEY); - let alice_bonded = ah_api - .storage() - .at_latest() - .await? - .fetch_raw(alice_bonded_key) - .await?; - println!(" Alice bonded: {}", !alice_bonded.is_empty()); - - println!("\n=== INITIALIZATION COMPLETE ==="); - println!("\nNext: Wait for AH to plan a new era and send validator set to RC."); - println!("Monitor with: AH ActiveEra, RC StakingAhClient::ValidatorSet"); - - Ok(()) -} diff --git a/vendor/pezkuwi-subxt/subxt/examples/local_sim_setup.rs b/vendor/pezkuwi-subxt/subxt/examples/local_sim_setup.rs deleted file mode 100644 index 992b90d9..00000000 --- a/vendor/pezkuwi-subxt/subxt/examples/local_sim_setup.rs +++ /dev/null @@ -1,341 +0,0 @@ -//! Local Mainnet Simulation Setup -//! -//! Replicates mainnet sudo operations on the local simulation: -//! 1. RC: Set balances for test accounts (Founder, Alice, Bob, TestWallet) -//! 2. AH (via XCM): Set NominationPools config (MinJoinBond, MinCreateBond) -//! 3. AH (via XCM): Set Founder and Alice balances -//! 4. People (via XCM): Set Founder balance + create Nfts Collection 0 for Tiki -//! -//! Run: -//! SUDO_MNEMONIC="..." cargo run --release -p pezkuwi-subxt --example local_sim_setup - -#![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; - -const HEZ: u128 = 1_000_000_000_000_000_000; // 10^18 - -const AH_PARA_ID: u32 = 1000; -const PEOPLE_PARA_ID: u32 = 1004; - -// Known public keys -const FOUNDER_PUBKEY: [u8; 32] = [ - 0x28, 0x92, 0x5e, 0xd8, 0xb4, 0xc0, 0xc9, 0x54, 0x02, 0xb3, 0x15, 0x63, 0x25, 0x1f, 0xd3, - 0x18, 0x41, 0x43, 0x51, 0x11, 0x4b, 0x1c, 0x77, 0x97, 0xee, 0x78, 0x86, 0x66, 0xd2, 0x7d, - 0x63, 0x05, -]; -const ALICE_PUBKEY: [u8; 32] = [ - 0xd4, 0x35, 0x93, 0xc7, 0x15, 0xfd, 0xd3, 0x1c, 0x61, 0x14, 0x1a, 0xbd, 0x04, 0xa9, 0x9f, - 0xd6, 0x82, 0x2c, 0x85, 0x58, 0x85, 0x4c, 0xcd, 0xe3, 0x9a, 0x56, 0x84, 0xe7, 0xa5, 0x6d, - 0xa2, 0x7d, -]; -const BOB_PUBKEY: [u8; 32] = [ - 0x8e, 0xaf, 0x04, 0x15, 0x16, 0x87, 0x73, 0x63, 0x26, 0xc9, 0xfe, 0xa1, 0x7e, 0x25, 0xfc, - 0x52, 0x87, 0x61, 0x36, 0x93, 0xc9, 0x12, 0x90, 0x9c, 0xb2, 0x26, 0xaa, 0x47, 0x94, 0xf2, - 0x6a, 0x48, -]; -const TEST_WALLET_PUBKEY: [u8; 32] = [ - 0x3e, 0x2e, 0xb6, 0x2a, 0x8a, 0x77, 0xf5, 0xfc, 0x15, 0xfd, 0x3d, 0x4c, 0x6d, 0xa5, 0x3f, - 0xa6, 0xdb, 0xf9, 0x0c, 0x15, 0xd3, 0xa0, 0xd1, 0xc8, 0x8c, 0x3b, 0x1d, 0xfc, 0xe2, 0xd7, - 0x1e, 0x63, -]; - -/// Pallet indices (from construct_runtime!) -const AH_BALANCES: u8 = 10; -const AH_NOMINATION_POOLS: u8 = 81; -const PEOPLE_BALANCES: u8 = 10; -const PEOPLE_NFTS: u8 = 60; - -/// Build XCM V3 dest + message for a teyrchain transact -fn build_xcm_transact(para_id: u32, encoded_call: &[u8]) -> (Value, Value) { - let dest = Value::unnamed_variant( - "V3", - vec![Value::named_composite([ - ("parents", Value::u128(0)), - ( - "interior", - Value::unnamed_variant( - "X1", - vec![Value::unnamed_variant( - "Teyrchain", - vec![Value::u128(para_id as u128)], - )], - ), - ), - ])], - ); - - let message = Value::unnamed_variant( - "V3", - vec![Value::unnamed_composite(vec![ - Value::named_variant( - "UnpaidExecution", - [ - ("weight_limit", Value::unnamed_variant("Unlimited", vec![])), - ("check_origin", Value::unnamed_variant("None", vec![])), - ], - ), - Value::named_variant( - "Transact", - [ - ("origin_kind", Value::unnamed_variant("Superuser", vec![])), - ( - "require_weight_at_most", - Value::named_composite([ - ("ref_time", Value::u128(5_000_000_000u128)), - ("proof_size", Value::u128(500_000u128)), - ]), - ), - ("call", Value::from_bytes(encoded_call)), - ], - ), - ])], - ); - - (dest, message) -} - -/// Send XCM via sudo on relay chain -async fn sudo_xcm_send( - api: &OnlineClient, - sudo: &Keypair, - dest: Value, - message: Value, -) -> Result> { - let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, message]); - let sudo_tx = - pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![xcm_send.into_value()]); - - let progress = api - .tx() - .sign_and_submit_then_watch_default(&sudo_tx, sudo) - .await?; - let events = progress.wait_for_finalized_success().await?; - - let mut sent = false; - for event in events.iter() { - let event = event?; - if event.pallet_name() == "XcmPallet" && event.variant_name() == "Sent" { - sent = true; - } - } - Ok(sent) -} - -/// SCALE Compact encoding -fn encode_compact_u128(buf: &mut Vec, val: u128) { - if val < 64 { - buf.push((val as u8) << 2); - } else if val < 16384 { - let v = ((val as u16) << 2) | 0x01; - buf.extend_from_slice(&v.to_le_bytes()); - } else if val < (1u128 << 30) { - let v = ((val as u32) << 2) | 0x02; - buf.extend_from_slice(&v.to_le_bytes()); - } else { - // BigInteger mode - let bytes = val.to_le_bytes(); - let len = 16 - val.leading_zeros() as usize / 8; - let len = if len == 0 { 1 } else { len }; - buf.push(((len as u8 - 4) << 2) | 0x03); - buf.extend_from_slice(&bytes[..len]); - } -} - -/// Encode Balances::force_set_balance(who, #[compact] new_free) -/// call_index = 8, amount is compact-encoded -fn encode_force_set_balance(pallet: u8, account: &[u8; 32], amount: u128) -> Vec { - let mut data = Vec::new(); - data.push(pallet); - data.push(8); // force_set_balance call_index - data.push(0x00); // MultiAddress::Id - data.extend_from_slice(account); - encode_compact_u128(&mut data, amount); - data -} - -/// Encode NominationPools::set_configs (call_index=11) -/// ConfigOp: Noop=0, Set(val)=1, Remove=2 -/// Balance values are plain u128 LE (NOT compact) -fn encode_set_configs(pallet: u8, min_join: u128, min_create: u128) -> Vec { - let mut data = Vec::new(); - data.push(pallet); - data.push(11); // set_configs call_index - - // min_join_bond: ConfigOp::Set(Balance) - data.push(1); // Set variant - data.extend_from_slice(&min_join.to_le_bytes()); - - // min_create_bond: ConfigOp::Set(Balance) - data.push(1); // Set variant - data.extend_from_slice(&min_create.to_le_bytes()); - - // max_pools: ConfigOp::Noop - data.push(0); - // max_members: ConfigOp::Noop - data.push(0); - // max_members_per_pool: ConfigOp::Noop - data.push(0); - // global_max_commission: ConfigOp::Noop - data.push(0); - - data -} - -/// Encode Nfts::force_create(owner, config) - call_index=1 -/// CollectionConfig { settings: u64, max_supply: Option, mint_settings: MintSettings } -/// MintSettings { mint_type: Issuer, price: None, start_block: None, end_block: None, -/// default_item_settings: u64 } -fn encode_nfts_force_create(pallet: u8, owner: &[u8; 32]) -> Vec { - let mut data = Vec::new(); - data.push(pallet); - data.push(1); // force_create call_index - - // owner: MultiAddress::Id - data.push(0x00); - data.extend_from_slice(owner); - - // CollectionConfig: - // settings: CollectionSettings = BitFlags as u64 = 0 (all_enabled) - data.extend_from_slice(&0u64.to_le_bytes()); - // max_supply: Option = None - data.push(0x00); - // mint_settings.mint_type: MintType::Issuer = variant 0 - data.push(0x00); - // mint_settings.price: Option = None - data.push(0x00); - // mint_settings.start_block: Option = None - data.push(0x00); - // mint_settings.end_block: Option = None - data.push(0x00); - // mint_settings.default_item_settings: ItemSettings = BitFlags as u64 = 0 - data.extend_from_slice(&0u64.to_le_bytes()); - - data -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - println!("╔═══════════════════════════════════════════════════════╗"); - println!("║ LOCAL SIM SETUP - Mainnet Duplication ║"); - println!("╚═══════════════════════════════════════════════════════╝\n"); - - let rc_url = - std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string()); - let api = OnlineClient::::from_url(&rc_url).await?; - println!("Connected to RC: {}", rc_url); - - 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: {}\n", sudo_keypair.public_key().to_account_id()); - - // ─── STEP 1: Set balances on RC ─── - println!("=== STEP 1: Set balances on RC ==="); - - let accounts: &[(&str, &[u8; 32], u128)] = &[ - ("Founder", &FOUNDER_PUBKEY, 1_000_000 * HEZ), - ("Alice", &ALICE_PUBKEY, 100_000 * HEZ), - ("Bob", &BOB_PUBKEY, 10_000 * HEZ), - ("TestWallet", &TEST_WALLET_PUBKEY, 10_000 * HEZ), - ]; - - for (name, pubkey, amount) in accounts { - let sudo_tx = pezkuwi_subxt::dynamic::tx( - "Sudo", - "sudo", - vec![pezkuwi_subxt::dynamic::tx( - "Balances", - "force_set_balance", - vec![ - Value::unnamed_variant("Id", vec![Value::from_bytes(*pubkey)]), - Value::u128(*amount), - ], - ) - .into_value()], - ); - - let progress = api - .tx() - .sign_and_submit_then_watch_default(&sudo_tx, &sudo_keypair) - .await?; - let _events = progress.wait_for_finalized_success().await?; - println!(" {}: {} HEZ [OK]", name, amount / HEZ); - } - - // ─── STEP 2: AH NominationPools config via XCM ─── - println!("\n=== STEP 2: AH NominationPools config ==="); - - let set_configs_data = encode_set_configs( - AH_NOMINATION_POOLS, - 10 * HEZ, // MinJoinBond = 10 HEZ - 100 * HEZ, // MinCreateBond = 100 HEZ - ); - println!(" set_configs encoded: {} bytes", set_configs_data.len()); - - let (dest, msg) = build_xcm_transact(AH_PARA_ID, &set_configs_data); - let sent = sudo_xcm_send(&api, &sudo_keypair, dest, msg).await?; - println!( - " MinJoinBond=10 HEZ, MinCreateBond=100 HEZ: {}", - if sent { "XCM Sent!" } else { "WARN: no Sent event" } - ); - - // ─── STEP 3: AH balances via XCM ─── - println!("\n=== STEP 3: AH balances via XCM ==="); - - let ah_balances: &[(&str, &[u8; 32], u128)] = &[ - ("Founder", &FOUNDER_PUBKEY, 1_000_000 * HEZ), - ("Alice", &ALICE_PUBKEY, 100_000 * HEZ), - ]; - - for (name, pubkey, amount) in ah_balances { - let data = encode_force_set_balance(AH_BALANCES, pubkey, *amount); - let (dest, msg) = build_xcm_transact(AH_PARA_ID, &data); - let sent = sudo_xcm_send(&api, &sudo_keypair, dest, msg).await?; - println!( - " AH {}: {} HEZ [{}]", - name, - amount / HEZ, - if sent { "OK" } else { "WARN" } - ); - } - - // ─── STEP 4: People Chain setup via XCM ─── - println!("\n=== STEP 4: People Chain setup ==="); - - // Founder balance - let people_balance_data = - encode_force_set_balance(PEOPLE_BALANCES, &FOUNDER_PUBKEY, 100_000 * HEZ); - let (dest, msg) = build_xcm_transact(PEOPLE_PARA_ID, &people_balance_data); - let sent = sudo_xcm_send(&api, &sudo_keypair, dest, msg).await?; - println!( - " People Founder: 100,000 HEZ [{}]", - if sent { "OK" } else { "WARN" } - ); - - // Nfts Collection 0 (for Tiki) - let nfts_data = encode_nfts_force_create(PEOPLE_NFTS, &FOUNDER_PUBKEY); - println!(" Nfts.force_create encoded: {} bytes", nfts_data.len()); - let (dest, msg) = build_xcm_transact(PEOPLE_PARA_ID, &nfts_data); - let sent = sudo_xcm_send(&api, &sudo_keypair, dest, msg).await?; - println!( - " Nfts Collection 0: [{}]", - if sent { "OK" } else { "WARN" } - ); - - // ─── SUMMARY ─── - println!("\n╔═══════════════════════════════════════════════════════╗"); - println!("║ SETUP COMPLETE ║"); - println!("╠═══════════════════════════════════════════════════════╣"); - println!("║ RC: Founder=1M, Alice=100K, Bob=10K, Test=10K HEZ ║"); - println!("║ AH: MinJoin=10, MinCreate=100 HEZ ║"); - println!("║ Founder=1M, Alice=100K HEZ ║"); - println!("║ People: Founder=100K HEZ, Nfts Collection 0 ║"); - println!("╚═══════════════════════════════════════════════════════╝"); - - Ok(()) -} diff --git a/vendor/pezkuwi-subxt/subxt/examples/sim_fix_stuck_era_v2.rs b/vendor/pezkuwi-subxt/subxt/examples/sim_fix_stuck_era_v2.rs deleted file mode 100644 index 85e70818..00000000 --- a/vendor/pezkuwi-subxt/subxt/examples/sim_fix_stuck_era_v2.rs +++ /dev/null @@ -1,269 +0,0 @@ -//! Fix stuck era on AH — v2 (correct storage keys) -//! -//! The era is stuck because: -//! - CurrentEra=1, ActiveEra=0 → is_planning()=Some(1) -//! - The first election produced empty validator set (no stakers at election time) -//! - RC never activated it → AH can't advance -//! -//! Fix strategy: -//! 1. Use system.killStorage to DELETE CurrentEra key (makes is_planning()=None) -//! 2. Call Staking.force_new_era() to trigger new election -//! Both via XCM Transact from RC. -//! -//! Run: -//! SUDO_MNEMONIC="..." cargo run --release -p pezkuwi-subxt --example sim_fix_stuck_era_v2 - -#![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; - -fn build_xcm_transact(para_id: u32, encoded_call: &[u8]) -> (Value, Value) { - let dest = Value::unnamed_variant( - "V3", - vec![Value::named_composite([ - ("parents", Value::u128(0)), - ( - "interior", - Value::unnamed_variant( - "X1", - vec![Value::unnamed_variant( - "Teyrchain", - vec![Value::u128(para_id as u128)], - )], - ), - ), - ])], - ); - let message = Value::unnamed_variant( - "V3", - vec![Value::unnamed_composite(vec![ - Value::named_variant( - "UnpaidExecution", - [ - ("weight_limit", Value::unnamed_variant("Unlimited", vec![])), - ("check_origin", Value::unnamed_variant("None", vec![])), - ], - ), - Value::named_variant( - "Transact", - [ - ("origin_kind", Value::unnamed_variant("Superuser", vec![])), - ( - "require_weight_at_most", - Value::named_composite([ - ("ref_time", Value::u128(5_000_000_000u128)), - ("proof_size", Value::u128(500_000u128)), - ]), - ), - ("call", Value::from_bytes(encoded_call)), - ], - ), - ])], - ); - (dest, message) -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - println!("=== FIX STUCK ERA ON AH — V2 ===\n"); - - let rc_url = std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string()); - let ah_url = std::env::var("AH_RPC").unwrap_or_else(|_| "ws://127.0.0.1:40944".to_string()); - - // First, connect to AH to get the correct storage key from metadata - println!("--- Step 1: Get correct CurrentEra storage key from AH metadata ---"); - let ah_api = OnlineClient::::from_url(&ah_url).await?; - - // Use the metadata to get the correct storage key for Staking.CurrentEra - let current_era_key = { - let metadata = ah_api.metadata(); - let pallet = metadata.pallet_by_name("Staking").expect("Staking pallet exists"); - let entry = pallet - .storage() - .expect("storage exists") - .entry_by_name("CurrentEra") - .expect("CurrentEra exists"); - - // Build the key: twox_128(pallet_prefix) + twox_128(entry_name) - let mut key = Vec::new(); - key.extend_from_slice(&pezsp_crypto_hashing::twox_128( - pallet.name().as_bytes(), - )); - key.extend_from_slice(&pezsp_crypto_hashing::twox_128( - entry.name().as_bytes(), - )); - println!( - " Pallet name in metadata: {:?}", - pallet.name() - ); - println!( - " Entry name in metadata: {:?}", - entry.name() - ); - println!(" Storage key: 0x{}", hex::encode(&key)); - key - }; - - // Verify the key works by querying current value - let storage = ah_api.storage().at_latest().await?; - let addr = pezkuwi_subxt::dynamic::storage::<(), Value>("Staking", "CurrentEra"); - match storage.entry(addr) { - Ok(entry) => match entry.try_fetch(()).await { - Ok(Some(val)) => println!(" Current value: {:?}", val.decode()), - Ok(None) => println!(" Current value: None"), - _ => {}, - }, - _ => {}, - } - - // Also get ForceEra key - let force_era_key = { - let metadata = ah_api.metadata(); - let pallet = metadata.pallet_by_name("Staking").expect("Staking pallet exists"); - let entry = pallet - .storage() - .expect("storage exists") - .entry_by_name("ForceEra") - .expect("ForceEra exists"); - let mut key = Vec::new(); - key.extend_from_slice(&pezsp_crypto_hashing::twox_128( - pallet.name().as_bytes(), - )); - key.extend_from_slice(&pezsp_crypto_hashing::twox_128( - entry.name().as_bytes(), - )); - println!(" ForceEra key: 0x{}", hex::encode(&key)); - key - }; - - // Connect to RC - println!("\n--- Step 2: Connect to RC and prepare fix ---"); - let rc_api = OnlineClient::::from_url(&rc_url).await?; - - let mnemonic_str = - std::env::var("SUDO_MNEMONIC").expect("SUDO_MNEMONIC environment variable required"); - let sudo = Keypair::from_phrase( - &Mnemonic::from_str(&mnemonic_str)?, - None, - )?; - - // Build the fix: utility.batch_all([ - // system.setStorage([(current_era_key, 0x00000000)]), // CurrentEra = Some(0) - // system.setStorage([(force_era_key, 0x01)]) // ForceEra = ForceNew - // ]) - // Wait — we need to set CurrentEra storage value to u32=0 (4 bytes), NOT Option - // The StorageValue OptionQuery stores just the raw type, Option wrapping is done at decode - - println!("\n--- Step 3: Build and send fix via XCM ---"); - - // Approach: Use system.setStorage to set CurrentEra=0 and ForceEra=ForceNew - let mut call_bytes = Vec::new(); - - // System pallet index on AH = 0, setStorage call_index = 4 - // NOTE: call_index 1 = set_heap_pages (WRONG!), 4 = set_storage (CORRECT) - call_bytes.push(0u8); // System pallet index - call_bytes.push(4u8); // setStorage call index (#[pezpallet::call_index(4)]) - - // items: Vec<(Vec, Vec)>, 2 items - encode_compact_u32(&mut call_bytes, 2); // compact(2) - - // Item 1: CurrentEra = 0 (raw u32 LE, NOT Option-wrapped) - encode_compact_u32(&mut call_bytes, current_era_key.len() as u32); - call_bytes.extend_from_slice(¤t_era_key); - let current_era_value: Vec = vec![0x00, 0x00, 0x00, 0x00]; // u32 LE = 0 - encode_compact_u32(&mut call_bytes, current_era_value.len() as u32); - call_bytes.extend_from_slice(¤t_era_value); - - // Item 2: ForceEra = ForceNew (0x01) - encode_compact_u32(&mut call_bytes, force_era_key.len() as u32); - call_bytes.extend_from_slice(&force_era_key); - let force_era_value: Vec = vec![0x01]; // Forcing::ForceNew - encode_compact_u32(&mut call_bytes, force_era_value.len() as u32); - call_bytes.extend_from_slice(&force_era_value); - - println!("Encoded call ({} bytes): 0x{}", call_bytes.len(), hex::encode(&call_bytes)); - println!(" CurrentEra value: 0x{} (raw u32=0)", hex::encode(¤t_era_value)); - println!(" ForceEra value: 0x{} (ForceNew)", hex::encode(&force_era_value)); - - // Send via XCM - let (dest, msg) = build_xcm_transact(1000, &call_bytes); - let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, msg]); - let sudo_tx = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![xcm_send.into_value()]); - - println!("\nSending XCM Transact to AH..."); - let events = rc_api - .tx() - .sign_and_submit_then_watch_default(&sudo_tx, &sudo) - .await? - .wait_for_finalized_success() - .await?; - - let sent = events - .iter() - .flatten() - .any(|e| e.pallet_name() == "XcmPallet" && e.variant_name() == "Sent"); - println!( - " [{}] setStorage(CurrentEra=0, ForceEra=ForceNew)", - if sent { "OK" } else { "WARN" } - ); - - // Wait and verify - println!("\nWaiting 15 seconds for DMP processing..."); - tokio::time::sleep(std::time::Duration::from_secs(15)).await; - - let ah_api2 = OnlineClient::::from_url(&ah_url).await?; - let storage2 = ah_api2.storage().at_latest().await?; - - let addr = pezkuwi_subxt::dynamic::storage::<(), Value>("Staking", "CurrentEra"); - match storage2.entry(addr) { - Ok(entry) => match entry.try_fetch(()).await { - Ok(Some(val)) => println!("CurrentEra after fix: {:?}", val.decode()), - Ok(None) => println!("CurrentEra after fix: None"), - _ => {}, - }, - _ => {}, - } - - let addr = pezkuwi_subxt::dynamic::storage::<(), Value>("Staking", "ForceEra"); - match storage2.entry(addr) { - Ok(entry) => match entry.try_fetch(()).await { - Ok(Some(val)) => println!("ForceEra after fix: {:?}", val.decode()), - Ok(None) => println!("ForceEra after fix: None"), - _ => {}, - }, - _ => {}, - } - - let addr = pezkuwi_subxt::dynamic::storage::<(), Value>("Staking", "ActiveEra"); - match storage2.entry(addr) { - Ok(entry) => match entry.try_fetch(()).await { - Ok(Some(val)) => println!("ActiveEra: {:?}", val.decode()), - Ok(None) => println!("ActiveEra: None"), - _ => {}, - }, - _ => {}, - } - - println!("\n=== FIX V2 SENT ==="); - println!("Monitor AH logs for:"); - println!(" 1. 'planned None' (CurrentEra cleared)"); - println!(" 2. Election starting (MBE phases)"); - println!(" 3. 'Sending new validator set of size 2' (Alice+Bob)"); - - Ok(()) -} - -fn encode_compact_u32(buf: &mut Vec, val: u32) { - if val < 64 { - buf.push((val as u8) << 2); - } else if val < 16384 { - let v = ((val as u16) << 2) | 0x01; - buf.extend_from_slice(&v.to_le_bytes()); - } else { - let v = (val << 2) | 0x02; - buf.extend_from_slice(&v.to_le_bytes()); - } -} diff --git a/vendor/pezkuwi-subxt/subxt/examples/sim_full_setup.rs b/vendor/pezkuwi-subxt/subxt/examples/sim_full_setup.rs deleted file mode 100644 index ac5893fa..00000000 --- a/vendor/pezkuwi-subxt/subxt/examples/sim_full_setup.rs +++ /dev/null @@ -1,850 +0,0 @@ -//! Full Simulation Setup — Mainnet Simulation Initialization -//! -//! Uses REAL validator stash keys (from wallet file) on the local mainnet simulation. -//! -//! Phase 1: Fund validator stash accounts + test wallets on AH (via XCM) -//! Phase 2: Staking config via XCM (ValidatorCount, min bonds, pool configs) -//! Phase 3: Validators bond + validate on AH (direct tx, real stash keys) -//! Phase 4: Test wallets bond + nominate validators -//! Phase 5: NominationPool — Test06 creates, Test07-10 join -//! Phase 6: Fund People Chain + referrals -//! Phase 7: Force new era -//! Phase 8: Verify state -//! -//! Run: -//! SUDO_MNEMONIC="..." WALLETS_FILE="/path/to/wallets.json" \ -//! cargo run --release -p pezkuwi-subxt --example sim_full_setup -//! -//! Optional: -//! RC_RPC="ws://127.0.0.1:9944" -//! AH_RPC="ws://127.0.0.1:40944" -//! PEOPLE_RPC="ws://127.0.0.1:41944" - -#![allow(missing_docs)] -use pezkuwi_subxt::dynamic::Value; -use pezkuwi_subxt::tx::TxStatus; -use pezkuwi_subxt::{OnlineClient, PezkuwiConfig}; -use pezkuwi_subxt_signer::bip39::Mnemonic; -use pezkuwi_subxt_signer::sr25519::Keypair; -use pezkuwi_subxt_signer::SecretUri; -use std::str::FromStr; - -/// Load a keypair from the wallets JSON array by wallet name -fn wallet_keypair(wallets: &[serde_json::Value], name: &str) -> Keypair { - let wallet = wallets - .iter() - .find(|w| w["name"].as_str() == Some(name)) - .unwrap_or_else(|| panic!("Wallet '{}' not found in wallets file", name)); - let seed = wallet["seed_phrase"] - .as_str() - .unwrap_or_else(|| panic!("Wallet '{}' has no seed_phrase", name)); - let mnemonic = Mnemonic::from_str(seed).unwrap_or_else(|e| { - panic!("Invalid mnemonic for '{}': {}", name, e) - }); - Keypair::from_phrase(&mnemonic, None).unwrap_or_else(|e| { - panic!("Failed to create keypair for '{}': {}", name, e) - }) -} - -const HEZ: u128 = 1_000_000_000_000; // 10^12 — pezkuwichain_runtime_constants::currency::UNITS -const AH_PARA_ID: u32 = 1000; -const PEOPLE_PARA_ID: u32 = 1004; - -// AH pallet indices -const AH_BALANCES: u8 = 10; -const AH_STAKING: u8 = 80; -const AH_NOM_POOLS: u8 = 81; - -// People pallet indices -const PEOPLE_BALANCES: u8 = 10; -const PEOPLE_REFERRAL: u8 = 52; - -/// SCALE Compact -fn encode_compact_u128(buf: &mut Vec, val: u128) { - if val < 64 { - buf.push((val as u8) << 2); - } else if val < 16384 { - let v = ((val as u16) << 2) | 0x01; - buf.extend_from_slice(&v.to_le_bytes()); - } else if val < (1u128 << 30) { - let v = ((val as u32) << 2) | 0x02; - buf.extend_from_slice(&v.to_le_bytes()); - } else { - let bytes = val.to_le_bytes(); - let len = 16 - val.leading_zeros() as usize / 8; - let len = if len == 0 { 1 } else { len }; - buf.push(((len as u8 - 4) << 2) | 0x03); - buf.extend_from_slice(&bytes[..len]); - } -} - -/// SCALE Compact -fn encode_compact_u32(buf: &mut Vec, val: u32) { - encode_compact_u128(buf, val as u128); -} - -/// Encode Balances::force_set_balance(who, amount) -fn encode_force_set_balance(pallet: u8, account: &[u8; 32], amount: u128) -> Vec { - let mut data = Vec::new(); - data.push(pallet); - data.push(8); // call_index - data.push(0x00); // MultiAddress::Id - data.extend_from_slice(account); - encode_compact_u128(&mut data, amount); - data -} - -/// Encode Staking::set_validator_count(new) -fn encode_set_validator_count(count: u32) -> Vec { - let mut data = Vec::new(); - data.push(AH_STAKING); - data.push(9); - encode_compact_u32(&mut data, count); - data -} - -/// Encode Staking::force_new_era() -fn encode_force_new_era() -> Vec { - vec![AH_STAKING, 13] -} - -/// Encode Staking::set_staking_configs(...) -/// All params are ConfigOp where Noop=0, Set=1(value), Remove=2 -/// Balance values are plain u128 LE (NOT compact) -fn encode_set_staking_configs( - min_nominator_bond: Option, - min_validator_bond: Option, -) -> Vec { - let mut data = Vec::new(); - data.push(AH_STAKING); - data.push(22); // call_index for set_staking_configs - - // min_nominator_bond: ConfigOp - match min_nominator_bond { - Some(v) => { - data.push(1); // Set - data.extend_from_slice(&v.to_le_bytes()); - }, - None => data.push(0), // Noop - } - - // min_validator_bond: ConfigOp - match min_validator_bond { - Some(v) => { - data.push(1); - data.extend_from_slice(&v.to_le_bytes()); - }, - None => data.push(0), - } - - // max_nominator_count: Noop - data.push(0); - // max_validator_count: Noop - data.push(0); - // chill_threshold: Noop - data.push(0); - // min_commission: Noop - data.push(0); - // max_staked_rewards: Noop - data.push(0); - - data -} - -/// Encode NominationPools::set_configs(min_join, min_create, ...) -/// Balance values are plain u128 LE (NOT compact) -fn encode_pool_set_configs(min_join: u128, min_create: u128) -> Vec { - let mut data = Vec::new(); - data.push(AH_NOM_POOLS); - data.push(11); // call_index for set_configs - - // min_join_bond: Set(value) - data.push(1); - data.extend_from_slice(&min_join.to_le_bytes()); - - // min_create_bond: Set(value) - data.push(1); - data.extend_from_slice(&min_create.to_le_bytes()); - - // max_pools: Noop - data.push(0); - // max_members: Noop - data.push(0); - // max_members_per_pool: Noop - data.push(0); - // global_max_commission: Noop - data.push(0); - - data -} - -/// Encode Referral::force_confirm_referral(referrer, referred) -fn encode_force_confirm_referral(referrer: &[u8; 32], referred: &[u8; 32]) -> Vec { - let mut data = Vec::new(); - data.push(PEOPLE_REFERRAL); - data.push(1); // call_index for force_confirm_referral - data.extend_from_slice(referrer); - data.extend_from_slice(referred); - data -} - -/// Build XCM V3 transact -fn build_xcm_transact(para_id: u32, encoded_call: &[u8]) -> (Value, Value) { - let dest = Value::unnamed_variant( - "V3", - vec![Value::named_composite([ - ("parents", Value::u128(0)), - ( - "interior", - Value::unnamed_variant( - "X1", - vec![Value::unnamed_variant( - "Teyrchain", - vec![Value::u128(para_id as u128)], - )], - ), - ), - ])], - ); - - let message = Value::unnamed_variant( - "V3", - vec![Value::unnamed_composite(vec![ - Value::named_variant( - "UnpaidExecution", - [ - ("weight_limit", Value::unnamed_variant("Unlimited", vec![])), - ("check_origin", Value::unnamed_variant("None", vec![])), - ], - ), - Value::named_variant( - "Transact", - [ - ("origin_kind", Value::unnamed_variant("Superuser", vec![])), - ( - "require_weight_at_most", - Value::named_composite([ - ("ref_time", Value::u128(5_000_000_000u128)), - ("proof_size", Value::u128(500_000u128)), - ]), - ), - ("call", Value::from_bytes(encoded_call)), - ], - ), - ])], - ); - - (dest, message) -} - -/// Send sudo XCM -async fn sudo_xcm( - api: &OnlineClient, - sudo: &Keypair, - para_id: u32, - encoded_call: &[u8], - label: &str, -) -> Result<(), Box> { - let (dest, msg) = build_xcm_transact(para_id, encoded_call); - let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, msg]); - let sudo_tx = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![xcm_send.into_value()]); - - let progress = api - .tx() - .sign_and_submit_then_watch_default(&sudo_tx, sudo) - .await?; - let events = progress.wait_for_finalized_success().await?; - - let sent = events - .iter() - .flatten() - .any(|e| e.pallet_name() == "XcmPallet" && e.variant_name() == "Sent"); - if sent { - println!(" [OK] {}", label); - } else { - println!(" [WARN] {} — no Sent event", label); - } - Ok(()) -} - -/// Submit AH tx with retry on priority/nonce/invalid errors -async fn submit_ah_tx( - api: &OnlineClient, - tx: &pezkuwi_subxt::tx::DynamicPayload, - signer: &Keypair, - label: &str, -) -> Result> { - for attempt in 0..5 { - if attempt > 0 { - // Wait one block before retry (nonce race condition) - tokio::time::sleep(std::time::Duration::from_secs(12)).await; - } - match api.tx().sign_and_submit_then_watch_default(tx, signer).await { - Ok(progress) => return wait_tx(progress, label).await, - Err(e) => { - let msg = format!("{}", e); - if msg.contains("Priority") || msg.contains("priority") || - msg.contains("Invalid Transaction") || msg.contains("1010") - { - println!( - " [RETRY] {} — {} (attempt {}/5)", - label, - if msg.contains("1010") { "nonce race" } else { "priority" }, - attempt + 1 - ); - continue; - } - println!(" [FAIL] {} — {}", label, e); - return Ok(false); - }, - } - } - println!(" [FAIL] {} — max retries exceeded", label); - Ok(false) -} - -/// Wait for AH tx result -async fn wait_tx( - mut progress: pezkuwi_subxt::tx::TxProgress>, - label: &str, -) -> Result> { - loop { - match progress.next().await { - Some(Ok(TxStatus::InBestBlock(details))) => match details.wait_for_success().await { - Ok(_) => { - println!(" [OK] {}", label); - return Ok(true); - }, - Err(e) => { - let err_str = format!("{:?}", e); - println!(" [FAIL] {} — {}", label, e); - if err_str.contains("DispatchError") { - println!(" [DEBUG] Raw error: {}", err_str); - } - return Ok(false); - }, - }, - Some(Ok(TxStatus::Error { message })) | - Some(Ok(TxStatus::Invalid { message })) | - Some(Ok(TxStatus::Dropped { message })) => { - println!(" [FAIL] {} — {}", label, message); - return Ok(false); - }, - Some(Err(e)) => { - println!(" [FAIL] {} — {}", label, e); - return Ok(false); - }, - None => { - println!(" [FAIL] {} — stream ended", label); - return Ok(false); - }, - _ => {}, - } - } -} - -/// Poll AH balance for an account until nonzero or timeout (seconds) -async fn poll_ah_balance( - api: &OnlineClient, - account: &[u8; 32], - timeout_secs: u64, -) -> Result> { - let start = std::time::Instant::now(); - loop { - let storage = api.storage().at_latest().await?; - let mut key: Vec = pezsp_crypto_hashing::twox_128(b"System") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"Account").iter()) - .copied() - .collect(); - key.extend_from_slice(&pezsp_crypto_hashing::blake2_128(account)); - key.extend_from_slice(account); - let data = storage.fetch_raw(key).await?; - // AccountInfo layout: nonce(4) + consumers(4) + providers(4) + sufficients(4) - // + AccountData { free(16), reserved(16), frozen(16), flags(16) } - if data.len() >= 32 { - let offset = 16; // skip nonce+consumers+providers+sufficients - let free = u128::from_le_bytes(data[offset..offset + 16].try_into().unwrap()); - if free > 0 { - return Ok(free); - } - } - if start.elapsed().as_secs() >= timeout_secs { - return Ok(0); - } - tokio::time::sleep(std::time::Duration::from_secs(6)).await; - } -} - -/// Check if an account is already bonded on AH -/// Note: Bonded storage uses Twox64Concat hasher (NOT Blake2_128Concat) -async fn is_bonded( - api: &OnlineClient, - account: &[u8; 32], -) -> Result> { - let storage = api.storage().at_latest().await?; - let mut key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"Bonded").iter()) - .copied() - .collect(); - // Twox64Concat: twox_64(account) ++ account - let hash = pezsp_crypto_hashing::twox_64(account); - key.extend_from_slice(&hash); - key.extend_from_slice(account); - // fetch_raw returns NoValueFound when key doesn't exist — that means "not bonded" - match storage.fetch_raw(key).await { - Ok(data) => Ok(!data.is_empty()), - Err(_) => Ok(false), - } -} - -/// Generate test keypair from derivation path -fn test_keypair(n: u32) -> Keypair { - let uri = format!("//Test{}", n); - Keypair::from_uri(&SecretUri::from_str(&uri).unwrap()).unwrap() -} - -fn pubkey_bytes(kp: &Keypair) -> [u8; 32] { - let id = kp.public_key().to_account_id(); - let bytes = id.0; - bytes -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - println!("=== FULL SIMULATION SETUP ===\n"); - - let rc_url = std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string()); - let ah_url = - std::env::var("AH_RPC").unwrap_or_else(|_| "ws://127.0.0.1:40944".to_string()); - let _people_url = - std::env::var("PEOPLE_RPC").unwrap_or_else(|_| "ws://127.0.0.1:41944".to_string()); - - let rc_api = OnlineClient::::from_url(&rc_url).await?; - let ah_api = OnlineClient::::from_url(&ah_url).await?; - - println!("RC: {} (spec {})", rc_url, rc_api.runtime_version().spec_version); - println!("AH: {} (spec {})\n", ah_url, ah_api.runtime_version().spec_version); - - let mnemonic_str = - std::env::var("SUDO_MNEMONIC").expect("SUDO_MNEMONIC environment variable required"); - let sudo = Keypair::from_phrase(&Mnemonic::from_str(&mnemonic_str)?, None)?; - - // Load real validator stash keys from wallet file - let wallets_path = std::env::var("WALLETS_FILE") - .unwrap_or_else(|_| "/home/mamostehp/res/MAINNET_WALLETS_20260128_235407.json".to_string()); - let wallets_json: serde_json::Value = - serde_json::from_str(&std::fs::read_to_string(&wallets_path)?)?; - let wallets_arr = wallets_json["wallets"] - .as_array() - .expect("wallets.json must contain a 'wallets' array"); - - let val1 = wallet_keypair(wallets_arr, "Validator_01_Stash"); - let val2 = wallet_keypair(wallets_arr, "Validator_02_Stash"); - - let val1_pub = pubkey_bytes(&val1); - let val2_pub = pubkey_bytes(&val2); - - // Generate 10 test wallets - let test_wallets: Vec<(Keypair, [u8; 32])> = (1..=10) - .map(|i| { - let kp = test_keypair(i); - let pub_bytes = pubkey_bytes(&kp); - (kp, pub_bytes) - }) - .collect(); - - println!("Val01: {}", val1.public_key().to_account_id()); - println!("Val02: {}", val2.public_key().to_account_id()); - for (i, (kp, _)) in test_wallets.iter().enumerate() { - println!("Test{:02}: {}", i + 1, kp.public_key().to_account_id()); - } - - // ========================================================= - // PHASE 1: Fund all accounts on AH via XCM - // ========================================================= - println!("\n========== PHASE 1: Fund AH Accounts =========="); - - // Validator stash accounts: 100K HEZ each - sudo_xcm( - &rc_api, - &sudo, - AH_PARA_ID, - &encode_force_set_balance(AH_BALANCES, &val1_pub, 100_000 * HEZ), - "Val01 100K HEZ on AH", - ) - .await?; - - sudo_xcm( - &rc_api, - &sudo, - AH_PARA_ID, - &encode_force_set_balance(AH_BALANCES, &val2_pub, 100_000 * HEZ), - "Val02 100K HEZ on AH", - ) - .await?; - - // 10 test wallets: 20K HEZ each - for (i, (_, pub_bytes)) in test_wallets.iter().enumerate() { - sudo_xcm( - &rc_api, - &sudo, - AH_PARA_ID, - &encode_force_set_balance(AH_BALANCES, pub_bytes, 20_000 * HEZ), - &format!("Test{:02} 20K HEZ on AH", i + 1), - ) - .await?; - } - - // Wait for DMP processing, verify Val01 balance on AH - println!("\n Waiting for DMP processing (polling Val01 balance on AH)..."); - let val1_balance = poll_ah_balance(&ah_api, &val1_pub, 120).await?; - if val1_balance == 0 { - println!(" [FATAL] Val01 has 0 balance on AH after 120s — DMP not processed. Aborting."); - return Ok(()); - } - println!(" [OK] Val01 AH balance: {} HEZ", val1_balance / HEZ); - - // ========================================================= - // PHASE 2: Staking config via XCM (root operations) - // ========================================================= - println!("\n========== PHASE 2: Staking Config =========="); - - // Set staking configs: min_nominator_bond=100 HEZ, min_validator_bond=1000 HEZ - sudo_xcm( - &rc_api, - &sudo, - AH_PARA_ID, - &encode_set_staking_configs(Some(100 * HEZ), Some(1_000 * HEZ)), - "Staking configs (min_nom=100, min_val=1000 HEZ)", - ) - .await?; - - // Set validator count = 2 - sudo_xcm( - &rc_api, - &sudo, - AH_PARA_ID, - &encode_set_validator_count(2), - "ValidatorCount = 2", - ) - .await?; - - // NominationPools config: MinJoin=10 HEZ, MinCreate=100 HEZ - sudo_xcm( - &rc_api, - &sudo, - AH_PARA_ID, - &encode_pool_set_configs(10 * HEZ, 100 * HEZ), - "NominationPools config (MinJoin=10, MinCreate=100 HEZ)", - ) - .await?; - - println!("\n Waiting 30s for DMP processing..."); - tokio::time::sleep(std::time::Duration::from_secs(30)).await; - - // ========================================================= - // PHASE 3: Bond + Validate (Val01 & Val02 directly on AH) - // ========================================================= - println!("\n========== PHASE 3: Validators Bond + Validate =========="); - - let bond_amount = 50_000 * HEZ; - - // Check if already bonded (idempotency for re-runs) - let val1_already_bonded = is_bonded(&ah_api, &val1_pub).await?; - let val2_already_bonded = is_bonded(&ah_api, &val2_pub).await?; - - if val1_already_bonded { - println!(" [SKIP] Val01 already bonded — skipping bond"); - } else { - let tx = pezkuwi_subxt::dynamic::tx( - "Staking", - "bond", - vec![Value::u128(bond_amount), Value::unnamed_variant("Staked", vec![])], - ); - submit_ah_tx(&ah_api, &tx, &val1, "Val01 bond 50K HEZ").await?; - } - // Always try validate (idempotent — will fail harmlessly if already validating) - let tx = pezkuwi_subxt::dynamic::tx( - "Staking", - "validate", - vec![Value::named_composite([ - ("commission", Value::u128(0)), - ("blocked", Value::bool(false)), - ])], - ); - submit_ah_tx(&ah_api, &tx, &val1, "Val01 validate").await?; - - if val2_already_bonded { - println!(" [SKIP] Val02 already bonded — skipping bond"); - } else { - let tx = pezkuwi_subxt::dynamic::tx( - "Staking", - "bond", - vec![Value::u128(bond_amount), Value::unnamed_variant("Staked", vec![])], - ); - submit_ah_tx(&ah_api, &tx, &val2, "Val02 bond 50K HEZ").await?; - } - // Always try validate - let tx = pezkuwi_subxt::dynamic::tx( - "Staking", - "validate", - vec![Value::named_composite([ - ("commission", Value::u128(0)), - ("blocked", Value::bool(false)), - ])], - ); - submit_ah_tx(&ah_api, &tx, &val2, "Val02 validate").await?; - - // ========================================================= - // PHASE 4: Test wallets nominate validators - // ========================================================= - println!("\n========== PHASE 4: Test Wallets Nominate =========="); - - // Test1-5: bond + nominate both validators - for i in 0..5 { - let (kp, pub_bytes) = &test_wallets[i]; - let nom_amount = 5_000 * HEZ; - - if is_bonded(&ah_api, pub_bytes).await? { - println!(" [SKIP] Test{:02} already bonded — skipping bond", i + 1); - } else { - // Bond - let tx = pezkuwi_subxt::dynamic::tx( - "Staking", - "bond", - vec![Value::u128(nom_amount), Value::unnamed_variant("Staked", vec![])], - ); - submit_ah_tx(&ah_api, &tx, kp, &format!("Test{:02} bond 5K HEZ", i + 1)).await?; - } - - // Always try nominate (idempotent — will update if already nominating) - let tx = pezkuwi_subxt::dynamic::tx( - "Staking", - "nominate", - vec![Value::unnamed_composite(vec![ - Value::unnamed_variant( - "Id", - vec![Value::from_bytes(&val1_pub)], - ), - Value::unnamed_variant( - "Id", - vec![Value::from_bytes(&val2_pub)], - ), - ])], - ); - submit_ah_tx(&ah_api, &tx, kp, &format!("Test{:02} nominate Val01+Val02", i + 1)).await?; - } - - // ========================================================= - // PHASE 5: NominationPool — Test6 creates, Test7-10 join - // ========================================================= - println!("\n========== PHASE 5: Nomination Pool =========="); - - let (pool_creator, pool_creator_pub) = &test_wallets[5]; // Test06 - - // Check if pool already exists (LastPoolId storage) - let pool_key: Vec = pezsp_crypto_hashing::twox_128(b"NominationPools") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"LastPoolId").iter()) - .copied() - .collect(); - let pool_exists = match ah_api.storage().at_latest().await?.fetch_raw(pool_key).await { - Ok(d) if !d.is_empty() && d.len() >= 4 => { - let id = u32::from_le_bytes(d[..4].try_into().unwrap()); - id >= 1 - }, - _ => false, - }; - - if pool_exists { - println!(" [SKIP] Pool already exists — skipping create+join"); - } else { - // Test06 creates pool with 1000 HEZ - let tx = pezkuwi_subxt::dynamic::tx( - "NominationPools", - "create", - vec![ - Value::u128(1_000 * HEZ), // amount - Value::unnamed_variant("Id", vec![Value::from_bytes(pool_creator_pub)]), // root - Value::unnamed_variant("Id", vec![Value::from_bytes(pool_creator_pub)]), // nominator - Value::unnamed_variant("Id", vec![Value::from_bytes(pool_creator_pub)]), // bouncer - ], - ); - submit_ah_tx(&ah_api, &tx, pool_creator, "Test06 create pool (1000 HEZ)").await?; - - // Pool nominate Val01 - let tx = pezkuwi_subxt::dynamic::tx( - "NominationPools", - "nominate", - vec![ - Value::u128(1), // pool_id - Value::unnamed_composite(vec![Value::from_bytes(&val1_pub)]), - ], - ); - submit_ah_tx(&ah_api, &tx, pool_creator, "Pool 1 nominate Val01").await?; - - // Test07-10 join pool - for i in 6..10 { - let (kp, _) = &test_wallets[i]; - let tx = pezkuwi_subxt::dynamic::tx( - "NominationPools", - "join", - vec![ - Value::u128(500 * HEZ), // amount - Value::u128(1), // pool_id - ], - ); - submit_ah_tx(&ah_api, &tx, kp, &format!("Test{:02} join pool 1 (500 HEZ)", i + 1)) - .await?; - } - } - - // ========================================================= - // PHASE 6: Fund People Chain + Referrals - // ========================================================= - println!("\n========== PHASE 6: People Chain + Referrals =========="); - - // Fund test wallets on People Chain - for (i, (_, pub_bytes)) in test_wallets.iter().enumerate() { - sudo_xcm( - &rc_api, - &sudo, - PEOPLE_PARA_ID, - &encode_force_set_balance(PEOPLE_BALANCES, pub_bytes, 1_000 * HEZ), - &format!("Test{:02} 1K HEZ on People", i + 1), - ) - .await?; - } - - println!("\n Waiting 30s for DMP processing..."); - tokio::time::sleep(std::time::Duration::from_secs(30)).await; - - // Referral chain: Test01 refers Test02, Test02 refers Test03, etc. - // Using force_confirm_referral via XCM (root) since KYC not setup in sim - for i in 0..9 { - let referrer_pub = &test_wallets[i].1; - let referred_pub = &test_wallets[i + 1].1; - sudo_xcm( - &rc_api, - &sudo, - PEOPLE_PARA_ID, - &encode_force_confirm_referral(referrer_pub, referred_pub), - &format!("Test{:02} refers Test{:02}", i + 1, i + 2), - ) - .await?; - } - - // ========================================================= - // PHASE 7: Force new era to trigger validator set flow - // ========================================================= - println!("\n========== PHASE 7: Trigger Era Rotation =========="); - - sudo_xcm( - &rc_api, - &sudo, - AH_PARA_ID, - &encode_force_new_era(), - "ForceEra = ForceNew on AH", - ) - .await?; - - println!("\n Waiting 30s for era planning..."); - tokio::time::sleep(std::time::Duration::from_secs(30)).await; - - // ========================================================= - // PHASE 8: Verify - // ========================================================= - println!("\n========== VERIFICATION =========="); - - let storage = ah_api.storage().at_latest().await?; - - // ValidatorCount - let key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"ValidatorCount").iter()) - .copied() - .collect(); - let data = storage.fetch_raw(key).await?; - if data.len() >= 4 { - let n = u32::from_le_bytes(data[..4].try_into().unwrap()); - println!(" AH ValidatorCount: {}", n); - } else { - println!(" AH ValidatorCount: NOT SET"); - } - - // ForceEra - let key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"ForceEra").iter()) - .copied() - .collect(); - let data = storage.fetch_raw(key).await?; - if !data.is_empty() { - let modes = ["NotForcing", "ForceNew", "ForceNone", "ForceAlways"]; - println!( - " AH ForceEra: {}", - modes.get(data[0] as usize).unwrap_or(&"?") - ); - } - - // ActiveEra - let key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"ActiveEra").iter()) - .copied() - .collect(); - let data = storage.fetch_raw(key).await?; - if data.len() >= 4 { - let era = u32::from_le_bytes(data[..4].try_into().unwrap()); - println!(" AH ActiveEra: {}", era); - } - - // CurrentEra - let key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"CurrentEra").iter()) - .copied() - .collect(); - let data = storage.fetch_raw(key).await?; - if data.len() >= 4 { - let era = u32::from_le_bytes(data[..4].try_into().unwrap()); - println!(" AH CurrentEra: {}", era); - } - - // Bonded check (Twox64Concat hasher) - let bonded_prefix: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"Bonded").iter()) - .copied() - .collect(); - let mut val1_key = bonded_prefix.clone(); - val1_key.extend_from_slice(&pezsp_crypto_hashing::twox_64(&val1_pub)); - val1_key.extend_from_slice(&val1_pub); - let val1_bonded = match storage.fetch_raw(val1_key).await { - Ok(d) => !d.is_empty(), - Err(_) => false, - }; - println!(" Val01 bonded: {}", val1_bonded); - - let mut val2_key = bonded_prefix; - val2_key.extend_from_slice(&pezsp_crypto_hashing::twox_64(&val2_pub)); - val2_key.extend_from_slice(&val2_pub); - let val2_bonded = match storage.fetch_raw(val2_key).await { - Ok(d) => !d.is_empty(), - Err(_) => false, - }; - println!(" Val02 bonded: {}", val2_bonded); - - println!("\n=== SETUP COMPLETE ==="); - println!("\nExpected flow:"); - println!(" 1. AH staking plans new era → sends ValidatorSet to RC via XCM"); - println!(" 2. RC receives ValidatorSet → stores it"); - println!(" 3. RC session end → sends SessionReport with activation_timestamp to AH"); - println!(" 4. AH ActiveEra advances"); - println!("\nMonitor: watch AH ActiveEra, RC StakingAhClient::ValidatorSet"); - println!("If era doesn't advance after 5+ sessions, investigate AH staking election."); - - Ok(()) -} diff --git a/vendor/pezkuwi-subxt/subxt/examples/sim_query_state.rs b/vendor/pezkuwi-subxt/subxt/examples/sim_query_state.rs deleted file mode 100644 index 8f63ead8..00000000 --- a/vendor/pezkuwi-subxt/subxt/examples/sim_query_state.rs +++ /dev/null @@ -1,87 +0,0 @@ -//! Query AH staking state using subxt dynamic storage API -//! -//! Run: -//! cargo run --release -p pezkuwi-subxt --example sim_query_state - -#![allow(missing_docs)] -use pezkuwi_subxt::dynamic::Value; -use pezkuwi_subxt::{OnlineClient, PezkuwiConfig}; - -#[tokio::main] -async fn main() -> Result<(), Box> { - let ah_url = std::env::var("AH_RPC").unwrap_or_else(|_| "ws://127.0.0.1:40944".to_string()); - let api = OnlineClient::::from_url(&ah_url).await?; - - println!("=== AH STAKING STATE ===\n"); - - let storage = api.storage().at_latest().await?; - - let items = [ - ("Staking", "CurrentEra"), - ("Staking", "ActiveEra"), - ("Staking", "ForceEra"), - ("Staking", "ValidatorCount"), - ("Staking", "CounterForValidators"), - ("Staking", "CounterForNominators"), - ("Staking", "NextElectionPage"), - ("Staking", "OutgoingValidatorSet"), - ("Staking", "ElectableStashes"), - ("Staking", "BondedEras"), - ("Staking", "MinimumValidatorCount"), - ]; - - for (pallet, name) in &items { - let addr = pezkuwi_subxt::dynamic::storage::<(), Value>(*pallet, *name); - match storage.entry(addr) { - Ok(entry) => match entry.try_fetch(()).await { - Ok(Some(val)) => { - let decoded = val.decode(); - println!("{}.{} = {:?}", pallet, name, decoded); - }, - Ok(None) => println!("{}.{} = None", pallet, name), - Err(e) => println!("{}.{} = ", pallet, name, e), - }, - Err(e) => println!("{}.{} = ", pallet, name, e), - } - } - - println!(); - - let mbe_items = [ - ("MultiBlockElection", "CurrentPhase"), - ("MultiBlockElection", "Round"), - ]; - - for (pallet, name) in &mbe_items { - let addr = pezkuwi_subxt::dynamic::storage::<(), Value>(*pallet, *name); - match storage.entry(addr) { - Ok(entry) => match entry.try_fetch(()).await { - Ok(Some(val)) => println!("{}.{} = {:?}", pallet, name, val.decode()), - Ok(None) => println!("{}.{} = None", pallet, name), - Err(e) => println!("{}.{} = ", pallet, name, e), - }, - Err(e) => println!("{}.{} = ", pallet, name, e), - } - } - - println!(); - - let rc_items = [ - ("StakingRcClient", "LastSessionReportEndingIndex"), - ("StakingRcClient", "Mode"), - ]; - - for (pallet, name) in &rc_items { - let addr = pezkuwi_subxt::dynamic::storage::<(), Value>(*pallet, *name); - match storage.entry(addr) { - Ok(entry) => match entry.try_fetch(()).await { - Ok(Some(val)) => println!("{}.{} = {:?}", pallet, name, val.decode()), - Ok(None) => println!("{}.{} = None", pallet, name), - Err(e) => println!("{}.{} = ", pallet, name, e), - }, - Err(e) => println!("{}.{} = ", pallet, name, e), - } - } - - Ok(()) -} diff --git a/vendor/pezkuwi-subxt/subxt/examples/sim_reset_election.rs b/vendor/pezkuwi-subxt/subxt/examples/sim_reset_election.rs deleted file mode 100644 index 5932c508..00000000 --- a/vendor/pezkuwi-subxt/subxt/examples/sim_reset_election.rs +++ /dev/null @@ -1,270 +0,0 @@ -//! Reset MBE election on AH: ForceRotateRound + ForceNew -//! -//! When the snapshot was taken with stale data (e.g. only 1 validator), -//! the election cycles forever with WrongWinnerCount. This script: -//! 1. Calls manage(ForceRotateRound) to kill the current round and snapshot -//! 2. Calls Staking.force_new_era() to trigger a fresh election with new snapshot -//! -//! Run: -//! SUDO_MNEMONIC="..." cargo run --release -p pezkuwi-subxt --example sim_reset_election - -#![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; - -fn build_xcm_transact(para_id: u32, encoded_call: &[u8]) -> (Value, Value) { - let dest = Value::unnamed_variant( - "V3", - vec![Value::named_composite([ - ("parents", Value::u128(0)), - ( - "interior", - Value::unnamed_variant( - "X1", - vec![Value::unnamed_variant( - "Teyrchain", - vec![Value::u128(para_id as u128)], - )], - ), - ), - ])], - ); - - let message = Value::unnamed_variant( - "V3", - vec![Value::unnamed_composite(vec![ - Value::named_variant( - "UnpaidExecution", - [ - ("weight_limit", Value::unnamed_variant("Unlimited", vec![])), - ("check_origin", Value::unnamed_variant("None", vec![])), - ], - ), - Value::named_variant( - "Transact", - [ - ("origin_kind", Value::unnamed_variant("Superuser", vec![])), - ( - "require_weight_at_most", - Value::named_composite([ - ("ref_time", Value::u128(5_000_000_000u128)), - ("proof_size", Value::u128(500_000u128)), - ]), - ), - ("call", Value::from_bytes(encoded_call)), - ], - ), - ])], - ); - - (dest, message) -} - -async fn sudo_xcm( - api: &OnlineClient, - sudo: &Keypair, - para_id: u32, - encoded_call: &[u8], - label: &str, -) -> Result<(), Box> { - let (dest, msg) = build_xcm_transact(para_id, encoded_call); - let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, msg]); - let sudo_tx = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![xcm_send.into_value()]); - - let progress = api - .tx() - .sign_and_submit_then_watch_default(&sudo_tx, sudo) - .await?; - let events = progress.wait_for_finalized_success().await?; - - let sent = events - .iter() - .flatten() - .any(|e| e.pallet_name() == "XcmPallet" && e.variant_name() == "Sent"); - if sent { - println!(" [OK] {}", label); - } else { - println!(" [WARN] {} — no Sent event", label); - for ev in events.iter().flatten() { - println!(" Event: {}::{}", ev.pallet_name(), ev.variant_name()); - } - } - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<(), Box> { - println!("=== RESET MBE ELECTION ON AH ===\n"); - - let rc_url = - std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string()); - let rc_api = OnlineClient::::from_url(&rc_url).await?; - println!("RC connected: spec {}", rc_api.runtime_version().spec_version); - - let ah_url = - std::env::var("AH_RPC").unwrap_or_else(|_| "ws://127.0.0.1:40944".to_string()); - let ah_api = OnlineClient::::from_url(&ah_url).await?; - println!("AH connected: spec {}", ah_api.runtime_version().spec_version); - - 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: {}\n", sudo_keypair.public_key().to_account_id()); - - // Check current MBE state - let phase_key: Vec = pezsp_crypto_hashing::twox_128(b"MultiBlockElection") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"CurrentPhase").iter()) - .copied() - .collect(); - let phase_val = match ah_api - .storage() - .at_latest() - .await? - .fetch_raw(phase_key) - .await - { - Ok(data) => data, - Err(_) => vec![], - }; - println!("Current phase raw: 0x{}", hex::encode(&phase_val)); - - // Step 1: ForceRotateRound - // MultiBlockElection pallet index = 85 - // manage call_index = 0 - // ManagerOperation::ForceRotateRound = variant 0 - println!("\n--- Step 1: ForceRotateRound ---"); - let force_rotate_call: Vec = vec![85, 0, 0]; // [pallet=85, call=0, variant=0] - sudo_xcm( - &rc_api, - &sudo_keypair, - 1000, - &force_rotate_call, - "manage(ForceRotateRound)", - ) - .await?; - - println!("Waiting 30s for DMP processing..."); - tokio::time::sleep(std::time::Duration::from_secs(30)).await; - - // Verify phase is now Off - let phase_key2: Vec = pezsp_crypto_hashing::twox_128(b"MultiBlockElection") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"CurrentPhase").iter()) - .copied() - .collect(); - let phase_val2 = match ah_api - .storage() - .at_latest() - .await? - .fetch_raw(phase_key2) - .await - { - Ok(data) => data, - Err(_) => vec![], - }; - println!("Phase after ForceRotateRound: 0x{}", hex::encode(&phase_val2)); - - // Step 2: force_new_era via XCM to AH Staking - println!("\n--- Step 2: ForceEra = ForceNew ---"); - // Staking pallet index on AH - let staking_idx: u8 = { - // Look up from runtime: Staking = 80 - 80 - }; - // force_new_era call_index needs to be checked - // For now, we use the sim_full_setup approach: set ForceEra storage directly - // ForceEra key: twox128(Staking) + twox128(ForceEra) - let force_era_key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"ForceEra").iter()) - .copied() - .collect(); - - // ForceNew = variant index 2 in Forcing enum - // enum Forcing { NotForcing=0, ForceNew=1, ForceNone=2, ForceAlways=3 } - // Actually, let me check: from sim_full_setup output "ForceEra: ForceNew" was set. - // In the SCALE encoding: ForceNew = 1 - let force_new_value: Vec = vec![1u8]; // ForceNew = variant index 1 - - // Encode system.set_storage call - let mut set_storage_call: Vec = Vec::new(); - set_storage_call.push(0u8); // System pallet = 0 - set_storage_call.push(4u8); // set_storage call_index = 4 - set_storage_call.push(4u8); // compact(1) = 1 item - // Key - set_storage_call.push((force_era_key.len() as u8) << 2); // compact(32) = 128 - set_storage_call.extend_from_slice(&force_era_key); - // Value - set_storage_call.push((force_new_value.len() as u8) << 2); // compact(1) = 4 - set_storage_call.extend_from_slice(&force_new_value); - - println!("ForceEra key: 0x{}", hex::encode(&force_era_key)); - println!( - "Encoded call ({} bytes): 0x{}", - set_storage_call.len(), - hex::encode(&set_storage_call) - ); - - sudo_xcm( - &rc_api, - &sudo_keypair, - 1000, - &set_storage_call, - "system.set_storage(ForceEra = ForceNew)", - ) - .await?; - - println!("Waiting 30s for DMP processing..."); - tokio::time::sleep(std::time::Duration::from_secs(30)).await; - - // Verify - let force_era_val = match ah_api - .storage() - .at_latest() - .await? - .fetch_raw(force_era_key) - .await - { - Ok(data) => data, - Err(_) => vec![], - }; - let force_era_str = if force_era_val.is_empty() { - "empty (default NotForcing)" - } else { - match force_era_val[0] { - 0 => "NotForcing", - 1 => "ForceNew", - 2 => "ForceNone", - 3 => "ForceAlways", - _ => "Unknown", - } - }; - println!("ForceEra: {}", force_era_str); - - // Check validators - let cv_key: Vec = pezsp_crypto_hashing::twox_128(b"Staking") - .iter() - .chain(pezsp_crypto_hashing::twox_128(b"CounterForValidators").iter()) - .copied() - .collect(); - let cv_val = match ah_api.storage().at_latest().await?.fetch_raw(cv_key).await { - Ok(data) => data, - Err(_) => vec![], - }; - if cv_val.len() >= 4 { - let count = u32::from_le_bytes(cv_val[..4].try_into().unwrap()); - println!("CounterForValidators: {}", count); - } - - println!("\n=== DONE ==="); - println!("Next: AH will take a new snapshot at session boundary with 2 validators."); - println!("Then: MBE election should produce a valid 2-winner solution."); - println!("Monitor: sim_query_state → watch CurrentPhase and ActiveEra"); - - Ok(()) -}