From 44cefc17f57df4a0252157c8af234f6633884581 Mon Sep 17 00:00:00 2001
From: tuxmain <tuxmain@zettascript.org>
Date: Fri, 18 Nov 2022 00:33:10 +0100
Subject: [PATCH] feat: Smith commands

---
 src/commands.rs       |  1 +
 src/commands/smith.rs | 78 +++++++++++++++++++++++++++++++++++++++++++
 src/main.rs           | 26 ++++++++++++++-
 3 files changed, 104 insertions(+), 1 deletion(-)
 create mode 100644 src/commands/smith.rs

diff --git a/src/commands.rs b/src/commands.rs
index 15ad4fb..0ea4b71 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -2,5 +2,6 @@ pub mod expire;
 pub mod net_test;
 pub mod oneshot;
 pub mod revocation;
+pub mod smith;
 pub mod sudo;
 pub mod transfer;
diff --git a/src/commands/smith.rs b/src/commands/smith.rs
new file mode 100644
index 0000000..12ea71c
--- /dev/null
+++ b/src/commands/smith.rs
@@ -0,0 +1,78 @@
+use crate::{gdev, Client};
+
+use anyhow::{anyhow, Result};
+use sp_core::{crypto::AccountId32, sr25519::Pair, Pair as _};
+use std::ops::Deref;
+use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner};
+
+type SessionKeys = [u8; 128];
+
+pub async fn rotate_keys(client: &Client) -> Result<SessionKeys> {
+    client
+        .rpc()
+        .rotate_keys()
+        .await?
+        .deref()
+        .try_into()
+        .map_err(|e| anyhow!("Session keys have wrong length: {:?}", e))
+}
+
+pub async fn set_session_keys(pair: Pair, client: Client, session_keys: SessionKeys) -> Result<()> {
+    client
+        .tx()
+        .sign_and_submit_then_watch(
+            &gdev::tx()
+                .authority_members()
+                .set_session_keys(session_keys),
+            &PairSigner::new(pair),
+            BaseExtrinsicParamsBuilder::new(),
+        )
+        .await?;
+
+    Ok(())
+}
+
+pub async fn update_session_keys(pair: Pair, client: Client) -> Result<()> {
+    let session_keys = rotate_keys(&client).await?;
+    set_session_keys(pair, client, session_keys).await
+}
+
+pub async fn go_online(pair: Pair, client: Client) -> Result<()> {
+    if client
+        .storage()
+        .fetch(
+            &gdev::storage()
+                .session()
+                .next_keys(AccountId32::from(pair.public())),
+            None,
+        )
+        .await?
+        .is_none()
+    {
+        return Err(anyhow!("This account has not set session keys!"));
+    }
+
+    client
+        .tx()
+        .sign_and_submit_then_watch(
+            &gdev::tx().authority_members().go_online(),
+            &PairSigner::new(pair),
+            BaseExtrinsicParamsBuilder::new(),
+        )
+        .await?;
+
+    Ok(())
+}
+
+pub async fn go_offline(pair: Pair, client: Client) -> Result<()> {
+    client
+        .tx()
+        .sign_and_submit_then_watch(
+            &gdev::tx().authority_members().go_offline(),
+            &PairSigner::new(pair),
+            BaseExtrinsicParamsBuilder::new(),
+        )
+        .await?;
+
+    Ok(())
+}
diff --git a/src/main.rs b/src/main.rs
index bf047ee..ac32f55 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -106,6 +106,8 @@ pub enum Subcommand {
     },
     /// Generate a revocation document for the provided account
     GenRevocDoc,
+    GoOffline,
+    GoOnline,
     OneshotBalance {
         account: sp_core::crypto::AccountId32,
     },
@@ -118,7 +120,9 @@ pub enum Subcommand {
         actual_repart: Option<u32>,
     },
     #[clap(hide = true)]
-    SpamRoll { actual_repart: usize },
+    SpamRoll {
+        actual_repart: usize,
+    },
     SudoSetKey {
         new_key: sp_core::crypto::AccountId32,
     },
@@ -128,6 +132,8 @@ pub enum Subcommand {
         #[clap(short = 'k')]
         keep_alive: bool,
     },
+    /// Rotate and set session keys
+    UpdateKeys,
 }
 
 #[tokio::main(flavor = "current_thread")]
@@ -164,6 +170,10 @@ async fn main() -> Result<()> {
         (None, None) => (None, None),
     };
 
+    if let Some(account_id) = &account_id {
+        println!("Account address: {}", account_id);
+    }
+
     let client = Client::new().await.unwrap();
 
     if let Some(account_id) = &account_id {
@@ -222,6 +232,14 @@ async fn main() -> Result<()> {
             )
             .await?
         }
+        Subcommand::GoOffline => {
+            commands::smith::go_offline(pair.expect("This subcommand needs a secret."), client)
+                .await?
+        }
+        Subcommand::GoOnline => {
+            commands::smith::go_online(pair.expect("This subcommand needs a secret."), client)
+                .await?
+        }
         Subcommand::OneshotBalance { account } => {
             commands::oneshot::oneshot_account_balance(client, account).await?
         }
@@ -267,6 +285,12 @@ async fn main() -> Result<()> {
             )
             .await?
         }
+        Subcommand::UpdateKeys => commands::smith::update_session_keys(
+            pair.expect("This subcommand needs a secret."),
+            client,
+        )
+        .await
+        .unwrap(),
     }
 
     Ok(())
-- 
GitLab