diff --git a/src/commands.rs b/src/commands.rs index 18967d6f7c573910cd82808d5c12f35c3a25b8e9..ea2a9620692d730315bb4f1bd1ca580f1e172042 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,4 +1,5 @@ pub mod account; +pub mod blockchain; pub mod collective; pub mod expire; pub mod identity; diff --git a/src/commands/blockchain.rs b/src/commands/blockchain.rs new file mode 100644 index 0000000000000000000000000000000000000000..626ace7ae4becfbcd301de8d3e89532d1d6cb7e2 --- /dev/null +++ b/src/commands/blockchain.rs @@ -0,0 +1,11 @@ +use crate::*; + +/// get genesis hash +pub async fn fetch_genesis_hash(data: &Data) -> Result<Hash, anyhow::Error> { + Ok(data + .client() + .storage() + .fetch(&runtime::storage().system().block_hash(0), None) + .await? + .unwrap()) +} diff --git a/src/conf.rs b/src/conf.rs index ddedd20411daaf9b23e892a8a0775b6bbb9689ad..89a4c93e72c7414294d2b1734d96341e0c355913 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -40,6 +40,8 @@ pub enum Subcommand { Where, /// Show config Show, + /// Save config as modified by command line arguments + Save, } /// handle conf command @@ -55,6 +57,9 @@ pub fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<()> { Subcommand::Show => { println!("{:?}", data.cfg); } + Subcommand::Save => { + confy::store(APP_NAME, None, &data.cfg).expect("unable to write config"); + } }; Ok(()) diff --git a/src/data.rs b/src/data.rs index f09fb7cf7984c5ed09d81d757ce980339f984d6f..8b28bab3cde0b748d5879234a1f177a60964f3d1 100644 --- a/src/data.rs +++ b/src/data.rs @@ -168,33 +168,34 @@ impl Data { self } /// build a client from url - // TODO get client from a pre-defined list - pub async fn build_client(mut self) -> Self { + pub async fn build_client(mut self) -> Result<Self, GcliError> { let duniter_endpoint = self.cfg.duniter_endpoint.clone(); - self.client = Some( - Client::from_url(&duniter_endpoint) - .await - .unwrap_or_else(|e| { - panic!( - "could not establish connection with the server {}, due to error {}", - duniter_endpoint, dbg!(e) - ) - }), - ); - self + self.client = Some(Client::from_url(&duniter_endpoint).await.map_err(|e| { + GcliError::Duniter(format!( + "could not establish connection with the server {}, due to error {}", + duniter_endpoint, + dbg!(e) + )) + })?); + self.genesis_hash = commands::blockchain::fetch_genesis_hash(&self).await?; + Ok(self) } /// build an indexer if not disabled - // TODO check that indexer matches client - pub fn build_indexer(mut self) -> Result<Self, anyhow::Error> { - self.indexer = if self.args.no_indexer { - None + pub async fn build_indexer(mut self) -> Result<Self, anyhow::Error> { + if self.args.no_indexer { + log::info!("called build_indexer while providing no_indexer"); + self.indexer = None; } else { - Some(Indexer { + self.indexer = Some(Indexer { gql_client: reqwest::Client::builder() .user_agent("gcli/0.1.0") .build()?, gql_url: self.cfg.indexer_endpoint.clone(), - }) + }); + self.indexer_genesis_hash = self.indexer().fetch_genesis_hash().await?; + if self.indexer_genesis_hash != self.genesis_hash { + println!("âš ï¸ indexer does not have the same genesis hash as blockchain") + } }; Ok(self) } @@ -208,21 +209,6 @@ impl Data { ); Ok(self) } - /// get genesis hash - pub async fn fetch_genesis_hash(mut self) -> Result<Self, anyhow::Error> { - self.genesis_hash = self - .client() - .storage() - .fetch(&runtime::storage().system().block_hash(0), None) - .await? - .unwrap(); - Ok(self) - } - /// get indexer genesis hash - pub async fn fetch_indexer_genesis_hash(mut self) -> Result<Self, anyhow::Error> { - self.indexer_genesis_hash = self.indexer().fetch_genesis_hash().await?; - Ok(self) - } /// get properties pub async fn fetch_system_properties(mut self) -> Result<Self, anyhow::Error> { let system_properties = self.client().rpc().system_properties().await?; diff --git a/src/indexer.rs b/src/indexer.rs index fd178157c7b07d3fcde0be88aea53dae77153ad3..ab376b892a0d536d16c6a72896159a1794593186 100644 --- a/src/indexer.rs +++ b/src/indexer.rs @@ -97,7 +97,7 @@ impl Indexer { ) .await? .data - .unwrap() // must have a data field + .ok_or(GcliError::Indexer("could not reach indexer".to_string()))? .block .first() .unwrap() // must have one and only one block matching request @@ -115,13 +115,13 @@ pub enum Subcommand { ShowEndpoint, /// Fetch latest indexed block LatestBlock, - /// Fetch genesis block hash - GenesisHash, + /// Check that indexer and node are on the same network (same genesis hash) + Check, } pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<()> { // build indexer because it is needed for all subcommands - let data = data.build_indexer()?; + let mut data = data.build_indexer().await?; // match subcommand match command { Subcommand::ShowEndpoint => { @@ -134,11 +134,24 @@ pub async fn handle_command(data: Data, command: Subcommand) -> anyhow::Result<( data.indexer().fetch_latest_block().await? ); } - Subcommand::GenesisHash => { - println!( - "hash of genesis block is: {}", - data.indexer().fetch_genesis_hash().await? - ); + Subcommand::Check => { + data = data.build_client().await?; + if data.genesis_hash == data.indexer_genesis_hash { + println!( + "{} and {} have the same genesis hash: {}", + data.cfg.duniter_endpoint, + data.indexer().gql_url, + data.genesis_hash + ); + } else { + println!( + "âš ï¸ {} ({}) and {} ({}) do not share same genesis", + data.cfg.duniter_endpoint, + data.genesis_hash, + data.indexer().gql_url, + data.indexer_genesis_hash + ); + } } }; diff --git a/src/main.rs b/src/main.rs index 73ea92bb082211c302c1941d6d612244082224ea..09c643a28b65ec871971e9095f10db8d291c26dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -96,7 +96,6 @@ pub struct Args { /// Chose target network #[clap(short, long)] network: Option<String>, - } /// track progress of transaction on the network @@ -116,6 +115,10 @@ pub async fn track_progress(progress: TxProgress) -> anyhow::Result<()> { pub enum GcliError { /// error coming from subxt Subxt(subxt::Error), + /// error coming from duniter + Duniter(String), + /// error coming from indexer + Indexer(String), /// error coming from anyhow Anyhow(anyhow::Error), } @@ -257,8 +260,6 @@ pub enum Subcommand { RuntimeInfo, /// Check current block CurrentBlock, - /// Check that indexer and node are on the same network - CheckIndexerAgainstBlockchain, /// Indexer subcommands #[clap(subcommand)] Indexer(indexer::Subcommand), @@ -281,7 +282,7 @@ async fn main() -> Result<(), GcliError> { data = data .build_address() .build_client() - .await + .await? .fetch_system_properties() .await?; commands::account::get_balance(data).await? @@ -291,13 +292,13 @@ async fn main() -> Result<(), GcliError> { println!("address is: {}", data.address()); } Subcommand::CreateIdentity { target } => { - data = data.build_client().await.build_keypair(); + data = data.build_client().await?.build_keypair(); let progress = commands::identity::create_identity(data.keypair(), data.client(), target).await?; track_progress(progress).await? } Subcommand::ConfirmIdentity { name } => { - data = data.build_client().await.build_keypair(); + data = data.build_client().await?.build_keypair(); let progress = commands::identity::confirm_identity(data.keypair(), data.client(), name).await?; track_progress(progress).await? @@ -305,17 +306,15 @@ async fn main() -> Result<(), GcliError> { Subcommand::RevokeIdentity => { data = data .build_client() - .await + .await? .build_keypair() .fetch_idty_index() - .await? - .fetch_genesis_hash() .await?; let progress = commands::identity::revoke_identity(data).await?; track_progress(progress).await? } Subcommand::CreateOneshot { balance, dest } => { - data = data.build_client().await; + data = data.build_client().await?; commands::oneshot::create_oneshot_account( get_keys( args.secret_format, @@ -332,7 +331,7 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::ConsumeOneshot { dest, dest_oneshot } => { - data = data.build_client().await; + data = data.build_client().await?; commands::oneshot::consume_oneshot_account( get_keys( args.secret_format, @@ -355,7 +354,7 @@ async fn main() -> Result<(), GcliError> { remaining_to, remaining_to_oneshot, } => { - data = data.build_client().await; + data = data.build_client().await?; commands::oneshot::consume_oneshot_account_with_remaining( get_keys( args.secret_format, @@ -375,7 +374,7 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::Expire { blocks, sessions } => { - data = data.build_client().await; + data = data.build_client().await?; commands::expire::monitor_expirations(&data, blocks, sessions).await? } Subcommand::Identity { @@ -383,7 +382,7 @@ async fn main() -> Result<(), GcliError> { identity_id, ref username, } => { - data = data.build_client().await; + data = data.build_client().await?; commands::identity::get_identity( &data, account_id.clone(), @@ -395,16 +394,14 @@ async fn main() -> Result<(), GcliError> { Subcommand::GenRevocDoc => { data = data .build_client() - .await + .await? .build_keypair() .fetch_idty_index() - .await? - .fetch_genesis_hash() .await?; commands::revocation::print_revoc_sig(&data) } Subcommand::GoOffline => { - data = data.build_client().await; + data = data.build_client().await?; commands::smith::go_offline( get_keys( args.secret_format, @@ -419,7 +416,7 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::GoOnline => { - data = data.build_client().await; + data = data.build_client().await?; commands::smith::go_online( get_keys( args.secret_format, @@ -434,18 +431,18 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::OneshotBalance { account } => { - data = data.build_client().await; + data = data.build_client().await?; commands::oneshot::oneshot_account_balance(data.client(), account).await? } Subcommand::Online => { - data = data.build_client().await; + data = data.build_client().await?; commands::smith::online(&data).await? } Subcommand::Repart { target, actual_repart, } => { - data = data.build_client().await; + data = data.build_client().await?; commands::net_test::repart( get_keys( args.secret_format, @@ -462,7 +459,7 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::SpamRoll { actual_repart } => { - data = data.build_client().await; + data = data.build_client().await?; commands::net_test::spam_roll( get_keys( args.secret_format, @@ -478,28 +475,28 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::SudoSetKey { new_key } => { - data = data.build_keypair().build_client().await; + data = data.build_keypair().build_client().await?; commands::sudo::set_key(data.keypair(), data.client(), new_key).await? } Subcommand::SmithCert { to } => { data = data .build_client() - .await + .await? .build_keypair() .fetch_idty_index() .await?; commands::smith::cert(data.client(), data.keypair(), data.idty_index(), to).await? } Subcommand::TechMembers => { - data = data.build_client().await; + data = data.build_client().await?; commands::collective::technical_committee_members(&data).await? } Subcommand::TechProposals => { - data = data.build_client().await; + data = data.build_client().await?; commands::collective::technical_committee_proposals(data.client()).await? } Subcommand::TechVote { hash, index, vote } => { - data = data.build_client().await; + data = data.build_client().await?; let vote = match vote { 0 => false, 1 => true, @@ -526,7 +523,7 @@ async fn main() -> Result<(), GcliError> { dest, keep_alive, } => { - data = data.build_client().await; + data = data.build_client().await?; commands::transfer::transfer( get_keys( args.secret_format, @@ -544,7 +541,7 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::TransferMultiple { amount, dests } => { - data = data.build_client().await; + data = data.build_client().await?; commands::transfer::transfer_multiple( get_keys( args.secret_format, @@ -561,7 +558,7 @@ async fn main() -> Result<(), GcliError> { .await? } Subcommand::UpdateKeys => { - data = data.build_client().await; + data = data.build_client().await?; commands::smith::update_session_keys( get_keys( args.secret_format, @@ -577,11 +574,11 @@ async fn main() -> Result<(), GcliError> { .unwrap() } Subcommand::RuntimeInfo => { - data = data.build_client().await.fetch_system_properties().await?; + data = data.build_client().await?.fetch_system_properties().await?; commands::runtime::runtime_info(data).await; } Subcommand::CurrentBlock => { - data = data.build_client().await; + data = data.build_client().await?; println!( "current block on {}: {}", data.cfg.duniter_endpoint, @@ -592,32 +589,6 @@ async fn main() -> Result<(), GcliError> { .unwrap() ); } - Subcommand::CheckIndexerAgainstBlockchain => { - data = data - .build_client() - .await - .build_indexer()? - .fetch_genesis_hash() - .await? - .fetch_indexer_genesis_hash() - .await?; - if data.genesis_hash == data.indexer_genesis_hash { - println!( - "{} and {} have the same genesis hash: {}", - data.cfg.duniter_endpoint, - data.indexer().gql_url, - data.genesis_hash - ); - } else { - println!( - "âš ï¸ {} ({}) and {} ({}) do not share same genesis", - data.cfg.duniter_endpoint, - data.genesis_hash, - data.indexer().gql_url, - data.indexer_genesis_hash - ); - } - } Subcommand::Indexer(subcommand) => indexer::handle_command(data, subcommand).await?, Subcommand::Config(subcommand) => conf::handle_command(data, subcommand)?, }