Skip to content
Snippets Groups Projects
main.rs 10.5 KiB
Newer Older
Éloïs's avatar
Éloïs committed
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::{
    extrinsic::{BaseExtrinsicParams, BaseExtrinsicParamsBuilder},
    ClientBuilder, DefaultConfig, PairSigner,
};
Éloïs's avatar
Éloïs committed

#[subxt::subxt(runtime_metadata_path = "res/metadata.scale")]
pub mod gdev_300 {}
Éloïs's avatar
Éloïs committed

pub type Api = gdev_300::RuntimeApi<DefaultConfig, BaseExtrinsicParams<DefaultConfig, Tip>>;
Éloïs's avatar
Éloïs committed
type Client = subxt::Client<DefaultConfig>;

#[derive(Copy, Clone, Debug, Default, Encode)]
pub struct Tip {
    #[codec(compact)]
    tip: u64,
}

impl Tip {
    pub fn new(amount: u64) -> Self {
        Tip { tip: amount }
    }
}

impl From<u64> for Tip {
    fn from(n: u64) -> Self {
        Self::new(n)
    }
}

Éloïs's avatar
Éloïs committed
#[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:9944")]
Éloïs's avatar
Éloïs committed
    url: String,
}

#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
    CreateOneshot {
        balance: u64,
        dest: sp_core::crypto::AccountId32,
    },
    ConsumeOneshot {
        dest: sp_core::crypto::AccountId32,
        #[clap(long = "oneshot")]
        dest_oneshot: bool,
    },
    ConsumeOneshotWithRemaining {
        balance: u64,
        dest: sp_core::crypto::AccountId32,
        #[clap(long = "one")]
        dest_oneshot: bool,
        remaining_to: sp_core::crypto::AccountId32,
        #[clap(long = "rem-one")]
        remaining_to_oneshot: bool,
    },
Éloïs's avatar
Éloïs committed
    /// Generate a revocation document for the provided account
    GenRevocDoc,
    OneshotBalance {
        account: sp_core::crypto::AccountId32,
    },
Éloïs's avatar
Éloïs committed
    #[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 },
    Transfer {
        balance: u64,
        dest: sp_core::crypto::AccountId32,
        #[clap(short = 'k')]
        keep_alive: bool,
    },
Éloïs's avatar
Éloïs committed
}

#[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 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?;
Éloïs's avatar
Éloïs committed
    logs::info!("Account free balance: {}", account.data.free);

    match args.subcommand {
        Subcommand::CreateOneshot { balance, dest } => {
            api.tx()
                .oneshot_account()
                .create_oneshot_account(dest.into(), balance)?
                .sign_and_submit_then_watch(
                    &PairSigner::new(pair),
                    BaseExtrinsicParamsBuilder::new(),
                )
                .await?;
        }
        Subcommand::ConsumeOneshot { dest, dest_oneshot } => {
            let number = api.storage().system().number(None).await?;
            api.tx()
                .oneshot_account()
                .consume_oneshot_account(
                    number,
                    if dest_oneshot {
                        gdev_300::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
                            dest.into(),
                        )
                    } else {
                        gdev_300::runtime_types::pallet_oneshot_account::types::Account::Normal(
                            dest.into(),
                        )
                    },
                )?
                .sign_and_submit_then_watch(
                    &PairSigner::new(pair),
                    BaseExtrinsicParamsBuilder::new(),
                )
                .await?;
        }
        Subcommand::ConsumeOneshotWithRemaining {
            balance,
            dest,
            dest_oneshot,
            remaining_to,
            remaining_to_oneshot,
        } => {
            let number = api.storage().system().number(None).await?;
            api.tx()
                .oneshot_account()
                .consume_oneshot_account_with_remaining(
                    number,
                    if dest_oneshot {
                        gdev_300::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
                            dest.into(),
                        )
                    } else {
                        gdev_300::runtime_types::pallet_oneshot_account::types::Account::Normal(
                            dest.into(),
                        )
                    },
                    if remaining_to_oneshot {
                        gdev_300::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
                            remaining_to.into(),
                        )
                    } else {
                        gdev_300::runtime_types::pallet_oneshot_account::types::Account::Normal(
                            remaining_to.into(),
                        )
                    },
                    balance,
                )?
                .sign_and_submit_then_watch(
                    &PairSigner::new(pair),
                    BaseExtrinsicParamsBuilder::new(),
                )
                .await?;
        }
Éloïs's avatar
Éloïs committed
        Subcommand::GenRevocDoc => gen_revoc_doc(&api, &pair).await?,
        Subcommand::OneshotBalance { account } => {
            logs::info!(
                "{}",
                api.storage()
                    .oneshot_account()
                    .oneshot_accounts(&account, None)
                    .await?
                    .unwrap_or(0)
            );
        }
Éloïs's avatar
Éloïs committed
        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, BaseExtrinsicParamsBuilder::new())
Éloïs's avatar
Éloïs committed
                    .await?
                    .wait_for_in_block()
                    .await?;
                signer.increment_nonce();*/

                let pair_i_account = api
                    .storage()
                    .system()
                    .account(&pair_i.public().into(), None)
Éloïs's avatar
Éloïs committed
                    .await?;
                logs::info!("account //{} balance: {}", i, pair_i_account.data.free);
            }
        }
        Subcommand::SpamRoll { actual_repart } => {
            let mut pairs =
                Vec::<(PairSigner<DefaultConfig, Pair>, AccountId32)>::with_capacity(actual_repart);
Éloïs's avatar
Éloïs committed
            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, BaseExtrinsicParamsBuilder::new())
Éloïs's avatar
Éloïs committed
                        .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,
                        BaseExtrinsicParamsBuilder::new(),
                    )
Éloïs's avatar
Éloïs committed
                    .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?;
                }
            }
        }
        Subcommand::Transfer {
            balance,
            dest,
            keep_alive,
        } => {
            if keep_alive {
                api.tx()
                    .balances()
                    .transfer(dest.into(), balance)?
                    .sign_and_submit_then_watch(
                        &PairSigner::new(pair),
                        BaseExtrinsicParamsBuilder::new(),
                    )
                    .await?;
            } else {
                api.tx()
                    .balances()
                    .transfer_keep_alive(dest.into(), balance)?
                    .sign_and_submit_then_watch(
                        &PairSigner::new(pair),
                        BaseExtrinsicParamsBuilder::new(),
                    )
                    .await?;
            }
        }
Éloïs's avatar
Éloïs committed
    }

    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?;
Éloïs's avatar
Éloïs committed
    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(())
}