From 7fb2a00139da3aa42928b1dbff68db5a33abb47f Mon Sep 17 00:00:00 2001
From: tuxmain <tuxmain@zettascript.org>
Date: Thu, 17 Nov 2022 00:12:04 +0100
Subject: [PATCH] ref: split commands into modules

---
 src/cache.rs               |   4 +-
 src/commands.rs            |   6 +
 src/commands/expire.rs     | 193 +++++++++++++++
 src/commands/net_test.rs   |  95 ++++++++
 src/commands/oneshot.rs    | 122 ++++++++++
 src/commands/revocation.rs |  23 ++
 src/commands/sudo.rs       |  18 ++
 src/commands/transfer.rs   |  37 +++
 src/main.rs                | 480 +++++--------------------------------
 9 files changed, 559 insertions(+), 419 deletions(-)
 create mode 100644 src/commands.rs
 create mode 100644 src/commands/expire.rs
 create mode 100644 src/commands/net_test.rs
 create mode 100644 src/commands/oneshot.rs
 create mode 100644 src/commands/revocation.rs
 create mode 100644 src/commands/sudo.rs
 create mode 100644 src/commands/transfer.rs

diff --git a/src/cache.rs b/src/cache.rs
index 16836c5..f22b2a9 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -1,4 +1,4 @@
-use crate::gdev_300;
+use crate::gdev;
 use crate::indexer::*;
 use crate::Client;
 
