Răsfoiți Sursa

explorer: Support CLI args for RPC and DB path

x 6 luni în urmă
părinte
comite
f1b5e5aeb9
3 a modificat fișierele cu 77 adăugiri și 15 ștergeri
  1. 1 0
      Cargo.lock
  2. 2 0
      bin/explorer/Cargo.toml
  3. 74 15
      bin/explorer/src/main.rs

+ 1 - 0
Cargo.lock

@@ -2834,6 +2834,7 @@ dependencies = [
 name = "explorer"
 version = "0.5.0"
 dependencies = [
+ "arg",
  "async-channel 2.5.0",
  "async-trait",
  "bytemuck",

+ 2 - 0
bin/explorer/Cargo.toml

@@ -22,3 +22,5 @@ hex = "0.4.3"
 sled = "0.34.7"
 tapes = {git = "https://github.com/Cuprate/Tapes"}
 bytemuck = {version = "1.24.0", features = ["derive"]}
+
+arg = {git = "https://github.com/parazyd/arg"}

+ 74 - 15
bin/explorer/src/main.rs

@@ -18,13 +18,15 @@
 
 use std::{
     collections::HashSet,
-    path::Path,
+    path::{Path, PathBuf},
+    process::exit,
     sync::{
         atomic::{AtomicBool, Ordering},
         Arc,
     },
 };
 
+use arg::Args;
 use async_trait::async_trait;
 use darkfi::{
     blockchain::BlockInfo,
@@ -35,8 +37,8 @@ use darkfi::{
         settings::RpcSettings,
     },
     system::{CondVar, Publisher, PublisherPtr, StoppableTask, StoppableTaskPtr},
-    util::encoding::base64,
-    Error, Result,
+    util::{encoding::base64, path::expand_path},
+    Error, Result, ANSI_LOGO,
 };
 use darkfi_serial::deserialize_async;
 use smol::{
@@ -47,6 +49,7 @@ use smol::{
 use tapes::{TapeOpenOptions, Tapes};
 use tinyjson::JsonValue;
 use tracing::{debug, info};
+use url::Url;
 
 /// Database interfaces
 mod db;
@@ -54,7 +57,21 @@ use db::{DifficultyIndex, TapesDatabase};
 /// JSON-RPC server methods
 mod rpc;
 
-const RPC_ENDPOINT: &str = "tcp://127.0.0.1:18345";
+const ABOUT: &str =
+    concat!("explorer ", env!("CARGO_PKG_VERSION"), '\n', env!("CARGO_PKG_DESCRIPTION"));
+
+const USAGE: &str = r#"
+Usage: explorer [OPTIONS]
+
+Options:
+  -e <endpoint>  darkfid JSON-RPC endpoint (default: tcp://127.0.0.1:18345)
+  -d <path>      Path to database (default: ~/.local/share/darkfi/explorer/db)
+  -h             Show this help
+"#;
+
+fn usage() {
+    print!("{ANSI_LOGO}{ABOUT}\n{USAGE}");
+}
 
 pub struct Explorer {
     synced: AtomicBool,
@@ -130,7 +147,7 @@ impl Explorer {
         })
     }
 
-    async fn handle_block_sub(&self, ex: Arc<Executor<'_>>) -> Result<()> {
+    async fn handle_block_sub(&self, rpc_endpoint: Url, ex: Arc<Executor<'_>>) -> Result<()> {
         info!("Started handle_block_sub(), waiting until blockchain is synced");
         let block_subscription = self.blocks_publisher.clone().subscribe().await;
         self.synced_notifier.wait();
@@ -169,7 +186,7 @@ impl Explorer {
 
                     // Get difficulty
                     let rpc_client =
-                        RpcClient::new(RPC_ENDPOINT.parse().unwrap(), ex.clone()).await.unwrap();
+                        RpcClient::new(rpc_endpoint.clone(), ex.clone()).await.unwrap();
 
                     let req = JsonRequest::new(
                         "blockchain.get_difficulty",
@@ -194,6 +211,7 @@ impl Explorer {
 
     async fn sync_blockchain(
         &self,
+        rpc_endpoint: Url,
         from_height: u64,
         to_height: u64,
         ex: Arc<Executor<'_>>,
@@ -204,7 +222,7 @@ impl Explorer {
         }
 
         info!("sync_blockchain started from_height={from_height} to_height={to_height}...");
-        let rpc_client = Arc::new(RpcClient::new(RPC_ENDPOINT.parse().unwrap(), ex.clone()).await?);
+        let rpc_client = Arc::new(RpcClient::new(rpc_endpoint, ex.clone()).await?);
 
         for height in from_height..=to_height {
             info!("Requesting block at height {height}");
@@ -240,11 +258,11 @@ impl Explorer {
     }
 }
 
-async fn realmain(ex: Arc<Executor<'static>>) -> Result<()> {
+async fn realmain(rpc_endpoint: Url, db_path: PathBuf, ex: Arc<Executor<'static>>) -> Result<()> {
     let explorer = Arc::new(Explorer::new(
-        Path::new("db/sled_db"),
-        Path::new("db/tapes_metadata"),
-        Path::new("db/tapes"),
+        &db_path.join("sled_db"),
+        &db_path.join("tapes_metadata"),
+        &db_path.join("tapes"),
     )?);
 
     // First we should subscribe to new blocks and queue them to apply
@@ -254,8 +272,9 @@ async fn realmain(ex: Arc<Executor<'static>>) -> Result<()> {
     let explorer_ = Arc::clone(&explorer);
     let explorer__ = Arc::clone(&explorer);
     let ex_ = ex.clone();
+    let rpc_endpoint_ = rpc_endpoint.clone();
     explorer.rpc_sub_handler.clone().start(
-        async move { explorer_.handle_block_sub(ex_).await },
+        async move { explorer_.handle_block_sub(rpc_endpoint_, ex_).await },
         |res| async move {
             match res {
                 Ok(()) | Err(Error::DetachedTaskStopped) | Err(Error::RpcServerStopped) => {}
@@ -278,7 +297,7 @@ async fn realmain(ex: Arc<Executor<'static>>) -> Result<()> {
     // Then we subscribe to darkfid's RPC to get new blocks. We should first
     // fetch the current height, so we know how far to sync. Then any blocks
     // that come after that should be queued in the `blocks_publisher`.
-    let rpc_client = Arc::new(RpcClient::new(RPC_ENDPOINT.parse().unwrap(), ex.clone()).await?);
+    let rpc_client = Arc::new(RpcClient::new(rpc_endpoint.clone(), ex.clone()).await?);
 
     let req = JsonRequest::new("blockchain.last_confirmed_block", JsonValue::Array(vec![]));
     let rep = rpc_client.request(req).await?;
@@ -319,7 +338,9 @@ async fn realmain(ex: Arc<Executor<'static>>) -> Result<()> {
     // is going to request and parse all the necessary blocks, and then
     // apply them to the databases.
     let sync_from = explorer.get_height()?.unwrap_or(0);
-    explorer.sync_blockchain(sync_from, last_confirmed_height, ex.clone()).await?;
+    explorer
+        .sync_blockchain(rpc_endpoint.clone(), sync_from, last_confirmed_height, ex.clone())
+        .await?;
     explorer.synced.store(true, Ordering::SeqCst);
     explorer.synced_notifier.notify();
 
@@ -333,6 +354,44 @@ async fn realmain(ex: Arc<Executor<'static>>) -> Result<()> {
 }
 
 fn main() -> Result<()> {
+    let argv;
+    let mut hflag = false;
+    let mut evalue = "tcp://127.0.0.1:18345".to_string();
+    let mut dvalue = "~/.local/share/darkfi/explorer/db".to_string();
+
+    {
+        let mut args = Args::new().with_cb(|args, flag| match flag {
+            'e' => evalue = args.eargf().to_string(),
+            'd' => dvalue = args.eargf().to_string(),
+            _ => hflag = true,
+        });
+
+        argv = args.parse();
+    }
+
+    if hflag || argv.is_empty() {
+        usage();
+        exit(1);
+    }
+
+    let rpc_endpoint: Url = match evalue.parse() {
+        Ok(v) => v,
+        Err(e) => {
+            println!("Error parsing RPC endpoint: {e}");
+            usage();
+            exit(1);
+        }
+    };
+
+    let db_path: PathBuf = match expand_path(&dvalue) {
+        Ok(v) => v,
+        Err(e) => {
+            println!("Error parsing DB path: {e}");
+            usage();
+            exit(1);
+        }
+    };
+
     let ex = Arc::new(Executor::new());
     let (signal, shutdown) = async_channel::unbounded::<()>();
 
@@ -344,7 +403,7 @@ fn main() -> Result<()> {
         // Run the main future on the current thread
         .finish(|| {
             future::block_on(async {
-                realmain(ex.clone()).await?;
+                realmain(rpc_endpoint, db_path, ex.clone()).await?;
                 drop(signal);
                 Ok::<(), darkfi::Error>(())
             })