use anyhow::{anyhow, Context, Result}; use clap::Parser; use codec::Encode; use sp_core::{ crypto::{AccountId32, DeriveJunction, Pair as _}, sr25519::Pair, }; use subxt::sp_runtime::MultiAddress; use subxt::{ClientBuilder, DefaultConfig, DefaultExtra, PairSigner}; #[subxt::subxt(runtime_metadata_path = "res/metadata.scale")] pub mod gdev_100 {} pub type Api = gdev_100::RuntimeApi<DefaultConfig, DefaultExtra<DefaultConfig>>; type Client = subxt::Client<DefaultConfig>; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] struct Args { #[clap(subcommand)] pub subcommand: Subcommand, /// Secret key or BIP39 mnemonic /// (eventually followed by derivation path) #[clap(short, long)] secret: String, /// Websocket RPC endpoint #[clap(short, long, default_value = "ws://localhost:9945")] url: String, } #[derive(Debug, clap::Subcommand)] pub enum Subcommand { /// Generate a revocation document for the provided account GenRevocDoc, #[clap(hide = true)] Repart { // Number of transactions per block to target target: u32, #[clap(short = 'o', long = "old-repart")] // Old/actual repartition actual_repart: Option<u32>, }, #[clap(hide = true)] SpamRoll { actual_repart: usize }, } #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { env_logger::init(); let args = Args::parse(); let pair = Pair::from_string(&args.secret, None) .map_err(|_| anyhow!("Invalid secret {}", args.secret))?; //let mut signer = PairSigner::new(pair.clone()); let client: Client = ClientBuilder::new() .set_url(&args.url) .build() .await .with_context(|| "fail to connect to node")?; let api = client.clone().to_runtime_api::<Api>(); let account_id: sp_core::crypto::AccountId32 = pair.public().into(); let account = api.storage().system().account(account_id, None).await?; logs::info!("Account free balance: {}", account.data.free); match args.subcommand { Subcommand::GenRevocDoc => gen_revoc_doc(&api, &pair).await?, Subcommand::Repart { target, actual_repart, } => { let mut pairs = Vec::new(); for i in actual_repart.unwrap_or_default()..target { let pair_i = pair .derive(std::iter::once(DeriveJunction::hard::<u32>(i)), None) .map_err(|_| anyhow!("Fail to derive //{}", i))? .0; pairs.push((i, pair_i)); } for (i, pair_i) in &pairs { /*let _ = api .tx() .balances() .transfer(MultiAddress::Id(pair_i.public().into()), 501) .sign_and_submit_then_watch(&signer) .await? .wait_for_in_block() .await?; signer.increment_nonce();*/ let pair_i_account = api .storage() .system() .account(pair_i.public().into(), None) .await?; logs::info!("account //{} balance: {}", i, pair_i_account.data.free); } } Subcommand::SpamRoll { actual_repart } => { let mut pairs = Vec::<( PairSigner<DefaultConfig, DefaultExtra<DefaultConfig>, Pair>, AccountId32, )>::with_capacity(actual_repart); for i in 0..actual_repart { let pair_i = pair .derive(std::iter::once(DeriveJunction::hard::<u32>(i as u32)), None) .map_err(|_| anyhow!("Fail to derive //{}", i))? .0; let account_id_i = pair_i.public().into(); pairs.push((PairSigner::new(pair_i), account_id_i)); } loop { let mut watchers = Vec::with_capacity(actual_repart); for i in 0..(actual_repart - 1) { let dest: AccountId32 = pairs[i + 1].1.clone(); let watcher = api .tx() .balances() .transfer(MultiAddress::Id(dest), 1) .sign_and_submit_then_watch(&pairs[i].0) .await?; pairs[i].0.increment_nonce(); logs::info!("send 1 cent from //{} to //{}", i, i + 1); watchers.push(watcher); } let dest: AccountId32 = pairs[0].1.clone(); let watcher = api .tx() .balances() .transfer(MultiAddress::Id(dest), 1) .sign_and_submit_then_watch(&pairs[actual_repart - 1].0) .await?; pairs[actual_repart - 1].0.increment_nonce(); logs::info!("send 1 cent from //{} to //0", actual_repart - 1); watchers.push(watcher); // Wait all transactions for watcher in watchers { watcher.wait_for_in_block().await?; } } } } Ok(()) } async fn gen_revoc_doc(api: &Api, pair: &Pair) -> Result<()> { let account_id: sp_core::crypto::AccountId32 = pair.public().into(); let genesis_hash = api.storage().system().block_hash(0, None).await?; let payload = (account_id, genesis_hash).encode(); let signature = pair.sign(&payload); println!("payload: 0x{}", hex::encode(payload)); println!("signature: 0x{}", hex::encode(signature)); Ok(()) }