@@ -33,7 +33,7 @@ impl<'a> IdentityCache<'a> {
                         .client
                         .storage()
                         .fetch(
-                            &gdev_300::storage().identity().identities(identity_id),
+                            &gdev::storage().identity().identities(identity_id),
                             Some(parent_hash),
                         )
                         .await?
diff --git a/src/commands.rs b/src/commands.rs
new file mode 100644
index 0000000..15ad4fb
--- /dev/null
+++ b/src/commands.rs
@@ -0,0 +1,6 @@
+pub mod expire;
+pub mod net_test;
+pub mod oneshot;
+pub mod revocation;
+pub mod sudo;
+pub mod transfer;
diff --git a/src/commands/expire.rs b/src/commands/expire.rs
new file mode 100644
index 0000000..70a5c80
--- /dev/null
+++ b/src/commands/expire.rs
@@ -0,0 +1,193 @@
+use crate::{cache, gdev, Args, Client};
+
+use anyhow::Result;
+use futures::join;
+use std::collections::BTreeMap;
+
+pub async fn monitor_expirations(
+    client: Client,
+    blocks: u32,
+    sessions: u32,
+    args: &Args,
+) -> Result<()> {
+    let gql_client = reqwest::Client::builder()
+        .user_agent("gcli/0.1.0")
+        .build()?;
+
+    let parent_hash = client
+        .storage()
+        .fetch(&gdev::storage().system().parent_hash(), None)
+        .await?
+        .unwrap();
+    let addr_current_block = gdev::storage().system().number();
+    let addr_current_session = gdev::storage().session().current_index();
+    let (current_block, current_session) = join!(
+        client
+            .storage()
+            .fetch(&addr_current_block, Some(parent_hash)),
+        client
+            .storage()
+            .fetch(&addr_current_session, Some(parent_hash),)
+    );
+    let current_block = current_block?.unwrap();
+    let current_session = current_session?.unwrap();
+
+    let end_block = current_block + blocks;
+    let end_session = current_session + sessions;
+
+    let mut identity_cache = cache::IdentityCache::new(
+        &client,
+        if args.no_indexer {
+            None
+        } else {
+            Some((&gql_client, &args.indexer))
+        },
+    );
+
+    // Rotate keys
+    let mut must_rotate_keys_before_iter = client
+        .storage()
+        .iter(
+            gdev::storage()
+                .authority_members()
+                .must_rotate_keys_before(0),
+            10,
+            Some(parent_hash),
+        )
+        .await?;
+    let mut must_rotate_keys_before = BTreeMap::new();
+    while let Some((k, v)) = must_rotate_keys_before_iter.next().await? {
+        let session_index = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
+        if session_index < end_session {
+            must_rotate_keys_before.insert(session_index - current_session, v);
+        }
+    }
+
+    println!("\nAuthority members:");
+    for (sessions_left, identity_ids) in must_rotate_keys_before {
+        println!("Must rotate keys before {} sessions:", sessions_left);
+        for identity_id in identity_ids {
+            println!(
+                "  {} ({})",
+                identity_cache
+                    .fetch_identity(identity_id, parent_hash)
+                    .await
+                    .unwrap_or_else(|_| "?".into()),
+                identity_id
+            );
+        }
+    }
+
+    // Certifications
+    let mut basic_certs_iter = client
+        .storage()
+        .iter(
+            gdev::storage().cert().storage_certs_removable_on(0),
+            10,
+            Some(parent_hash),
+        )
+        .await?;
+    let mut basic_certs = BTreeMap::new();
+    while let Some((k, v)) = basic_certs_iter.next().await? {
+        let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
+        if block_number < end_block {
+            basic_certs.insert(block_number - current_block, v);
+        }
+    }
+
+    let mut smith_certs_iter = client
+        .storage()
+        .iter(
+            gdev::storage().smiths_cert().storage_certs_removable_on(0),
+            10,
+            Some(parent_hash),
+        )
+        .await?;
+    let mut smith_certs = BTreeMap::new();
+    while let Some((k, v)) = smith_certs_iter.next().await? {
+        let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
+        if block_number < end_block {
+            smith_certs.insert(block_number - current_block, v);
+        }
+    }
+
+    for (title, certs) in [
+        ("Certifications", basic_certs),
+        ("Smith certifications", smith_certs),
+    ] {
+        println!("\n{}:", title);
+        for (blocks_left, certs) in certs {
+            println!("{} blocks before expiration:", blocks_left);
+            for (issuer_id, receiver_id) in certs {
+                println!(
+                    "  {} ({}) -> {} ({})",
+                    identity_cache
+                        .fetch_identity(issuer_id, parent_hash)
+                        .await
+                        .unwrap_or_else(|_| "?".into()),
+                    issuer_id,
+                    identity_cache
+                        .fetch_identity(receiver_id, parent_hash)
+                        .await
+                        .unwrap_or_else(|_| "?".into()),
+                    receiver_id,
+                );
+            }
+        }
+    }
+
+    // Memberships
+    let mut basic_membership_iter = client
+        .storage()
+        .iter(
+            gdev::storage().membership().memberships_expire_on(0),
+            10,
+            Some(parent_hash),
+        )
+        .await?;
+    let mut basic_memberships = BTreeMap::new();
+    while let Some((k, v)) = basic_membership_iter.next().await? {
+        let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
+        if block_number < end_block {
+            basic_memberships.insert(block_number - current_block, v);
+        }
+    }
+
+    let mut smith_membership_iter = client
+        .storage()
+        .iter(
+            gdev::storage().smiths_membership().memberships_expire_on(0),
+            10,
+            Some(parent_hash),
+        )
+        .await?;
+    let mut smith_memberships = BTreeMap::new();
+    while let Some((k, v)) = smith_membership_iter.next().await? {
+        let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
+        if block_number < end_block {
+            smith_memberships.insert(block_number - current_block, v);
+        }
+    }
+
+    for (title, memberships) in [
+        ("Memberships", basic_memberships),
+        ("Smith memberships", smith_memberships),
+    ] {
+        println!("\n{}:", title);
+        for (blocks_left, membership) in memberships {
+            println!("{} blocks before expiration:", blocks_left);
+            for identity_id in membership {
+                println!(
+                    "  {} ({})",
+                    identity_cache
+                        .fetch_identity(identity_id, parent_hash)
+                        .await
+                        .unwrap_or_else(|_| "?".into()),
+                    identity_id,
+                );
+            }
+        }
+    }
+
+    Ok(())
+}
diff --git a/src/commands/net_test.rs b/src/commands/net_test.rs
new file mode 100644
index 0000000..4689b34
--- /dev/null
+++ b/src/commands/net_test.rs
@@ -0,0 +1,95 @@
+use crate::{gdev, Client, GdevConfig};
+
+use anyhow::{anyhow, Result};
+use sp_core::{crypto::AccountId32, sr25519::Pair, DeriveJunction, Pair as _};
+use subxt::ext::sp_runtime::MultiAddress;
+use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
+
+pub async fn repart(
+    pair: Pair,
+    client: Client,
+    target: u32,
+    actual_repart: Option<u32>,
+) -> Result<()> {
+    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())
+            .await?
+            .wait_for_in_block()
+            .await?;
+        signer.increment_nonce();*/
+
+        if let Some(pair_i_account) = client
+            .storage()
+            .fetch(
+                &gdev::storage().system().account(&pair_i.public().into()),
+                None,
+            )
+            .await?
+        {
+            logs::info!("account //{} balance: {}", i, pair_i_account.data.free);
+        }
+    }
+
+    Ok(())
+}
+
+pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Result<()> {
+    let mut pairs =
+        Vec::<(PairSigner<GdevConfig, 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 = client
+                .tx()
+                .sign_and_submit_then_watch(
+                    &gdev::tx().balances().transfer(MultiAddress::Id(dest), 1),
+                    &pairs[i].0,
+                    BaseExtrinsicParamsBuilder::new(),
+                )
+                .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 = client
+            .tx()
+            .sign_and_submit_then_watch(
+                &gdev::tx().balances().transfer(MultiAddress::Id(dest), 1),
+                &pairs[actual_repart - 1].0,
+                BaseExtrinsicParamsBuilder::new(),
+            )
+            .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?;
+        }
+    }
+}
diff --git a/src/commands/oneshot.rs b/src/commands/oneshot.rs
new file mode 100644
index 0000000..3e6d3c3
--- /dev/null
+++ b/src/commands/oneshot.rs
@@ -0,0 +1,122 @@
+use crate::{gdev, Client};
+
+use anyhow::Result;
+use sp_core::{crypto::AccountId32, sr25519::Pair};
+use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
+
+pub async fn create_oneshot_account(
+    pair: Pair,
+    client: Client,
+    balance: u64,
+    dest: AccountId32,
+) -> Result<()> {
+    client
+        .tx()
+        .sign_and_submit_then_watch(
+            &gdev::tx()
+                .oneshot_account()
+                .create_oneshot_account(dest.into(), balance),
+            &PairSigner::new(pair),
+            BaseExtrinsicParamsBuilder::new(),
+        )
+        .await?;
+
+    Ok(())
+}
+
+pub async fn consume_oneshot_account(
+    pair: Pair,
+    client: Client,
+    dest: AccountId32,
+    dest_oneshot: bool,
+) -> Result<()> {
+    let number = client
+        .storage()
+        .fetch(&gdev::storage().system().number(), None)
+        .await?
+        .unwrap();
+    client
+        .tx()
+        .sign_and_submit_then_watch(
+            &gdev::tx().oneshot_account().consume_oneshot_account(
+                number,
+                if dest_oneshot {
+                    gdev::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
+                        dest.into(),
+                    )
+                } else {
+                    gdev::runtime_types::pallet_oneshot_account::types::Account::Normal(dest.into())
+                },
+            ),
+            &PairSigner::new(pair),
+            BaseExtrinsicParamsBuilder::new(),
+        )
+        .await?;
+
+    Ok(())
+}
+
+pub async fn consume_oneshot_account_with_remaining(
+    pair: Pair,
+    client: Client,
+    balance: u64,
+    dest: AccountId32,
+    dest_oneshot: bool,
+    remaining_to: AccountId32,
+    remaining_to_oneshot: bool,
+) -> Result<()> {
+    let number = client
+        .storage()
+        .fetch(&gdev::storage().system().number(), None)
+        .await?
+        .unwrap();
+    client
+        .tx()
+        .sign_and_submit_then_watch(
+            &gdev::tx()
+                .oneshot_account()
+                .consume_oneshot_account_with_remaining(
+                    number,
+                    if dest_oneshot {
+                        gdev::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
+                            dest.into(),
+                        )
+                    } else {
+                        gdev::runtime_types::pallet_oneshot_account::types::Account::Normal(
+                            dest.into(),
+                        )
+                    },
+                    if remaining_to_oneshot {
+                        gdev::runtime_types::pallet_oneshot_account::types::Account::Oneshot(
+                            remaining_to.into(),
+                        )
+                    } else {
+                        gdev::runtime_types::pallet_oneshot_account::types::Account::Normal(
+                            remaining_to.into(),
+                        )
+                    },
+                    balance,
+                ),
+            &PairSigner::new(pair),
+            BaseExtrinsicParamsBuilder::new(),
+        )
+        .await?;
+
+    Ok(())
+}
+
+pub async fn oneshot_account_balance(client: Client, account: AccountId32) -> Result<()> {
+    logs::info!(
+        "{}",
+        client
+            .storage()
+            .fetch(
+                &gdev::storage().oneshot_account().oneshot_accounts(&account),
+                None
+            )
+            .await?
+            .unwrap_or(0)
+    );
+
+    Ok(())
+}
diff --git a/src/commands/revocation.rs b/src/commands/revocation.rs
new file mode 100644
index 0000000..7595a6b
--- /dev/null
+++ b/src/commands/revocation.rs
@@ -0,0 +1,23 @@
+use crate::{gdev, Client};
+
+use anyhow::Result;
+use futures::join;
+use sp_core::{sr25519::Pair, Encode, Pair as _};
+
+pub async fn gen_revoc_doc(api: &Client, pair: &Pair) -> Result<()> {
+    let account_id: sp_core::crypto::AccountId32 = pair.public().into();
+    let addr_idty_index = gdev::storage().identity().identity_index_of(&account_id);
+    let addr_block_hash = gdev::storage().system().block_hash(0);
+    let (idty_index, genesis_hash) = join!(
+        api.storage().fetch(&addr_idty_index, None,),
+        api.storage().fetch(&addr_block_hash, None)
+    );
+    let idty_index = idty_index?.unwrap();
+    let genesis_hash = genesis_hash?.unwrap();
+    let payload = (b"revo", genesis_hash, idty_index).encode();
+    let signature = pair.sign(&payload);
+
+    println!("0x{}", hex::encode(signature));
+
+    Ok(())
+}
diff --git a/src/commands/sudo.rs b/src/commands/sudo.rs
new file mode 100644
index 0000000..ee06cdf
--- /dev/null
+++ b/src/commands/sudo.rs
@@ -0,0 +1,18 @@
+use crate::{gdev, Client};
+
+use anyhow::Result;
+use sp_core::{crypto::AccountId32, sr25519::Pair};
+use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
+
+pub async fn set_key(pair: Pair, client: Client, new_key: AccountId32) -> Result<()> {
+    client
+        .tx()
+        .sign_and_submit_then_watch(
+            &gdev::tx().sudo().set_key(new_key.into()),
+            &PairSigner::new(pair),
+            BaseExtrinsicParamsBuilder::new(),
+        )
+        .await?;
+
+    Ok(())
+}
diff --git a/src/commands/transfer.rs b/src/commands/transfer.rs
new file mode 100644
index 0000000..65c3b85
--- /dev/null
+++ b/src/commands/transfer.rs
@@ -0,0 +1,37 @@
+use crate::{gdev, Client};
+
+use anyhow::Result;
+use sp_core::{crypto::AccountId32, sr25519::Pair};
+use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
+
+pub async fn transfer(
+    pair: Pair,
+    client: Client,
+    balance: u64,
+    dest: AccountId32,
+    keep_alive: bool,
+) -> Result<()> {
+    if keep_alive {
+        client
+            .tx()
+            .sign_and_submit_then_watch(
+                &gdev::tx().balances().transfer(dest.into(), balance),
+                &PairSigner::new(pair),
+                BaseExtrinsicParamsBuilder::new(),
+            )
+            .await?;
+    } else {
+        client
+            .tx()
+            .sign_and_submit_then_watch(
+                &gdev::tx()
+                    .balances()
+                    .transfer_keep_alive(dest.into(), balance),
+                &PairSigner::new(pair),
+                BaseExtrinsicParamsBuilder::new(),
+            )
+            .await?;
+    }
+
+    Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index b52a68f..bf047ee 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,21 +1,18 @@
 mod cache;
+mod commands;
 mod indexer;
 
 use anyhow::{anyhow, Result};
 use clap::Parser;
 use codec::Encode;
-use futures::join;
 use sp_core::{
-    crypto::{AccountId32, DeriveJunction, Pair as _, Ss58Codec},
+    crypto::{Pair as _, Ss58Codec},
     sr25519::Pair,
 };
-use std::collections::BTreeMap;
 use std::str::FromStr;
-use subxt::ext::sp_runtime::MultiAddress;
-use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
 
 #[subxt::subxt(runtime_metadata_path = "res/metadata.scale")]
-pub mod gdev_300 {}
+pub mod gdev {}
 
 pub type Client = subxt::OnlineClient<GdevConfig>;
 
@@ -56,7 +53,7 @@ impl From<u64> for Tip {
 
 #[derive(Parser, Debug)]
 #[clap(author, version, about, long_about = None)]
-struct Args {
+pub struct Args {
     #[clap(subcommand)]
     pub subcommand: Subcommand,
 
@@ -169,14 +166,10 @@ async fn main() -> Result<()> {
 
     let client = Client::new().await.unwrap();
 
-    let gql_client = reqwest::Client::builder()
-        .user_agent("gcli/0.1.0")
-        .build()?;
-
     if let Some(account_id) = &account_id {
         let account = client
             .storage()
-            .fetch(&gdev_300::storage().system().account(account_id), None)
+            .fetch(&gdev::storage().system().account(account_id), None)
             .await?
             .expect("Cannot fetch account");
         logs::info!("Account free balance: {}", account.data.free);
@@ -184,46 +177,22 @@ async fn main() -> Result<()> {
 
     match args.subcommand {
         Subcommand::CreateOneshot { balance, dest } => {
-            let pair = pair.expect("This subcommand needs a secret.");
-
-            client
-                .tx()
-                .sign_and_submit_then_watch(
-                    &gdev_300::tx()
-                        .oneshot_account()
-                        .create_oneshot_account(dest.into(), balance),
-                    &PairSigner::new(pair),
-                    BaseExtrinsicParamsBuilder::new(),
-                )
-                .await?;
+            commands::oneshot::create_oneshot_account(
+                pair.expect("This subcommand needs a secret."),
+                client,
+                balance,
+                dest,
+            )
+            .await?
         }
         Subcommand::ConsumeOneshot { dest, dest_oneshot } => {
-            let pair = pair.expect("This subcommand needs a secret.");
-
-            let number = client
-                .storage()
-                .fetch(&gdev_300::storage().system().number(), None)
-                .await?
-                .unwrap();
-            client.tx()
-                .sign_and_submit_then_watch(&gdev_300::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(),
-                        )
-                    },
-                ),
-                    &PairSigner::new(pair),
-                    BaseExtrinsicParamsBuilder::new(),
-                )
-                .await?;
+            commands::oneshot::consume_oneshot_account(
+                pair.expect("This subcommand needs a secret."),
+                client,
+                dest,
+                dest_oneshot,
+            )
+            .await?
         }
         Subcommand::ConsumeOneshotWithRemaining {
             balance,
@@ -232,396 +201,73 @@ async fn main() -> Result<()> {
             remaining_to,
             remaining_to_oneshot,
         } => {
-            let pair = pair.expect("This subcommand needs a secret.");
-
-            let number = client
-                .storage()
-                .fetch(&gdev_300::storage().system().number(), None)
-                .await?
-                .unwrap();
-            client.tx()
-                .sign_and_submit_then_watch(&gdev_300::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,
-                ),
-                    &PairSigner::new(pair),
-                    BaseExtrinsicParamsBuilder::new(),
-                )
-                .await?;
+            commands::oneshot::consume_oneshot_account_with_remaining(
+                pair.expect("This subcommand needs a secret."),
+                client,
+                balance,
+                dest,
+                dest_oneshot,
+                remaining_to,
+                remaining_to_oneshot,
+            )
+            .await?
         }
         Subcommand::Expire { blocks, sessions } => {
-            let parent_hash = client
-                .storage()
-                .fetch(&gdev_300::storage().system().parent_hash(), None)
-                .await?
-                .unwrap();
-            let addr_current_block = gdev_300::storage().system().number();
-            let addr_current_session = gdev_300::storage().session().current_index();
-            let (current_block, current_session) = join!(
-                client
-                    .storage()
-                    .fetch(&addr_current_block, Some(parent_hash)),
-                client
-                    .storage()
-                    .fetch(&addr_current_session, Some(parent_hash),)
-            );
-            let current_block = current_block?.unwrap();
-            let current_session = current_session?.unwrap();
-
-            let end_block = current_block + blocks;
-            let end_session = current_session + sessions;
-
-            let mut identity_cache = cache::IdentityCache::new(
-                &client,
-                if args.no_indexer {
-                    None
-                } else {
-                    Some((&gql_client, &args.indexer))
-                },
-            );
-
-            // Rotate keys
-            let mut must_rotate_keys_before_iter = client
-                .storage()
-                .iter(
-                    gdev_300::storage()
-                        .authority_members()
-                        .must_rotate_keys_before(0),
-                    10,
-                    Some(parent_hash),
-                )
-                .await?;
-            let mut must_rotate_keys_before = BTreeMap::new();
-            while let Some((k, v)) = must_rotate_keys_before_iter.next().await? {
-                let session_index = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
-                if session_index < end_session {
-                    must_rotate_keys_before.insert(session_index - current_session, v);
-                }
-            }
-
-            println!("\nAuthority members:");
-            for (sessions_left, identity_ids) in must_rotate_keys_before {
-                println!("Must rotate keys before {} sessions:", sessions_left);
-                for identity_id in identity_ids {
-                    println!(
-                        "  {} ({})",
-                        identity_cache
-                            .fetch_identity(identity_id, parent_hash)
-                            .await
-                            .unwrap_or_else(|_| "?".into()),
-                        identity_id
-                    );
-                }
-            }
-
-            // Certifications
-            let mut basic_certs_iter = client
-                .storage()
-                .iter(
-                    gdev_300::storage().cert().storage_certs_removable_on(0),
-                    10,
-                    Some(parent_hash),
-                )
-                .await?;
-            let mut basic_certs = BTreeMap::new();
-            while let Some((k, v)) = basic_certs_iter.next().await? {
-                let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
-                if block_number < end_block {
-                    basic_certs.insert(block_number - current_block, v);
-                }
-            }
-
-            let mut smith_certs_iter = client
-                .storage()
-                .iter(
-                    gdev_300::storage()
-                        .smiths_cert()
-                        .storage_certs_removable_on(0),
-                    10,
-                    Some(parent_hash),
-                )
-                .await?;
-            let mut smith_certs = BTreeMap::new();
-            while let Some((k, v)) = smith_certs_iter.next().await? {
-                let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
-                if block_number < end_block {
-                    smith_certs.insert(block_number - current_block, v);
-                }
-            }
-
-            for (title, certs) in [
-                ("Certifications", basic_certs),
-                ("Smith certifications", smith_certs),
-            ] {
-                println!("\n{}:", title);
-                for (blocks_left, certs) in certs {
-                    println!("{} blocks before expiration:", blocks_left);
-                    for (issuer_id, receiver_id) in certs {
-                        println!(
-                            "  {} ({}) -> {} ({})",
-                            identity_cache
-                                .fetch_identity(issuer_id, parent_hash)
-                                .await
-                                .unwrap_or_else(|_| "?".into()),
-                            issuer_id,
-                            identity_cache
-                                .fetch_identity(receiver_id, parent_hash)
-                                .await
-                                .unwrap_or_else(|_| "?".into()),
-                            receiver_id,
-                        );
-                    }
-                }
-            }
-
-            // Memberships
-            let mut basic_membership_iter = client
-                .storage()
-                .iter(
-                    gdev_300::storage().membership().memberships_expire_on(0),
-                    10,
-                    Some(parent_hash),
-                )
-                .await?;
-            let mut basic_memberships = BTreeMap::new();
-            while let Some((k, v)) = basic_membership_iter.next().await? {
-                let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
-                if block_number < end_block {
-                    basic_memberships.insert(block_number - current_block, v);
-                }
-            }
-
-            let mut smith_membership_iter = client
-                .storage()
-                .iter(
-                    gdev_300::storage()
-                        .smiths_membership()
-                        .memberships_expire_on(0),
-                    10,
-                    Some(parent_hash),
-                )
-                .await?;
-            let mut smith_memberships = BTreeMap::new();
-            while let Some((k, v)) = smith_membership_iter.next().await? {
-                let block_number = u32::from_le_bytes(k.as_ref()[40..44].try_into().unwrap());
-                if block_number < end_block {
-                    smith_memberships.insert(block_number - current_block, v);
-                }
-            }
-
-            for (title, memberships) in [
-                ("Memberships", basic_memberships),
-                ("Smith memberships", smith_memberships),
-            ] {
-                println!("\n{}:", title);
-                for (blocks_left, membership) in memberships {
-                    println!("{} blocks before expiration:", blocks_left);
-                    for identity_id in membership {
-                        println!(
-                            "  {} ({})",
-                            identity_cache
-                                .fetch_identity(identity_id, parent_hash)
-                                .await
-                                .unwrap_or_else(|_| "?".into()),
-                            identity_id,
-                        );
-                    }
-                }
-            }
+            commands::expire::monitor_expirations(client, blocks, sessions, &args).await?
         }
         Subcommand::GenRevocDoc => {
-            gen_revoc_doc(&client, &pair.expect("This subcommand needs a secret.")).await?
+            commands::revocation::gen_revoc_doc(
+                &client,
+                &pair.expect("This subcommand needs a secret."),
+            )
+            .await?
         }
         Subcommand::OneshotBalance { account } => {
-            logs::info!(
-                "{}",
-                client
-                    .storage()
-                    .fetch(
-                        &gdev_300::storage()
-                            .oneshot_account()
-                            .oneshot_accounts(&account),
-                        None
-                    )
-                    .await?
-                    .unwrap_or(0)
-            );
+            commands::oneshot::oneshot_account_balance(client, account).await?
         }
         Subcommand::Repart {
             target,
             actual_repart,
         } => {
-            let pair = pair.expect("This subcommand needs a secret.");
-
-            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())
-                    .await?
-                    .wait_for_in_block()
-                    .await?;
-                signer.increment_nonce();*/
-
-                if let Some(pair_i_account) = client
-                    .storage()
-                    .fetch(
-                        &gdev_300::storage()
-                            .system()
-                            .account(&pair_i.public().into()),
-                        None,
-                    )
-                    .await?
-                {
-                    logs::info!("account //{} balance: {}", i, pair_i_account.data.free);
-                }
-            }
+            commands::net_test::repart(
+                pair.expect("This subcommand needs a secret."),
+                client,
+                target,
+                actual_repart,
+            )
+            .await?
         }
         Subcommand::SpamRoll { actual_repart } => {
-            let pair = pair.expect("This subcommand needs a secret.");
-
-            let mut pairs =
-                Vec::<(PairSigner<GdevConfig, 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 = client
-                        .tx()
-                        .sign_and_submit_then_watch(
-                            &gdev_300::tx()
-                                .balances()
-                                .transfer(MultiAddress::Id(dest), 1),
-                            &pairs[i].0,
-                            BaseExtrinsicParamsBuilder::new(),
-                        )
-                        .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 = client
-                    .tx()
-                    .sign_and_submit_then_watch(
-                        &gdev_300::tx()
-                            .balances()
-                            .transfer(MultiAddress::Id(dest), 1),
-                        &pairs[actual_repart - 1].0,
-                        BaseExtrinsicParamsBuilder::new(),
-                    )
-                    .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?;
-                }
-            }
+            commands::net_test::spam_roll(
+                pair.expect("This subcommand needs a secret."),
+                client,
+                actual_repart,
+            )
+            .await?
         }
         Subcommand::SudoSetKey { new_key } => {
-            let pair = pair.expect("This subcommand needs a secret.");
-
-            client
-                .tx()
-                .sign_and_submit_then_watch(
-                    &gdev_300::tx().sudo().set_key(new_key.into()),
-                    &PairSigner::new(pair),
-                    BaseExtrinsicParamsBuilder::new(),
-                )
-                .await?;
+            commands::sudo::set_key(
+                pair.expect("This subcommand needs a secret."),
+                client,
+                new_key,
+            )
+            .await?
         }
         Subcommand::Transfer {
             balance,
             dest,
             keep_alive,
         } => {
-            let pair = pair.expect("This subcommand needs a secret.");
-
-            if keep_alive {
-                client
-                    .tx()
-                    .sign_and_submit_then_watch(
-                        &gdev_300::tx().balances().transfer(dest.into(), balance),
-                        &PairSigner::new(pair),
-                        BaseExtrinsicParamsBuilder::new(),
-                    )
-                    .await?;
-            } else {
-                client
-                    .tx()
-                    .sign_and_submit_then_watch(
-                        &gdev_300::tx()
-                            .balances()
-                            .transfer_keep_alive(dest.into(), balance),
-                        &PairSigner::new(pair),
-                        BaseExtrinsicParamsBuilder::new(),
-                    )
-                    .await?;
-            }
+            commands::transfer::transfer(
+                pair.expect("This subcommand needs a secret."),
+                client,
+                balance,
+                dest,
+                keep_alive,
+            )
+            .await?
         }
     }
 
     Ok(())
 }
-
-async fn gen_revoc_doc(api: &Client, pair: &Pair) -> Result<()> {
-    let account_id: sp_core::crypto::AccountId32 = pair.public().into();
-    let addr_idty_index = gdev_300::storage()
-        .identity()
-        .identity_index_of(&account_id);
-    let addr_block_hash = gdev_300::storage().system().block_hash(0);
-    let (idty_index, genesis_hash) = join!(
-        api.storage().fetch(&addr_idty_index, None,),
-        api.storage().fetch(&addr_block_hash, None)
-    );
-    let idty_index = idty_index?.unwrap();
-    let genesis_hash = genesis_hash?.unwrap();
-    let payload = (b"revo", genesis_hash, idty_index).encode();
-    let signature = pair.sign(&payload);
-
-    println!("0x{}", hex::encode(signature));
-
-    Ok(())
-}
-- 
GitLab