fix(rc-runtime): remove old pezpallet_staking and related pallets for RC 1_020_006

StakingAhClient (index 67) is now Active — old NPoS staking on RC is unused.

Removed pallets:
- Staking (pezpallet_staking, index 9)
- FastUnstake (pezpallet_fast_unstake, index 15)
- VoterBagsList (pezpallet_bags_list Instance1, index 100)

Changes:
- Added NoopFallback struct for ah_client::Config::Fallback
- Updated validator_manager to use StakingAhClient
- Added RemovePallet migrations for on-chain storage cleanup
- Removed StakingConfig from genesis presets
- Cleaned up unused imports and dependencies
- Updated upgrade scripts (ah_upgrade, rc_upgrade, people_upgrade, set_ah_client_active)
This commit is contained in:
2026-02-21 00:22:12 +03:00
parent 90a6917616
commit a516ffec65
9 changed files with 776 additions and 361 deletions
+212 -127
View File
@@ -1,11 +1,12 @@
//! Asset Hub Runtime Upgrade (Local Simulation)
//! Asset Hub Runtime Upgrade (Mainnet)
//!
//! Two-step process:
//! 1. RC → XCM → AH: System.authorize_upgrade(blake2_256(wasm))
//! 2. AH direct: System.apply_authorized_upgrade(wasm)
//!
//! Run:
//! SUDO_MNEMONIC="..." \
//! RC_RPC="ws://217.77.6.126:9944" \
//! AH_RPC="ws://217.77.6.126:40944" \
//! WASM_FILE="target/release/wbuild/asset-hub-pezkuwichain-runtime/asset_hub_pezkuwichain_runtime.compact.compressed.wasm" \
//! cargo run --release -p pezkuwi-subxt --example ah_upgrade
@@ -16,35 +17,76 @@ use pezkuwi_subxt_signer::bip39::Mnemonic;
use pezkuwi_subxt_signer::sr25519::Keypair;
use std::str::FromStr;
const AH_PARA_ID: u128 = 1000;
fn load_sudo_keypair() -> Keypair {
// 1. Try SUDO_MNEMONIC env var
if let Ok(mnemonic_str) = std::env::var("SUDO_MNEMONIC") {
if !mnemonic_str.is_empty() {
if let Ok(mnemonic) = Mnemonic::from_str(&mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from SUDO_MNEMONIC env var");
return kp;
}
}
}
}
// 2. Fallback to seeds file
let seeds_path = "/home/mamostehp/res/test_seeds.json";
if let Ok(content) = std::fs::read_to_string(seeds_path) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
if let Some(mnemonic_str) = json["sudo_mnemonic"].as_str() {
if let Ok(mnemonic) = Mnemonic::from_str(mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from {}", seeds_path);
return kp;
}
}
}
}
}
panic!("SUDO_MNEMONIC required! Set env var or create /home/mamostehp/res/test_seeds.json");
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== ASSET HUB RUNTIME UPGRADE ===\n");
println!("╔══════════════════════════════════════════╗");
println!("║ ASSET HUB RUNTIME UPGRADE ║");
println!("╚══════════════════════════════════════════╝\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 wasm_path = std::env::var("WASM_FILE").expect("WASM_FILE environment variable required");
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 sudo_keypair = load_sudo_keypair();
println!(" Sudo: {}", sudo_keypair.public_key().to_account_id());
// Load WASM
let wasm_data = std::fs::read(&wasm_path)?;
println!("WASM: {} ({:.2} MB)", wasm_path, wasm_data.len() as f64 / 1_048_576.0);
println!(
" WASM: {} ({:.2} MB)",
wasm_path,
wasm_data.len() as f64 / 1_048_576.0
);
// Blake2-256 hash of WASM
let code_hash = pezsp_crypto_hashing::blake2_256(&wasm_data);
println!("Code hash: 0x{}", hex::encode(code_hash));
println!(" Code hash: 0x{}", hex::encode(code_hash));
// Connect to RC
let rc_api = OnlineClient::<PezkuwiConfig>::from_url(&rc_url).await?;
println!("RC connected: {} (spec {})", rc_url, rc_api.runtime_version().spec_version);
let rc_api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&rc_url).await?;
println!(
" RC connected: {} (spec {})",
rc_url,
rc_api.runtime_version().spec_version
);
// Connect to AH
let ah_api = OnlineClient::<PezkuwiConfig>::from_url(&ah_url).await?;
println!("AH connected: {} (spec {})\n", ah_url, ah_api.runtime_version().spec_version);
let ah_api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&ah_url).await?;
let old_spec = ah_api.runtime_version().spec_version;
println!(" AH connected: {} (spec {})\n", ah_url, old_spec);
// ═══════════════════════════════════════════
// STEP 1: Authorize upgrade via XCM from RC
@@ -52,10 +94,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== STEP 1: Authorize upgrade (RC → XCM → AH) ===");
// Encode System::authorize_upgrade_without_checks(code_hash)
// System pallet index = 0, call_index = 10
// System pallet index = 0, call_index = 10 (verified from source)
let mut encoded_call = Vec::with_capacity(34);
encoded_call.push(0x00); // System pallet
encoded_call.push(0x0a); // authorize_upgrade_without_checks (10)
encoded_call.push(0x0a); // authorize_upgrade_without_checks = call_index(10)
encoded_call.extend_from_slice(&code_hash);
println!(" Encoded call: {} bytes", encoded_call.len());
@@ -67,7 +109,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
"interior",
Value::unnamed_variant(
"X1",
vec![Value::unnamed_variant("Teyrchain", vec![Value::u128(1000)])],
vec![Value::unnamed_variant(
"Teyrchain",
vec![Value::u128(AH_PARA_ID)],
)],
),
),
])],
@@ -113,7 +158,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
],
);
let progress = rc_api.tx().sign_and_submit_then_watch_default(&sudo_tx, &sudo_keypair).await?;
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 mut sent = false;
@@ -127,8 +175,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
if !sent {
println!(" WARNING: No XcmPallet::Sent event!");
println!(" Aborting.");
println!(" ERROR: No XcmPallet::Sent event! Aborting.");
return Ok(());
}
println!(" XCM authorize_upgrade sent!\n");
@@ -139,26 +186,26 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
for attempt in 1..=30 {
tokio::time::sleep(std::time::Duration::from_secs(6)).await;
// Reconnect to get fresh state
let ah_check = OnlineClient::<PezkuwiConfig>::from_url(&ah_url).await?;
let ah_check = OnlineClient::<PezkuwiConfig>::from_insecure_url(&ah_url).await?;
let block = ah_check.blocks().at_latest().await?;
let block_num = block.number();
// Check System::AuthorizedUpgrade storage via raw key
// twox128("System") ++ twox128("AuthorizedUpgrade")
let auth_key = pezsp_crypto_hashing::twox_128(b"System")
.iter()
.chain(pezsp_crypto_hashing::twox_128(b"AuthorizedUpgrade").iter())
.copied()
.collect::<Vec<u8>>();
let result = ah_check.storage().at_latest().await?.fetch_raw(auth_key).await?;
if !result.is_empty() {
println!(
" AuthorizedUpgrade found on AH at block {} (attempt {})!",
block_num, attempt
);
authorized = true;
break;
let result = ah_check.storage().at_latest().await?.fetch_raw(auth_key).await;
match result {
Ok(data) if !data.is_empty() => {
println!(
" AuthorizedUpgrade found on AH at block {} (attempt {})!",
block_num, attempt
);
authorized = true;
break;
},
_ => {}, // NoValueFound or empty — not yet set
}
println!(
" Attempt {}/30: AH block {} — AuthorizedUpgrade not yet set...",
@@ -172,98 +219,119 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
// ═══════════════════════════════════════════
// STEP 1.5: Fund sudo account on AH via XCM
// STEP 1.5: Fund sudo account on AH via XCM (if needed)
// ═══════════════════════════════════════════
println!("\n=== STEP 1.5: Fund sudo account on AH ===");
let sudo_account_id = sudo_keypair.public_key().to_account_id();
let account_bytes: [u8; 32] = *sudo_account_id.as_ref();
// Encode Balances::force_set_balance(who, new_free)
// Balances pallet = 10, call_index = 8
// who = MultiAddress::Id(AccountId32) = variant 0 + 32 bytes
// new_free = Compact<u128> = 100 HEZ = 100 * 10^18
let mut fund_call: Vec<u8> = Vec::new();
fund_call.push(10u8); // Balances pallet
fund_call.push(8u8); // force_set_balance
fund_call.push(0u8); // MultiAddress::Id variant
fund_call.extend_from_slice(&account_bytes);
// Compact<u128> for 100_000_000_000_000_000_000 (100 HEZ)
// For compact: values > 2^30 use BigInt mode: (byte_len - 4) << 2 | 0b11, then LE bytes
let amount: u128 = 100_000_000_000_000_000_000u128; // 100 HEZ
let amount_bytes = amount.to_le_bytes();
// Trim trailing zeros for compact encoding
let significant = amount_bytes.iter().rposition(|&b| b != 0).map(|i| i + 1).unwrap_or(1);
let byte_len = significant.max(4); // minimum 4 bytes for BigInt mode
fund_call.push(((byte_len as u8 - 4) << 2) | 0b11);
fund_call.extend_from_slice(&amount_bytes[..byte_len]);
// Check existing balance first
let balance_key = {
let mut key = Vec::new();
key.extend_from_slice(&pezsp_crypto_hashing::twox_128(b"System"));
key.extend_from_slice(&pezsp_crypto_hashing::twox_128(b"Account"));
let hash = pezsp_crypto_hashing::blake2_128(&account_bytes);
key.extend_from_slice(&hash);
key.extend_from_slice(&account_bytes);
key
};
let ah_storage = ah_api.storage().at_latest().await?;
let has_balance = match ah_storage.fetch_raw(balance_key).await {
Ok(data) => !data.is_empty(),
Err(_) => false,
};
println!(
" Encoded force_set_balance ({} bytes): 0x{}",
fund_call.len(),
hex::encode(&fund_call)
);
let fund_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(1000)])],
),
),
])],
);
let fund_msg = 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(&fund_call)),
],
),
])],
);
let fund_xcm = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![fund_dest, fund_msg]);
let fund_sudo = pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![fund_xcm.into_value()]);
let progress = rc_api
.tx()
.sign_and_submit_then_watch_default(&fund_sudo, &sudo_keypair)
.await?;
let events = progress.wait_for_finalized_success().await?;
let fund_sent = events
.iter()
.flatten()
.any(|e| e.pallet_name() == "XcmPallet" && e.variant_name() == "Sent");
if fund_sent {
println!(" [OK] Force set balance XCM sent");
if has_balance {
println!(" Sudo account already has funds on AH — skipping funding");
} else {
println!(" [WARN] No XcmPallet::Sent event for funding");
}
println!(" Sudo account has no funds — funding via XCM...");
println!(" Waiting 12s for DMP processing...");
tokio::time::sleep(std::time::Duration::from_secs(12)).await;
// Encode Balances::force_set_balance(who, new_free)
// Balances pallet = 10, call_index = 8 (verified from source)
let mut fund_call: Vec<u8> = Vec::new();
fund_call.push(10u8); // Balances pallet
fund_call.push(8u8); // force_set_balance
fund_call.push(0u8); // MultiAddress::Id variant
fund_call.extend_from_slice(&account_bytes);
// 10,000 HEZ = 10_000 * 10^12 (12 decimals, NOT 18)
let amount: u128 = 10_000_000_000_000_000u128; // 10,000 HEZ
let amount_bytes = amount.to_le_bytes();
let significant = amount_bytes
.iter()
.rposition(|&b| b != 0)
.map(|i| i + 1)
.unwrap_or(1);
let byte_len = significant.max(4);
fund_call.push(((byte_len as u8 - 4) << 2) | 0b11);
fund_call.extend_from_slice(&amount_bytes[..byte_len]);
let fund_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(AH_PARA_ID)],
)],
),
),
])],
);
let fund_msg = 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(&fund_call)),
],
),
])],
);
let fund_xcm =
pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![fund_dest, fund_msg]);
let fund_sudo =
pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![fund_xcm.into_value()]);
let progress = rc_api
.tx()
.sign_and_submit_then_watch_default(&fund_sudo, &sudo_keypair)
.await?;
let events = progress.wait_for_finalized_success().await?;
let fund_sent = events
.iter()
.flatten()
.any(|e| e.pallet_name() == "XcmPallet" && e.variant_name() == "Sent");
if fund_sent {
println!(" [OK] Force set balance XCM sent");
} else {
println!(" [WARN] No XcmPallet::Sent event for funding");
}
println!(" Waiting 18s for DMP processing...");
tokio::time::sleep(std::time::Duration::from_secs(18)).await;
}
// ═══════════════════════════════════════════
// STEP 2: Enact upgrade on AH directly
@@ -295,23 +363,40 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
if code_updated {
println!("\n UPGRADE SUCCESS!");
} else {
println!("\n WARNING: No CodeUpdated event!");
println!("\n ValidationFunctionStored — waiting for relay chain enactment...");
}
// ═══════════════════════════════════════════
// STEP 3: Verify
// STEP 3: Verify (poll for spec change)
// ═══════════════════════════════════════════
println!("\nWaiting 6 seconds for new runtime...");
tokio::time::sleep(std::time::Duration::from_secs(6)).await;
println!("\nVerifying upgrade...");
let mut verified = false;
for attempt in 1..=10 {
tokio::time::sleep(std::time::Duration::from_secs(12)).await;
let ah_api2 = OnlineClient::<PezkuwiConfig>::from_insecure_url(&ah_url).await?;
let new_spec = ah_api2.runtime_version().spec_version;
if new_spec > old_spec {
println!(
" AH spec_version: {}{} — UPGRADE VERIFIED! (attempt {})",
old_spec, new_spec, attempt
);
verified = true;
break;
}
println!(
" Attempt {}/10: spec still {} — waiting...",
attempt, new_spec
);
}
let ah_api2 = OnlineClient::<PezkuwiConfig>::from_url(&ah_url).await?;
println!(
"AH spec_version: {}{}",
ah_api.runtime_version().spec_version,
ah_api2.runtime_version().spec_version
);
if !verified {
println!(" WARNING: spec_version did not increase after 2 minutes!");
println!(" Check AH logs and relay chain for enactment status.");
}
println!("\n=== DONE ===");
println!("\n╔══════════════════════════════════════════╗");
println!("║ ASSET HUB UPGRADE COMPLETE ║");
println!("╚══════════════════════════════════════════╝");
Ok(())
}
+394
View File
@@ -0,0 +1,394 @@
//! People Chain Runtime Upgrade (Mainnet)
//!
//! Two-step process:
//! 1. RC → XCM → People: System.authorize_upgrade(blake2_256(wasm))
//! 2. People direct: System.apply_authorized_upgrade(wasm)
//!
//! Run:
//! SUDO_MNEMONIC="..." \
//! RC_RPC="ws://217.77.6.126:9944" \
//! PEOPLE_RPC="ws://217.77.6.126:41944" \
//! WASM_FILE="target/release/wbuild/people-pezkuwichain-runtime/people_pezkuwichain_runtime.compact.compressed.wasm" \
//! cargo run --release -p pezkuwi-subxt --example people_upgrade
#![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 PEOPLE_PARA_ID: u128 = 1004;
fn load_sudo_keypair() -> Keypair {
// 1. Try SUDO_MNEMONIC env var
if let Ok(mnemonic_str) = std::env::var("SUDO_MNEMONIC") {
if !mnemonic_str.is_empty() {
if let Ok(mnemonic) = Mnemonic::from_str(&mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from SUDO_MNEMONIC env var");
return kp;
}
}
}
}
// 2. Fallback to seeds file
let seeds_path = "/home/mamostehp/res/test_seeds.json";
if let Ok(content) = std::fs::read_to_string(seeds_path) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
if let Some(mnemonic_str) = json["sudo_mnemonic"].as_str() {
if let Ok(mnemonic) = Mnemonic::from_str(mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from {}", seeds_path);
return kp;
}
}
}
}
}
panic!("SUDO_MNEMONIC required! Set env var or create /home/mamostehp/res/test_seeds.json");
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("╔══════════════════════════════════════════╗");
println!("║ PEOPLE CHAIN RUNTIME UPGRADE ║");
println!("╚══════════════════════════════════════════╝\n");
let rc_url = std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string());
let people_url =
std::env::var("PEOPLE_RPC").unwrap_or_else(|_| "ws://127.0.0.1:41944".to_string());
let wasm_path = std::env::var("WASM_FILE").expect("WASM_FILE environment variable required");
let sudo_keypair = load_sudo_keypair();
println!(" Sudo: {}", sudo_keypair.public_key().to_account_id());
// Load WASM
let wasm_data = std::fs::read(&wasm_path)?;
println!(" WASM: {} ({:.2} MB)", wasm_path, wasm_data.len() as f64 / 1_048_576.0);
// Blake2-256 hash of WASM
let code_hash = pezsp_crypto_hashing::blake2_256(&wasm_data);
println!(" Code hash: 0x{}", hex::encode(code_hash));
// Connect to RC
let rc_api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&rc_url).await?;
println!(
" RC connected: {} (spec {})",
rc_url,
rc_api.runtime_version().spec_version
);
// Connect to People Chain
let people_api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&people_url).await?;
let old_spec = people_api.runtime_version().spec_version;
println!(" People connected: {} (spec {})\n", people_url, old_spec);
// ═══════════════════════════════════════════
// STEP 1: Authorize upgrade via XCM from RC
// ═══════════════════════════════════════════
println!("=== STEP 1: Authorize upgrade (RC → XCM → People) ===");
// Encode System::authorize_upgrade_without_checks(code_hash)
// System pallet index = 0, call_index = 10
let mut encoded_call = Vec::with_capacity(34);
encoded_call.push(0x00); // System pallet
encoded_call.push(0x0a); // authorize_upgrade_without_checks (10)
encoded_call.extend_from_slice(&code_hash);
println!(" Encoded call: {} bytes", encoded_call.len());
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(PEOPLE_PARA_ID)],
)],
),
),
])],
);
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)),
],
),
])],
);
let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, message]);
let sudo_tx = pezkuwi_subxt::dynamic::tx(
"Sudo",
"sudo_unchecked_weight",
vec![
xcm_send.into_value(),
Value::named_composite([
("ref_time", Value::u128(1u128)),
("proof_size", Value::u128(1u128)),
]),
],
);
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 mut sent = false;
for event in events.iter() {
let event = event?;
if event.pallet_name() == "XcmPallet" && event.variant_name() == "Sent" {
sent = true;
}
if event.pallet_name() == "Sudo" || event.pallet_name() == "XcmPallet" {
println!(" {}::{}", event.pallet_name(), event.variant_name());
}
}
if !sent {
println!(" ERROR: No XcmPallet::Sent event! Aborting.");
return Ok(());
}
println!(" XCM authorize_upgrade sent!\n");
// Wait for People Chain to process the XCM — poll AuthorizedUpgrade storage
println!("Waiting for People Chain to process XCM authorize_upgrade...");
let mut authorized = false;
for attempt in 1..=30 {
tokio::time::sleep(std::time::Duration::from_secs(6)).await;
let people_check = OnlineClient::<PezkuwiConfig>::from_insecure_url(&people_url).await?;
let block = people_check.blocks().at_latest().await?;
let block_num = block.number();
// Check System::AuthorizedUpgrade storage via raw key
let auth_key = pezsp_crypto_hashing::twox_128(b"System")
.iter()
.chain(pezsp_crypto_hashing::twox_128(b"AuthorizedUpgrade").iter())
.copied()
.collect::<Vec<u8>>();
let result = people_check.storage().at_latest().await?.fetch_raw(auth_key).await;
match result {
Ok(data) if !data.is_empty() => {
println!(
" AuthorizedUpgrade found on People at block {} (attempt {})!",
block_num, attempt
);
authorized = true;
break;
},
_ => {}, // NoValueFound or empty — not yet set, continue polling
}
println!(
" Attempt {}/30: People block {} — AuthorizedUpgrade not yet set...",
attempt, block_num
);
}
if !authorized {
println!(" ERROR: AuthorizedUpgrade not set after 3 minutes. Aborting.");
return Ok(());
}
// ═══════════════════════════════════════════
// STEP 1.5: Fund sudo account on People via XCM (if needed)
// ═══════════════════════════════════════════
println!("\n=== STEP 1.5: Fund sudo account on People Chain ===");
let sudo_account_id = sudo_keypair.public_key().to_account_id();
let account_bytes: [u8; 32] = *sudo_account_id.as_ref();
// Check existing balance first
let balance_key = {
let mut key = Vec::new();
key.extend_from_slice(&pezsp_crypto_hashing::twox_128(b"System"));
key.extend_from_slice(&pezsp_crypto_hashing::twox_128(b"Account"));
// Blake2_128Concat hasher for account
let hash = pezsp_crypto_hashing::blake2_128(&account_bytes);
key.extend_from_slice(&hash);
key.extend_from_slice(&account_bytes);
key
};
let people_storage = people_api.storage().at_latest().await?;
let has_balance = match people_storage.fetch_raw(balance_key).await {
Ok(data) => !data.is_empty(),
Err(_) => false, // NoValueFound — account doesn't exist
};
if has_balance {
println!(" Sudo account already has funds on People Chain — skipping funding");
} else {
println!(" Sudo account has no funds — funding via XCM...");
// Encode Balances::force_set_balance(who, new_free)
// Balances pallet = 10, call_index = 8
let mut fund_call: Vec<u8> = Vec::new();
fund_call.push(10u8); // Balances pallet
fund_call.push(8u8); // force_set_balance
fund_call.push(0u8); // MultiAddress::Id variant
fund_call.extend_from_slice(&account_bytes);
// 10,000 HEZ = 10_000 * 10^12 (12 decimals, NOT 18)
// Generous amount to cover apply_authorized_upgrade fee (1.5MB extrinsic)
let amount: u128 = 10_000_000_000_000_000u128; // 10,000 HEZ
let amount_bytes = amount.to_le_bytes();
let significant = amount_bytes
.iter()
.rposition(|&b| b != 0)
.map(|i| i + 1)
.unwrap_or(1);
let byte_len = significant.max(4);
fund_call.push(((byte_len as u8 - 4) << 2) | 0b11);
fund_call.extend_from_slice(&amount_bytes[..byte_len]);
let fund_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(PEOPLE_PARA_ID)],
)],
),
),
])],
);
let fund_msg = 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(&fund_call)),
],
),
])],
);
let fund_xcm =
pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![fund_dest, fund_msg]);
let fund_sudo =
pezkuwi_subxt::dynamic::tx("Sudo", "sudo", vec![fund_xcm.into_value()]);
let progress = rc_api
.tx()
.sign_and_submit_then_watch_default(&fund_sudo, &sudo_keypair)
.await?;
let events = progress.wait_for_finalized_success().await?;
let fund_sent = events
.iter()
.flatten()
.any(|e| e.pallet_name() == "XcmPallet" && e.variant_name() == "Sent");
if fund_sent {
println!(" [OK] Force set balance XCM sent");
} else {
println!(" [WARN] No XcmPallet::Sent event for funding");
}
println!(" Waiting 18s for DMP processing...");
tokio::time::sleep(std::time::Duration::from_secs(18)).await;
}
// ═══════════════════════════════════════════
// STEP 2: Enact upgrade on People directly
// ═══════════════════════════════════════════
println!("\n=== STEP 2: Apply authorized upgrade on People Chain ===");
println!(" Submitting {} bytes WASM...", wasm_data.len());
let enact_call = pezkuwi_subxt::dynamic::tx(
"System",
"apply_authorized_upgrade",
vec![Value::from_bytes(&wasm_data)],
);
let progress = people_api
.tx()
.sign_and_submit_then_watch_default(&enact_call, &sudo_keypair)
.await?;
let events = progress.wait_for_finalized_success().await?;
let mut code_updated = false;
for event in events.iter() {
let event = event?;
println!(" {}::{}", event.pallet_name(), event.variant_name());
if event.pallet_name() == "System" && event.variant_name() == "CodeUpdated" {
code_updated = true;
}
}
if code_updated {
println!("\n UPGRADE SUCCESS!");
} else {
println!("\n WARNING: No CodeUpdated event!");
}
// ═══════════════════════════════════════════
// STEP 3: Verify
// ═══════════════════════════════════════════
println!("\nWaiting 12 seconds for new runtime...");
tokio::time::sleep(std::time::Duration::from_secs(12)).await;
let people_api2 = OnlineClient::<PezkuwiConfig>::from_insecure_url(&people_url).await?;
let new_spec = people_api2.runtime_version().spec_version;
println!(
"\n People Chain spec_version: {}{}",
old_spec, new_spec
);
if new_spec > old_spec {
println!(" UPGRADE VERIFIED!");
} else {
println!(" WARNING: spec_version did not increase!");
}
println!("\n╔══════════════════════════════════════════╗");
println!("║ PEOPLE CHAIN UPGRADE COMPLETE ║");
println!("╚══════════════════════════════════════════╝");
Ok(())
}
+70 -29
View File
@@ -1,15 +1,11 @@
//! Relay Chain Runtime Upgrade
//! Relay Chain Runtime Upgrade (Mainnet)
//!
//! Deploys new WASM via sudo(sudoUncheckedWeight(system.setCodeWithoutChecks)).
//! Does NOTHING else — no storage changes, no validator count, no ForceEra.
//!
//! Run:
//! SUDO_MNEMONIC="..." \
//! RC_RPC="ws://217.77.6.126:9944" \
//! WASM_FILE="target/release/wbuild/pezkuwichain-runtime/pezkuwichain_runtime.compact.compressed.wasm" \
//! cargo run --release -p pezkuwi-subxt --example rc_upgrade
//!
//! Optional:
//! RC_RPC="ws://127.0.0.1:9944" (default: ws://127.0.0.1:9944)
#![allow(missing_docs)]
use pezkuwi_subxt::dynamic::Value;
@@ -19,33 +15,65 @@ use pezkuwi_subxt_signer::bip39::Mnemonic;
use pezkuwi_subxt_signer::sr25519::Keypair;
use std::str::FromStr;
fn load_sudo_keypair() -> Keypair {
if let Ok(mnemonic_str) = std::env::var("SUDO_MNEMONIC") {
if !mnemonic_str.is_empty() {
if let Ok(mnemonic) = Mnemonic::from_str(&mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from SUDO_MNEMONIC env var");
return kp;
}
}
}
}
let seeds_path = "/home/mamostehp/res/test_seeds.json";
if let Ok(content) = std::fs::read_to_string(seeds_path) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
if let Some(mnemonic_str) = json["sudo_mnemonic"].as_str() {
if let Ok(mnemonic) = Mnemonic::from_str(mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from {}", seeds_path);
return kp;
}
}
}
}
}
panic!("SUDO_MNEMONIC required! Set env var or create /home/mamostehp/res/test_seeds.json");
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== RELAY CHAIN RUNTIME UPGRADE ===\n");
println!("╔══════════════════════════════════════════╗");
println!("║ RELAY CHAIN RUNTIME UPGRADE ║");
println!("╚══════════════════════════════════════════╝\n");
let rc_url = std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string());
let wasm_path = std::env::var("WASM_FILE").expect("WASM_FILE environment variable required");
// Load WASM
let wasm_data = std::fs::read(&wasm_path)?;
println!("WASM: {} ({:.2} MB)", wasm_path, wasm_data.len() as f64 / 1_048_576.0);
println!(
" WASM: {} ({:.2} MB)",
wasm_path,
wasm_data.len() as f64 / 1_048_576.0
);
let code_hash = pezsp_crypto_hashing::blake2_256(&wasm_data);
println!("Code hash: 0x{}", hex::encode(code_hash));
println!(" Code hash: 0x{}", hex::encode(code_hash));
// Connect
let api = OnlineClient::<PezkuwiConfig>::from_url(&rc_url).await?;
let api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&rc_url).await?;
let old_spec = api.runtime_version().spec_version;
println!("RC connected: {} (spec {})", rc_url, old_spec);
println!(" RC connected: {} (spec {})", rc_url, old_spec);
// 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 = Keypair::from_phrase(&mnemonic, None)?;
println!("Sudo: {}\n", sudo_keypair.public_key().to_account_id());
let sudo_keypair = load_sudo_keypair();
println!(" Sudo: {}\n", sudo_keypair.public_key().to_account_id());
// Deploy WASM via sudo(sudoUncheckedWeight(system.setCodeWithoutChecks))
println!("Deploying WASM...");
println!("=== Deploying WASM... ===");
let set_code = pezkuwi_subxt::dynamic::tx(
"System",
"set_code_without_checks",
@@ -63,7 +91,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
],
);
let tx_progress = api.tx().sign_and_submit_then_watch_default(&sudo_tx, &sudo_keypair).await?;
let tx_progress =
api.tx().sign_and_submit_then_watch_default(&sudo_tx, &sudo_keypair).await?;
println!(" TX: 0x{}", hex::encode(tx_progress.extrinsic_hash().as_ref()));
let mut progress = tx_progress;
@@ -115,18 +144,30 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
}
// Verify
println!("\nWaiting 12 seconds for new runtime...");
tokio::time::sleep(std::time::Duration::from_secs(12)).await;
let api2 = OnlineClient::<PezkuwiConfig>::from_url(&rc_url).await?;
let new_spec = api2.runtime_version().spec_version;
println!("spec_version: {}{}", old_spec, new_spec);
if new_spec > old_spec {
println!("\n=== UPGRADE SUCCESS ===");
} else {
println!("\n=== WARNING: spec_version did not increase ===");
println!("\nVerifying upgrade...");
let mut verified = false;
for attempt in 1..=5 {
tokio::time::sleep(std::time::Duration::from_secs(12)).await;
let api2 = OnlineClient::<PezkuwiConfig>::from_insecure_url(&rc_url).await?;
let new_spec = api2.runtime_version().spec_version;
if new_spec > old_spec {
println!(
" spec_version: {}{} — UPGRADE VERIFIED! (attempt {})",
old_spec, new_spec, attempt
);
verified = true;
break;
}
println!(" Attempt {}/5: spec still {} — waiting...", attempt, new_spec);
}
if !verified {
println!(" WARNING: spec_version did not increase after 1 minute!");
}
println!("\n╔══════════════════════════════════════════╗");
println!("║ RELAY CHAIN UPGRADE COMPLETE ║");
println!("╚══════════════════════════════════════════╝");
Ok(())
}
+29 -5
View File
@@ -10,18 +10,42 @@ use pezkuwi_subxt_signer::bip39::Mnemonic;
use pezkuwi_subxt_signer::sr25519::Keypair;
use std::str::FromStr;
fn load_sudo_keypair() -> Keypair {
if let Ok(mnemonic_str) = std::env::var("SUDO_MNEMONIC") {
if !mnemonic_str.is_empty() {
if let Ok(mnemonic) = Mnemonic::from_str(&mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from SUDO_MNEMONIC env var");
return kp;
}
}
}
}
let seeds_path = "/home/mamostehp/res/test_seeds.json";
if let Ok(content) = std::fs::read_to_string(seeds_path) {
if let Ok(json) = serde_json::from_str::<serde_json::Value>(&content) {
if let Some(mnemonic_str) = json["sudo_mnemonic"].as_str() {
if let Ok(mnemonic) = Mnemonic::from_str(mnemonic_str) {
if let Ok(kp) = Keypair::from_phrase(&mnemonic, None) {
println!(" [sudo] Loaded from {}", seeds_path);
return kp;
}
}
}
}
}
panic!("SUDO_MNEMONIC required!");
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== SET StakingAhClient MODE → Active ===\n");
let rc_url = std::env::var("RC_RPC").unwrap_or_else(|_| "ws://127.0.0.1:9944".to_string());
let api = OnlineClient::<PezkuwiConfig>::from_url(&rc_url).await?;
let api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&rc_url).await?;
println!("RC connected: spec {}", 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)?;
let sudo_keypair = load_sudo_keypair();
println!("Sudo: {}\n", sudo_keypair.public_key().to_account_id());
// Check current mode first