From 4f4524d3fb054bae3c810df370e8717d4e780867 Mon Sep 17 00:00:00 2001 From: Hugo Trentesaux <hugo.trentesaux@lilo.org> Date: Fri, 2 Jun 2023 11:55:16 +0200 Subject: [PATCH] Add smith certification --- Cargo.lock | 39 +------------- Cargo.toml | 2 +- README.md | 16 ++++++ res/metadata.scale | Bin 127211 -> 131872 bytes src/commands/expire.rs | 4 +- src/commands/identity.rs | 107 +++++++++++++++++++++------------------ src/commands/net_test.rs | 6 +-- src/commands/oneshot.rs | 2 +- src/commands/smith.rs | 76 ++++++++++++++++++++++++++- src/commands/transfer.rs | 2 +- src/main.rs | 11 ++-- 11 files changed, 164 insertions(+), 101 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03dd29b..52b2ad5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -855,7 +855,7 @@ dependencies = [ "futures", "graphql_client", "hex", - "logs", + "log", "parity-scale-codec", "reqwest", "rpassword", @@ -1509,16 +1509,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "logs" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "218ee85e6f2ed7e15253cbfcb61bf960ea797b4d8869d36f1a95bbc7fdc3686d" -dependencies = [ - "log", - "time", -] - [[package]] name = "lru" version = "0.7.8" @@ -3105,33 +3095,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "time" -version = "0.3.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" -dependencies = [ - "itoa", - "serde", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "time-macros" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" -dependencies = [ - "time-core", -] - [[package]] name = "tiny-bip39" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index e88715b..fce1a3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ env_logger = "0.10" futures = "0.3.27" graphql_client = { version = "0.12.0", features = ["reqwest"] } hex = "0.4.3" -logs = "0.7" +log = "0.4.17" reqwest = "0.11.14" rpassword = "7.2.0" serde = { version = "1.0", features = ["derive"] } diff --git a/README.md b/README.md index e04dce2..0f97ffb 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,22 @@ List certifications and session keys that will expire within one month: cargo run -- --url wss://gdev.p2p.legal:443/ws expire --blocks 432000 +#### Log level + +You can adjust the log level: + +``` +export RUST_LOG=gcli=info +``` + +#### Runtime metadata + +To update runtime metadata: + +``` +subxt metadata -f bytes > res/metadata.scale +``` + ### Smith You want to rotate keys and go online to start forging blocks. diff --git a/res/metadata.scale b/res/metadata.scale index d7c41820b07a0a2e6198009041d239e70602a2d9..3af1b14cabe3e2c9b3561e0ef21320d47923125d 100644 GIT binary patch delta 17827 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 delta 14532 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 YlRx#SaGk#D5d2LTzoa`Nb{!D^2h-g{-~a#s diff --git a/src/commands/expire.rs b/src/commands/expire.rs index d051ea8..a23a530 100644 --- a/src/commands/expire.rs +++ b/src/commands/expire.rs @@ -101,7 +101,7 @@ pub async fn monitor_expirations( let mut smith_certs_iter = client .storage() .iter( - gdev::storage().smiths_cert().storage_certs_removable_on(0), + gdev::storage().smith_cert().storage_certs_removable_on(0), 10, Some(parent_hash), ) @@ -162,7 +162,7 @@ pub async fn monitor_expirations( let mut smith_membership_iter = client .storage() .iter( - gdev::storage().smiths_membership().memberships_expire_on(0), + gdev::storage().smith_membership().memberships_expire_on(0), 10, Some(parent_hash), ) diff --git a/src/commands/identity.rs b/src/commands/identity.rs index 6cde145..e5ee3ca 100644 --- a/src/commands/identity.rs +++ b/src/commands/identity.rs @@ -1,5 +1,7 @@ use crate::{gdev, indexer::*, Args, Client}; +use crate::gdev::runtime_types::common_runtime::entities::IdtyData; +use crate::gdev::runtime_types::pallet_identity::types::*; use anyhow::{anyhow, Result}; use sp_core::{crypto::AccountId32, sr25519::Pair}; use std::str::FromStr; @@ -12,63 +14,50 @@ pub async fn get_identity( mut username: Option<String>, args: &Args, ) -> Result<()> { - let parent_hash = client - .storage() - .fetch(&gdev::storage().system().parent_hash(), None) - .await? - .unwrap(); - - let gql_client = reqwest::Client::builder() - .user_agent("gcli/0.1.0") - .build()?; - + // build indexer if not disabled let indexer = if args.no_indexer { None } else { Some(Indexer { - gql_client, + gql_client: reqwest::Client::builder() + .user_agent("gcli/0.1.0") + .build()?, gql_url: &args.indexer, }) }; - if let Some(account_id) = &account_id { - identity_id = client - .storage() - .fetch( - &gdev::storage().identity().identity_index_of(account_id), - Some(parent_hash), - ) - .await?; - } else if let Some(identity_id) = &identity_id { - account_id = client - .storage() - .fetch( - &gdev::storage().identity().identities(identity_id), - Some(parent_hash), - ) - .await? - .map(|idty| idty.owner_key); - } else if let Some(username) = &username { - let indexer = indexer.as_ref().ok_or(anyhow!( - "Cannot fetch identity from username without indexer." - ))?; - if let Some(pubkey) = indexer.pubkey_by_username(username).await? { - let some_account_id = AccountId32::from_str(&pubkey).map_err(|e| anyhow!(e))?; - identity_id = client - .storage() - .fetch( - &gdev::storage() - .identity() - .identity_index_of(&some_account_id), - Some(parent_hash), - ) - .await?; - account_id = Some(some_account_id); + // fetch missing information + match (&account_id, identity_id, &username) { + (None, Some(identity_id), None) => { + account_id = get_identity_by_index(client, identity_id) + .await? + .map(|idty| idty.owner_key); } - } else { - return Err(anyhow!("One argument is needed to fetch the identity.")); - } + (Some(account_id), None, None) => { + identity_id = get_idty_index_by_account_id(client, account_id).await?; + } + (None, None, Some(username)) => { + let indexer = indexer.as_ref().ok_or(anyhow!( + "Cannot fetch identity from username without indexer." + ))?; + if let Some(pubkey) = indexer.pubkey_by_username(username).await? { + // convert string to accountid + let fetched_account_id = AccountId32::from_str(&pubkey).map_err(|e| anyhow!(e))?; + // in the future, also ask indexer the identity index + identity_id = get_idty_index_by_account_id(client, &fetched_account_id).await?; + account_id = Some(fetched_account_id); + } else { + return Err(anyhow!("no identity found for this username")); + } + } + _ => { + return Err(anyhow!( + "One and only one argument is needed to fetch the identity." + )); + } + }; + // print result println!( "Account id: {}", account_id @@ -83,12 +72,34 @@ pub async fn get_identity( if let (Some(indexer), Some(account_id), None) = (&indexer, &account_id, &username) { username = indexer.username_by_pubkey(&account_id.to_string()).await?; } - println!("Username: {}", username.unwrap_or_default()); Ok(()) } +pub async fn get_idty_index_by_account_id( + client: Client, + account_id: &AccountId32, +) -> Result<Option<u32>> { + Ok(client + .storage() + .fetch( + &gdev::storage().identity().identity_index_of(account_id), + None, + ) + .await?) +} + +pub async fn get_identity_by_index( + client: Client, + idty_index: u32, +) -> Result<Option<IdtyValue<u32, AccountId32, IdtyData>>> { + Ok(client + .storage() + .fetch(&gdev::storage().identity().identities(idty_index), None) + .await?) +} + pub async fn create_identity(pair: Pair, client: Client, target: AccountId32) -> Result<()> { client .tx() diff --git a/src/commands/net_test.rs b/src/commands/net_test.rs index 335e0d6..ab2dff3 100644 --- a/src/commands/net_test.rs +++ b/src/commands/net_test.rs @@ -41,7 +41,7 @@ pub async fn repart( ) .await? { - logs::info!("account //{} balance: {}", i, pair_i_account.data.free); + log::info!("account //{} balance: {}", i, pair_i_account.data.free); } } @@ -76,7 +76,7 @@ pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Resu .submit_and_watch() .await?; nonce += 1; - logs::info!("send 1 cent from //{} to //{}", i, i + 1); + log::info!("send 1 cent from //{} to //{}", i, i + 1); watchers.push(watcher); } let dest: AccountId32 = pairs[0].1.clone(); @@ -89,7 +89,7 @@ pub async fn spam_roll(pair: Pair, client: Client, actual_repart: usize) -> Resu ) .await?; nonce += 1; - logs::info!("send 1 cent from //{} to //0", actual_repart - 1); + log::info!("send 1 cent from //{} to //0", actual_repart - 1); watchers.push(watcher); // Wait all transactions diff --git a/src/commands/oneshot.rs b/src/commands/oneshot.rs index 640cf9f..9933693 100644 --- a/src/commands/oneshot.rs +++ b/src/commands/oneshot.rs @@ -106,7 +106,7 @@ pub async fn consume_oneshot_account_with_remaining( } pub async fn oneshot_account_balance(client: Client, account: AccountId32) -> Result<()> { - logs::info!( + log::info!( "{}", client .storage() diff --git a/src/commands/smith.rs b/src/commands/smith.rs index 463c179..ca8928f 100644 --- a/src/commands/smith.rs +++ b/src/commands/smith.rs @@ -1,9 +1,10 @@ -use crate::{cache, gdev, indexer::*, Args, Client}; +use crate::indexer::Indexer; +use crate::*; use anyhow::{anyhow, Result}; use sp_core::{crypto::AccountId32, sr25519::Pair, Pair as _}; use std::ops::Deref; -use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner}; +use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner, TxStatus}; type SessionKeys = [u8; 128]; @@ -162,3 +163,74 @@ pub async fn online(client: Client, args: &Args) -> Result<()> { Ok(()) } + +/// emit a new smith cert from signer's identity to target identity +pub async fn emit_cert(args: Args, receiver: u32) -> Result<()> { + // issuer key + let pair = get_keys( + args.secret_format, + &args.address, + &args.secret, + NeededKeys::Secret, + )? + .1 + .unwrap(); + + // connect to client + let client = Client::from_url(&args.url).await.unwrap(); + + // get issuer index + let issuer = commands::identity::get_idty_index_by_account_id( + client.clone(), + &AccountId32::from(pair.public()), + ) + .await? + .ok_or(anyhow!("can not certify if not member"))?; + + // submit and track certification + cert(client, pair, issuer, receiver).await?; + + Ok(()) +} + +/// submit a certification and track progress +async fn cert(client: Client, pair: Pair, issuer: u32, receiver: u32) -> Result<()> { + let mut progress = client + .tx() + .sign_and_submit_then_watch( + &gdev::tx().smith_cert().add_cert(issuer, receiver), + &PairSigner::new(pair), + BaseExtrinsicParamsBuilder::new(), + ) + .await?; + + let in_block = loop { + if let Some(status) = progress.next_item().await { + match status? { + TxStatus::Ready => { + println!("transaction submitted to the network, waiting 6 seconds..."); + } + TxStatus::InBlock(in_block) => break in_block, + TxStatus::Invalid => { + println!("Invalid"); + } + _ => continue, + } + } + }; + + // get the block events and return if ExtrinsicFailed + let events = in_block.wait_for_success().await?; + // look for the expected event + let new_cert_event = events.find_first::<gdev::smith_cert::events::NewCert>()?; + let renew_cert_event = events.find_first::<gdev::smith_cert::events::RenewedCert>()?; + + if let Some(event) = new_cert_event { + println!("{event:?}"); + } + if let Some(event) = renew_cert_event { + println!("{event:?}"); + } + + Ok(()) +} diff --git a/src/commands/transfer.rs b/src/commands/transfer.rs index 4e51cf3..857dbde 100644 --- a/src/commands/transfer.rs +++ b/src/commands/transfer.rs @@ -4,7 +4,7 @@ use anyhow::Result; use sp_core::{crypto::AccountId32, sr25519::Pair}; use subxt::tx::{BaseExtrinsicParamsBuilder, PairSigner}; -type Call = gdev::runtime_types::gdev_runtime::Call; +type Call = gdev::runtime_types::gdev_runtime::RuntimeCall; type BalancesCall = gdev::runtime_types::pallet_balances::pallet::Call; pub async fn transfer( diff --git a/src/main.rs b/src/main.rs index 7b451af..f251d64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -56,11 +56,7 @@ pub struct Args { pub subcommand: Subcommand, /// Indexer URL - #[clap( - short, - long, - default_value = "https://gdev-indexer.p2p.legal/v1/graphql" - )] + #[clap(short, long, default_value = "http://localhost:8080/v1/graphql")] indexer: String, /// Do not use indexer #[clap(long)] @@ -154,6 +150,10 @@ pub enum Subcommand { SudoSetKey { new_key: sp_core::crypto::AccountId32, }, + /// Emit a smith certification + SmithCert { + to: u32, + }, /// List members of the technical committee TechMembers, /// List proposals to the technical committee @@ -419,6 +419,7 @@ async fn main() -> Result<()> { ) .await? } + Subcommand::SmithCert { to } => commands::smith::emit_cert(args, to).await?, Subcommand::TechMembers => { commands::collective::technical_committee_members( Client::from_url(&args.url).await.unwrap(), -- GitLab