diff --git a/Cargo.lock b/Cargo.lock index 03dd29be194292551798e936a2eddb312e1d4907..52b2ad57fd20b1aa63b34d06ce1bfeee6dd7d4f1 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 e88715bd18683a78811bc4ae1ed413d31cca4bc7..fce1a3e18202fa60a0bc506af615dedb9a601695 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 e04dce2e703ae122ac6c11f6cdb79f7fce1d920c..0f97ffbfc17acfd1e540c4dd431b368a2ae0b1dd 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 Binary files a/res/metadata.scale and b/res/metadata.scale differ diff --git a/src/commands/expire.rs b/src/commands/expire.rs index d051ea80f2719ad208d4476f38ee8a3df72fa37c..a23a5300509ee3e0f4e249352e8d7f25466af9f8 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 6cde145611f6cca3fa09fea638539055f4b6fbc0..e5ee3ca056d4cdb710997b37dad424012482e47e 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 335e0d6374bae3d1bbe0603c72181c3aae4c42dd..ab2dff3f711eb57379da6963d860dd79ca94cff6 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 640cf9f6fe5073cb49305272e4db8ceeb6f84616..99336933a87ca6f26b2adc2f9ee4b1b6025d211b 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 463c179a77ba0a43add4ed6bd7072cc38daa60aa..ca8928f1497415efc46a41c92f044177ee84f2cf 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 4e51cf3124cff223a6f9eab547664a7ec40c9b3b..857dbde5927b76ecbfbec225bffe5abc13acb671 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 7b451af894722ccb7c8bfd79520d708e335b1374..f251d642020bb5225bdfd4eb2a389159ec868b49 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(),