sec: remove hardcoded mnemonics, add mainnet tools and subxt examples

- Add grant_noter_tiki.rs: XCM batch to grant Noter tiki to 21 validators
- Add check_noter_tiki.rs: verify UserTikis storage on People Chain
- Update CRITICAL_STATE: People Chain 1_020_007, noter grant completed,
  relay chain staking-score removal noted
This commit is contained in:
2026-02-16 23:52:08 +03:00
parent 8df1a89e6d
commit 176fd52ab7
2 changed files with 340 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
//! Check if validators have Noter tiki on People Chain
//!
//! Run with:
//! cargo run --release -p pezkuwi-subxt --example check_noter_tiki
#![allow(missing_docs)]
use pezkuwi_subxt::dynamic::Value;
use pezkuwi_subxt::utils::AccountId32;
use pezkuwi_subxt::{OnlineClient, PezkuwiConfig};
fn validators() -> Vec<(&'static str, &'static str)> {
vec![
("Çiyager (Cihat Türkan)", "5GipBJs2uNWTCazyZQ2vG3DEqLz4tXNmNZtBAT1Mtm1orZ5i"),
("Mehmet Tunç", "5HWFZbhkZuTUySXu6ZXYKrTHBnWXHvWRKLozE22zhnwXGGxk"),
("Nagihan Akarsel", "5CrB5BWJfLNWEZAsAXDKXdJUGzFMXKvYnwRX4DVMcgBwxSdx"),
("Sait Çürükkaya", "5ELgySrX5ZyK7EWXjj6bAedyTCcTNWDANbiiipsT5gnpoCEp"),
("Evdile Koçer", "5GCZQNjRdHofEHPvVq4ePrfDYcjRzQ1HQ2awHMX6AawpRYuM"),
("Mam Zeki", "5H8jTzi4Gm4rbFtXw6h5enhLhgsuhNAqR5K2itmPiz83ymWy"),
("Kakaî Falah", "5Fs3P5tHuL9cvwPQojsheViRRAjFkMMFa32jAkDSwW9mbTfU"),
("Feryad Fazil Ömer", "5DXgq7uDXog6zcubT3wgtaYosoibjudz4w5ScPW2phLuAy3V"),
("Mevlud Afand", "5FyFwbGLgPXun3azh6Gx83wCuUt5FTavb2WAVDYrjziVB9rN"),
("Şêrko Fatih Şivandî", "5HEcuuypLDeJaSj6ZgH57aXhuviyeLNdw9QrCDJ8u6gsnjnL"),
("Ramin Hüseyin Penahi", "5EpmpTXbMXpz6ixy3WhutdzcexzPbvybNKv4eiiN1kvTnQH5"),
("Zanyar Moradi", "5DFsm3BBEgHmSEZkvwGKB7c7tiH2avhfuQE1SEjfMDGuczsW"),
("Heidar Ghorbani", "5HePVUXjGSM2hVZ1YMz2V3KoX6EdQNEmmzUnUvpfGV95ofUR"),
("Farhad Salimi", "5GP4nAcwtETTg1oAHQNvevmmhG8GEstGQeCirKEhaDTwpFgx"),
("Vafa Azarbar", "5FYoCM3oeEGeoFY94EgXBhmABkRCabvPp72ur5bJNG3cK619"),
("Dr. Aziz Mihemed", "5GspwkKF6aYzFkmAyBBQg7coSCSgDCore79fbW8uxJNAH347"),
("Arîn Mîrkan", "5GmuX11pN2fC4Fyq1V7MuiYt3aevZcVQs3HZWKyzmap9bKfe"),
("Ebu Leyla", "5FQptVCtM1qsxkLbQkATkw4Kio4M9LxWvM6TwgEo3QjmTXF3"),
("Rêvan Kobanê", "5E7VD2qmso1yRfyq3t9u2qhauAgtmjZTybVsCARF5Zz9bXy6"),
("Amanj Babani", "5Ccz5W7Q21g4UPCytzHxD3VSMLJ1BbbWSkJKFwsNtYRk3HkX"),
("Xosrow Gulan", "5D7WPmK1SAJyYDdCtgqEzGJpWXQe3Lj9FqWL8z9waLTkUNv3"),
]
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let url = std::env::var("RPC_URL").unwrap_or_else(|_| "ws://217.77.6.126:41944".to_string());
println!("=== CHECK NOTER TIKI ON PEOPLE CHAIN ===\n");
println!("RPC: {}", url);
let api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&url).await?;
println!("Connected! spec_version: {}\n", api.runtime_version().spec_version);
let storage = api.storage().at_latest().await?;
let mut noter_count = 0;
let mut no_tiki_count = 0;
for (name, ss58) in validators() {
let account: AccountId32 = ss58.parse()?;
// Query UserTikis storage map: Tiki::UserTikis(AccountId32) -> Vec<Tiki>
let query = pezkuwi_subxt::dynamic::storage::<Vec<Value>, Value>("Tiki", "UserTikis");
match storage.try_fetch(query, vec![Value::from_bytes(&account.0)]).await? {
Some(val) => {
let decoded = val.decode()?;
println!("{}: {:?}", name, decoded);
noter_count += 1;
},
None => {
println!("{}: NO TIKIS", name);
no_tiki_count += 1;
},
}
}
println!("\n=== SUMMARY ===");
println!("With tikis: {}", noter_count);
println!("No tikis: {}", no_tiki_count);
Ok(())
}
+265
View File
@@ -0,0 +1,265 @@
//! Grant Noter tiki to all 21 validators via XCM Transact batch
//!
//! People Chain has no Sudo, so we send from relay chain:
//! sudo(xcmPallet.send(Parachain(1004), Transact(
//! utility.batch_all([tiki.grant_tiki(val, Noter) × 21])
//! )))
//!
//! Run with:
//! SUDO_MNEMONIC="..." cargo run --release -p pezkuwi-subxt --example grant_noter_tiki
#![allow(missing_docs, dead_code)]
use pezkuwi_subxt::dynamic::Value;
use pezkuwi_subxt::utils::AccountId32;
use pezkuwi_subxt::{OnlineClient, PezkuwiConfig};
use pezkuwi_subxt_signer::bip39::Mnemonic;
use pezkuwi_subxt_signer::sr25519::Keypair;
use std::str::FromStr;
const PEOPLE_CHAIN_PARA_ID: u32 = 1004;
// People Chain pallet indices
const UTILITY_PALLET: u8 = 40; // 0x28
const TIKI_PALLET: u8 = 61; // 0x3d
// Call indices
const GRANT_TIKI_CALL: u8 = 0;
const BATCH_ALL_CALL: u8 = 2;
// Tiki enum values
const TIKI_NOTER: u8 = 9; // 0x09
fn validators() -> Vec<(&'static str, &'static str)> {
vec![
("Çiyager (Cihat Türkan)", "5GipBJs2uNWTCazyZQ2vG3DEqLz4tXNmNZtBAT1Mtm1orZ5i"),
("Mehmet Tunç", "5HWFZbhkZuTUySXu6ZXYKrTHBnWXHvWRKLozE22zhnwXGGxk"),
("Nagihan Akarsel", "5CrB5BWJfLNWEZAsAXDKXdJUGzFMXKvYnwRX4DVMcgBwxSdx"),
("Sait Çürükkaya", "5ELgySrX5ZyK7EWXjj6bAedyTCcTNWDANbiiipsT5gnpoCEp"),
("Evdile Koçer", "5GCZQNjRdHofEHPvVq4ePrfDYcjRzQ1HQ2awHMX6AawpRYuM"),
("Mam Zeki", "5H8jTzi4Gm4rbFtXw6h5enhLhgsuhNAqR5K2itmPiz83ymWy"),
("Kakaî Falah", "5Fs3P5tHuL9cvwPQojsheViRRAjFkMMFa32jAkDSwW9mbTfU"),
("Feryad Fazil Ömer", "5DXgq7uDXog6zcubT3wgtaYosoibjudz4w5ScPW2phLuAy3V"),
("Mevlud Afand", "5FyFwbGLgPXun3azh6Gx83wCuUt5FTavb2WAVDYrjziVB9rN"),
("Şêrko Fatih Şivandî", "5HEcuuypLDeJaSj6ZgH57aXhuviyeLNdw9QrCDJ8u6gsnjnL"),
("Ramin Hüseyin Penahi", "5EpmpTXbMXpz6ixy3WhutdzcexzPbvybNKv4eiiN1kvTnQH5"),
("Zanyar Moradi", "5DFsm3BBEgHmSEZkvwGKB7c7tiH2avhfuQE1SEjfMDGuczsW"),
("Heidar Ghorbani", "5HePVUXjGSM2hVZ1YMz2V3KoX6EdQNEmmzUnUvpfGV95ofUR"),
("Farhad Salimi", "5GP4nAcwtETTg1oAHQNvevmmhG8GEstGQeCirKEhaDTwpFgx"),
("Vafa Azarbar", "5FYoCM3oeEGeoFY94EgXBhmABkRCabvPp72ur5bJNG3cK619"),
("Dr. Aziz Mihemed", "5GspwkKF6aYzFkmAyBBQg7coSCSgDCore79fbW8uxJNAH347"),
("Arîn Mîrkan", "5GmuX11pN2fC4Fyq1V7MuiYt3aevZcVQs3HZWKyzmap9bKfe"),
("Ebu Leyla", "5FQptVCtM1qsxkLbQkATkw4Kio4M9LxWvM6TwgEo3QjmTXF3"),
("Rêvan Kobanê", "5E7VD2qmso1yRfyq3t9u2qhauAgtmjZTybVsCARF5Zz9bXy6"),
("Amanj Babani", "5Ccz5W7Q21g4UPCytzHxD3VSMLJ1BbbWSkJKFwsNtYRk3HkX"),
("Xosrow Gulan", "5D7WPmK1SAJyYDdCtgqEzGJpWXQe3Lj9FqWL8z9waLTkUNv3"),
]
}
/// SCALE compact encoding for small numbers (< 64)
fn encode_compact(value: usize) -> Vec<u8> {
if value < 64 {
vec![(value as u8) << 2]
} else if value < 16384 {
let v = ((value as u16) << 2) | 0x01;
v.to_le_bytes().to_vec()
} else {
panic!("Value too large for compact encoding: {}", value);
}
}
/// Encode tiki.grant_tiki(dest: MultiAddress::Id(AccountId32), tiki: Tiki::Noter)
fn encode_grant_noter(account_id: &[u8; 32]) -> Vec<u8> {
let mut encoded = Vec::with_capacity(36);
encoded.push(TIKI_PALLET); // 0x3d
encoded.push(GRANT_TIKI_CALL); // 0x00
encoded.push(0x00); // MultiAddress::Id variant
encoded.extend_from_slice(account_id);
encoded.push(TIKI_NOTER); // 0x09
encoded
}
/// Encode utility.batch_all(calls)
fn encode_batch_all(calls: Vec<Vec<u8>>) -> Vec<u8> {
let mut encoded = vec![UTILITY_PALLET, BATCH_ALL_CALL]; // 0x28, 0x02
encoded.extend(encode_compact(calls.len()));
for call in calls {
encoded.extend(call);
}
encoded
}
/// Build XCM V3 message: UnpaidExecution + Transact
fn build_xcm_values(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(PEOPLE_CHAIN_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(50_000_000_000u128)),
("proof_size", Value::u128(5_000_000u128)),
]),
),
("call", Value::from_bytes(encoded_call)),
],
),
])],
);
(dest, message)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== GRANT NOTER TIKI TO 21 VALIDATORS ===\n");
let relay_url =
std::env::var("RPC_URL").unwrap_or_else(|_| "ws://217.77.6.126:9944".to_string());
let vals = validators();
println!("Relay RPC: {}", relay_url);
println!("People Chain Para ID: {}", PEOPLE_CHAIN_PARA_ID);
println!("Validators: {}", vals.len());
println!("Tiki: Noter (index {})\n", TIKI_NOTER);
// Build individual grant_tiki calls
let mut grant_calls: Vec<Vec<u8>> = Vec::new();
for (name, ss58) in &vals {
let account: AccountId32 = ss58.parse()?;
let call = encode_grant_noter(&account.0);
println!(" {}{} bytes", name, call.len());
grant_calls.push(call);
}
// Batch all 21 calls
let batch_call = encode_batch_all(grant_calls);
println!(
"\nBatch call: {} bytes (0x{}...)",
batch_call.len(),
hex::encode(&batch_call[..8])
);
// Connect to relay chain
let api = OnlineClient::<PezkuwiConfig>::from_insecure_url(&relay_url).await?;
println!("Connected! specVersion: {}\n", api.runtime_version().spec_version);
// Load 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());
// Build XCM message
let (dest, message) = build_xcm_values(&batch_call);
// Wrap: xcmPallet.send → sudo.sudo_unchecked_weight
let xcm_send = pezkuwi_subxt::dynamic::tx("XcmPallet", "send", vec![dest, message]);
let sudo_call = 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)),
]),
],
);
println!("Submitting sudo(xcm.send(batch_all(grant_tiki × 21)))...");
use pezkuwi_subxt::tx::TxStatus;
let tx_progress = api.tx().sign_and_submit_then_watch_default(&sudo_call, &sudo_keypair).await?;
println!("TX: 0x{}", hex::encode(tx_progress.extrinsic_hash().as_ref()));
let mut progress = tx_progress;
loop {
let status = progress.next().await;
match status {
Some(Ok(TxStatus::InBestBlock(details))) => {
match details.wait_for_success().await {
Ok(events) => {
let mut has_sudid = false;
let mut has_sent = false;
for ev in events.iter().flatten() {
let pallet = ev.pallet_name();
let variant = ev.variant_name();
println!(" Event: {}::{}", pallet, variant);
if pallet == "Sudo" && variant == "Sudid" {
has_sudid = true;
}
if pallet == "XcmPallet" && variant == "Sent" {
has_sent = true;
}
}
if has_sudid && has_sent {
println!("\nSUCCESS! XCM batch sent to People Chain.");
println!("21 validators should now have Noter tiki.");
} else {
println!("\nWARNING: Expected Sudo::Sudid + XcmPallet::Sent");
}
},
Err(e) => println!("DISPATCH ERROR: {}", e),
}
break;
},
Some(Ok(TxStatus::Error { message })) => {
println!("TX ERROR: {}", message);
break;
},
Some(Ok(TxStatus::Invalid { message })) => {
println!("TX INVALID: {}", message);
break;
},
Some(Ok(TxStatus::Dropped { message })) => {
println!("TX DROPPED: {}", message);
break;
},
Some(Err(e)) => {
println!("STREAM ERROR: {}", e);
break;
},
None => {
println!("STREAM ENDED");
break;
},
_ => {},
}
}
println!("\nVerify on People Chain (port 41944):");
println!(" - UserTikis[validator] should include Noter");
Ok(())
}