From f3801bc372767f069fad0113347288c3965478b8 Mon Sep 17 00:00:00 2001
From: Hugo Trentesaux <hugo@trentesaux.fr>
Date: Tue, 6 Jun 2023 23:22:48 +0200
Subject: [PATCH] WIP add check when building indexer

---
 src/commands.rs            |  1 +
 src/commands/blockchain.rs | 11 +++++
 src/conf.rs                |  5 +++
 src/data.rs                | 54 +++++++++--------------
 src/indexer.rs             | 31 +++++++++----
 src/main.rs                | 89 +++++++++++++-------------------------
 6 files changed, 89 insertions(+), 102 deletions(-)
 create mode 100644 src/commands/blockchain.rs

diff --git a/src/commands.rs b/src/commands.rs
index 18967d6..ea2a962 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 0000000..626ace7
--- /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 ddedd20..89a4c93 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 f09fb7c..8b28bab 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 fd17815..ab376b8 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 73ea92b..09c643a 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)?,
 	}
-- 
GitLab