| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803 |
- use std::{net::SocketAddr, path::PathBuf, str::FromStr};
- use async_executor::Executor;
- use async_std::sync::{Arc, Mutex};
- use async_trait::async_trait;
- use clap::{IntoApp, Parser};
- use easy_parallel::Parallel;
- use log::{debug, info};
- use rand::rngs::OsRng;
- use serde::{Deserialize, Serialize};
- use serde_json::{json, Value};
- use simplelog::{ColorChoice, TermLogger, TerminalMode};
- use darkfi::{
- blockchain::{rocks::columns, Rocks, RocksColumn},
- crypto::{
- address::Address,
- keypair::{PublicKey, SecretKey},
- proof::VerifyingKey,
- token_id::generate_id2,
- types::DrkTokenId,
- },
- node::{client::Client, state::State},
- rpc::{
- jsonrpc::{error as jsonerr, response as jsonresp, ErrorCode::*, JsonRequest, JsonResult},
- rpcserver::{listen_and_serve, RequestHandler, RpcServerConfig},
- },
- util::{
- cli::{log_config, spawn_config, Config},
- expand_path, join_config_path,
- parse::truncate,
- serial::serialize,
- NetworkName,
- },
- wallet::{cashierdb::CashierDb, walletdb::WalletDb},
- zk::circuit::{MintContract, SpendContract},
- Error, Result,
- };
- use cashierd::service::{bridge, bridge::Bridge};
- #[derive(Clone, Debug, Serialize, Deserialize)]
- pub struct FeatureNetwork {
- /// Network name
- pub name: String,
- /// Blockchain (mainnet/testnet/etc.)
- pub blockchain: String,
- /// Keypair
- pub keypair: String,
- }
- #[derive(Clone, Serialize, Deserialize, Debug)]
- pub struct CashierdConfig {
- /// The DNS name of the cashier (can also be an IP, or a .onion address)
- pub dns_addr: String,
- /// The endpoint where cashierd will bind its RPC socket
- pub rpc_listen_address: SocketAddr,
- /// Whether to listen with TLS or plain TCP
- pub serve_tls: bool,
- /// Path to DER-formatted PKCS#12 archive. (Unused if serve_tls=false)
- pub tls_identity_path: String,
- /// Password for the TLS identity. (Unused if serve_tls=false)
- pub tls_identity_password: String,
- /// The endpoint to a gatewayd protocol API
- pub gateway_protocol_url: String,
- /// The endpoint to a gatewayd publisher API
- pub gateway_publisher_url: String,
- /// Path to cashierd wallet
- pub cashier_wallet_path: String,
- /// Password for cashierd wallet
- pub cashier_wallet_password: String,
- /// Path to client wallet
- pub client_wallet_path: String,
- /// Password for client wallet
- pub client_wallet_password: String,
- /// Path to database
- pub database_path: String,
- /// Geth IPC endpoint
- pub geth_socket: String,
- /// Geth passphrase
- pub geth_passphrase: String,
- /// The configured networks to use
- pub networks: Vec<FeatureNetwork>,
- }
- /// Cashierd cli
- #[derive(Parser)]
- #[clap(name = "cashierd")]
- pub struct CliCashierd {
- /// Sets a custom config file
- #[clap(short, long)]
- pub config: Option<String>,
- /// Get Cashier Public key
- #[clap(short, long)]
- pub address: bool,
- /// Increase verbosity
- #[clap(short, parse(from_occurrences))]
- pub verbose: u8,
- /// Refresh the wallet and slabstore
- #[clap(short, long)]
- pub refresh: bool,
- }
- const CONFIG_FILE_CONTENTS: &[u8] = include_bytes!("../cashierd_config.toml");
- fn handle_bridge_error(error_code: u32) -> Result<()> {
- match error_code {
- 1 => Err(Error::CashierError("Not Supported Client".into())),
- 2 => Err(Error::CashierError("Unable to watch the deposit address".into())),
- 3 => Err(Error::CashierError("Unable to send the token".into())),
- _ => Err(Error::CashierError("Unknown error_code".into())),
- }
- }
- #[derive(Clone, Debug)]
- pub struct Network {
- pub name: NetworkName,
- pub blockchain: String,
- pub keypair: String,
- }
- struct Cashierd {
- bridge: Arc<Bridge>,
- cashier_wallet: Arc<CashierDb>,
- networks: Vec<Network>,
- public_key: Address,
- config: CashierdConfig,
- }
- #[async_trait]
- impl RequestHandler for Cashierd {
- async fn handle_request(&self, req: JsonRequest, executor: Arc<Executor<'_>>) -> JsonResult {
- if req.params.as_array().is_none() {
- return JsonResult::Err(jsonerr(InvalidParams, None, req.id))
- }
- debug!(target: "RPC", "--> {}", serde_json::to_string(&req).unwrap());
- match req.method.as_str() {
- Some("deposit") => return self.deposit(req.id, req.params, executor).await,
- Some("withdraw") => return self.withdraw(req.id, req.params).await,
- Some("features") => return self.features(req.id, req.params).await,
- Some(_) => {}
- None => {}
- };
- return JsonResult::Err(jsonerr(MethodNotFound, None, req.id))
- }
- }
- impl Cashierd {
- async fn new(config: CashierdConfig, public_key: Address) -> Result<Self> {
- debug!(target: "CASHIER DAEMON", "Initialize");
- let wallet_path =
- format!("sqlite://{}", expand_path(&config.cashier_wallet_path)?.to_str().unwrap());
- let cashier_wallet = CashierDb::new(&wallet_path, &config.cashier_wallet_password).await?;
- let mut networks = Vec::new();
- for network in config.clone().networks {
- networks.push(Network {
- name: NetworkName::from_str(&network.name)?,
- blockchain: network.blockchain,
- keypair: network.keypair,
- });
- }
- let bridge = bridge::Bridge::new();
- Ok(Self { bridge, cashier_wallet, networks, public_key, config })
- }
- async fn start(
- &mut self,
- mut client: Client,
- state: Arc<Mutex<State>>,
- executor: Arc<Executor<'_>>,
- ) -> Result<(smol::Task<Result<()>>, smol::Task<Result<()>>)> {
- self.cashier_wallet.init_db().await?;
- for network in self.networks.iter() {
- match network.name {
- #[cfg(feature = "sol")]
- NetworkName::Solana => {
- debug!(target: "CASHIER DAEMON", "Adding solana network");
- use cashierd::service::SolClient;
- let _bridge = self.bridge.clone();
- let sol_client = SolClient::new(
- self.cashier_wallet.clone(),
- &network.blockchain,
- &network.keypair,
- )
- .await?;
- _bridge.add_clients(NetworkName::Solana, sol_client).await?;
- }
- #[cfg(feature = "eth")]
- NetworkName::Ethereum => {
- debug!(target: "CASHIER DAEMON", "Adding ethereum network");
- use cashierd::service::EthClient;
- let _bridge = self.bridge.clone();
- let passphrase = self.config.geth_passphrase.clone();
- let mut eth_client = EthClient::new(
- &network.blockchain,
- expand_path(&self.config.geth_socket)?.to_str().unwrap(),
- &passphrase,
- );
- eth_client.setup_keypair(self.cashier_wallet.clone(), &network.keypair).await?;
- _bridge.add_clients(NetworkName::Ethereum, Arc::new(eth_client)).await?;
- }
- #[cfg(feature = "btc")]
- NetworkName::Bitcoin => {
- debug!(target: "CASHIER DAEMON", "Adding bitcoin network");
- use cashierd::service::btc::BtcClient;
- let _bridge = self.bridge.clone();
- let btc_client = BtcClient::new(
- self.cashier_wallet.clone(),
- &network.blockchain,
- &network.keypair,
- )
- .await?;
- _bridge.add_clients(NetworkName::Bitcoin, btc_client).await?;
- }
- _ => {}
- }
- }
- client.start().await?;
- let (notify, recv_coin) = async_channel::unbounded::<(PublicKey, u64)>();
- client
- .connect_to_subscriber_from_cashier(
- state.clone(),
- self.cashier_wallet.clone(),
- notify.clone(),
- executor.clone(),
- )
- .await?;
- let cashier_wallet = self.cashier_wallet.clone();
- let bridge = self.bridge.clone();
- let ex = executor.clone();
- let listen_for_receiving_coins_task: smol::Task<Result<()>> = executor.spawn(async move {
- let ex2 = ex.clone();
- loop {
- Self::listen_for_receiving_coins(
- bridge.clone(),
- cashier_wallet.clone(),
- recv_coin.clone(),
- ex2.clone(),
- )
- .await?;
- }
- });
- let bridge2 = self.bridge.clone();
- let listen_for_notification_from_bridge_task: smol::Task<Result<()>> =
- executor.spawn(async move {
- while let Some(token_notification) = bridge2.clone().listen().await {
- debug!(target: "CASHIER DAEMON", "Received notification from bridge");
- let token_notification = token_notification?;
- let received_balance = truncate(
- token_notification.received_balance,
- 8,
- token_notification.decimals,
- )?;
- client
- .send(
- token_notification.drk_pub_key,
- received_balance,
- token_notification.token_id,
- true,
- state.clone(),
- )
- .await?;
- }
- Ok(())
- });
- Ok((listen_for_receiving_coins_task, listen_for_notification_from_bridge_task))
- }
- async fn listen_for_receiving_coins(
- bridge: Arc<Bridge>,
- cashier_wallet: Arc<CashierDb>,
- recv_coin: async_channel::Receiver<(PublicKey, u64)>,
- executor: Arc<Executor<'_>>,
- ) -> Result<()> {
- // received drk coin
- let (drk_pub_key, amount) = recv_coin.recv().await?;
- debug!(target: "CASHIER DAEMON", "Receive coin with amount: {}", amount);
- // get public key, and token_id of the token
- let token =
- cashier_wallet.get_withdraw_token_public_key_by_dkey_public(&drk_pub_key).await?;
- // send a request to bridge to send equivalent amount of
- // received drk coin to token publickey
- if let Some(withdraw_token) = token {
- let bridge_subscribtion = bridge
- .subscribe(drk_pub_key, Some(withdraw_token.mint_address), executor.clone())
- .await;
- // send a request to the bridge to send amount of token
- // equivalent to the received drk
- bridge_subscribtion
- .sender
- .send(bridge::BridgeRequests {
- network: withdraw_token.network.clone(),
- payload: bridge::BridgeRequestsPayload::Send(
- withdraw_token.token_public_key.clone(),
- amount,
- ),
- })
- .await?;
- // receive a response
- let res = bridge_subscribtion.receiver.recv().await?;
- // check the response's error
- let error_code = res.error as u32;
- if error_code != 0 {
- return handle_bridge_error(error_code)
- }
- match res.payload {
- bridge::BridgeResponsePayload::Send => {
- cashier_wallet
- .confirm_withdraw_key_record(
- &withdraw_token.token_public_key,
- &withdraw_token.network,
- )
- .await?;
- }
- _ => {
- return Err(Error::CashierError(
- "Receive unknown value from Subscription".into(),
- ))
- }
- }
- }
- Ok(())
- }
- fn check_token_id(network: &NetworkName, _token_id: &str) -> Result<Option<String>> {
- match network {
- #[cfg(feature = "sol")]
- NetworkName::Solana => {
- use cashierd::service::sol::SOL_NATIVE_TOKEN_ID;
- if _token_id != SOL_NATIVE_TOKEN_ID {
- return Ok(Some(_token_id.to_string()))
- }
- Ok(None)
- }
- #[cfg(feature = "eth")]
- NetworkName::Ethereum => {
- use cashierd::service::eth::ETH_NATIVE_TOKEN_ID;
- if _token_id != ETH_NATIVE_TOKEN_ID {
- return Ok(Some(_token_id.to_string()))
- }
- Ok(None)
- }
- #[cfg(feature = "btc")]
- NetworkName::Bitcoin => Ok(None),
- _ => Err(Error::NotSupportedNetwork),
- }
- }
- // RPCAPI:
- // Executes a deposit request given `network` and `token_id`.
- // Returns the address where the deposit shall be transferred to.
- // --> {"jsonrpc": "2.0", "method": "deposit", "params": ["network", "token", "publickey"], "id": 1}
- // <-- {"jsonrpc": "2.0", "result": "Ht5G1RhkcKnpLVLMhqJc5aqZ4wYUEbxbtZwGCVbgU7DL", "id": 1}
- async fn deposit(&self, id: Value, params: Value, executor: Arc<Executor<'_>>) -> JsonResult {
- info!(target: "CASHIER DAEMON", "Received deposit request");
- let args: &Vec<serde_json::Value> = params.as_array().unwrap();
- if args.len() != 3 {
- return JsonResult::Err(jsonerr(InvalidParams, None, id))
- }
- let network: NetworkName;
- let mut mint_address: &str;
- let drk_pub_key: &str;
- match (args[0].as_str(), args[1].as_str(), args[2].as_str()) {
- (Some(n), Some(m), Some(d)) => {
- if NetworkName::from_str(n).is_err() {
- return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id))
- }
- network = NetworkName::from_str(n).unwrap();
- mint_address = m;
- drk_pub_key = d;
- }
- (None, _, _) => return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id)),
- (_, None, _) => return JsonResult::Err(jsonerr(InvalidTokenIdParam, None, id)),
- (_, _, None) => return JsonResult::Err(jsonerr(InvalidAddressParam, None, id)),
- }
- // Check if the features list contains this network
- if !self.networks.iter().any(|net| net.name == network) {
- return JsonResult::Err(jsonerr(
- InvalidParams,
- Some(format!("Cashier doesn't support this network: {}", network)),
- id,
- ))
- }
- let result: Result<String> = async {
- let token_id = generate_id2(mint_address, &network)?;
- let mint_address_opt = Self::check_token_id(&network, mint_address)?;
- if mint_address_opt.is_none() {
- mint_address = "";
- }
- let drk_pub_key = Address::from_str(drk_pub_key)?;
- let drk_pub_key: PublicKey = PublicKey::try_from(drk_pub_key)?;
- // check if the drk public key already exist
- let check = self
- .cashier_wallet
- .get_deposit_token_keys_by_dkey_public(&drk_pub_key, &network)
- .await?;
- // start new subscription from the bridge and then cashierd will
- // send a request to the bridge to generate keypair for the desired token
- // and start watch this token's keypair
- // once a bridge receive an update for this token's address
- // cashierd will get notification from bridge.listen() function
- //
- // The "if statement" check from the cashierdb if the node's drk_pub_key already exist
- // in this case it will not generate new keypair but it will
- // retrieve the old generated keypair
- //
- // Once receive a response from the bridge, the cashierd then save a deposit
- // record in cashierdb with the network name and token id
- let bridge = self.bridge.clone();
- let bridge_subscribtion =
- bridge.subscribe(drk_pub_key, mint_address_opt, executor).await;
- if check.is_empty() {
- bridge_subscribtion
- .sender
- .send(bridge::BridgeRequests {
- network: network.clone(),
- payload: bridge::BridgeRequestsPayload::Watch(None),
- })
- .await?;
- } else {
- let keypair = check[0].clone();
- bridge_subscribtion
- .sender
- .send(bridge::BridgeRequests {
- network: network.clone(),
- payload: bridge::BridgeRequestsPayload::Watch(Some(keypair)),
- })
- .await?;
- }
- let bridge_res = bridge_subscribtion.receiver.recv().await?;
- let error_code = bridge_res.error as u32;
- if error_code != 0 {
- return handle_bridge_error(error_code).map(|_| String::new())
- }
- match bridge_res.payload {
- bridge::BridgeResponsePayload::Watch(token_key) => {
- // add pairings to db
- self.cashier_wallet
- .put_deposit_keys(
- &drk_pub_key,
- &token_key.private_key,
- &serialize(&token_key.public_key),
- &network,
- &token_id,
- mint_address.into(),
- )
- .await?;
- Ok(token_key.public_key)
- }
- bridge::BridgeResponsePayload::Address(token_pub) => Ok(token_pub),
- _ => Err(Error::CashierError("Receive unknown value from Subscription".into())),
- }
- }
- .await;
- match result {
- Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(id))),
- Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
- }
- }
- // RPCAPI:
- // Executes a withdraw request given `network`, `token_id`, `publickey`
- // and `amount`. `publickey` is supposed to correspond to `network`.
- // Returns the transaction ID of the processed withdraw.
- // --> {"jsonrpc": "2.0", "method": "withdraw", "params": ["network", "token", "publickey", "amount"], "id": 1}
- // <-- {"jsonrpc": "2.0", "result": "txID", "id": 1}
- async fn withdraw(&self, id: Value, params: Value) -> JsonResult {
- info!(target: "CASHIER DAEMON", "Received withdraw request");
- let args: &Vec<serde_json::Value> = params.as_array().unwrap();
- if args.len() != 4 {
- return JsonResult::Err(jsonerr(InvalidParams, None, id))
- }
- let network: NetworkName;
- let mut mint_address: &str;
- let address: &str;
- match (args[0].as_str(), args[1].as_str(), args[2].as_str()) {
- (Some(n), Some(m), Some(a)) => {
- if NetworkName::from_str(n).is_err() {
- return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id))
- }
- network = NetworkName::from_str(n).unwrap();
- mint_address = m;
- address = a;
- }
- (None, _, _) => return JsonResult::Err(jsonerr(InvalidNetworkParam, None, id)),
- (_, None, _) => return JsonResult::Err(jsonerr(InvalidTokenIdParam, None, id)),
- (_, _, None) => return JsonResult::Err(jsonerr(InvalidAddressParam, None, id)),
- }
- // Check if the features list contains this network
- if !self.networks.iter().any(|net| net.name == network) {
- return JsonResult::Err(jsonerr(
- InvalidParams,
- Some(format!("Cashier doesn't support this network: {}", network)),
- id,
- ))
- }
- let result: Result<String> = async {
- let token_id: DrkTokenId = generate_id2(mint_address, &network)?;
- let mint_address_opt = Self::check_token_id(&network, mint_address)?;
- if mint_address_opt.is_none() {
- // empty string
- mint_address = "";
- }
- let address = serialize(&address.to_string());
- let cashier_public: PublicKey;
- if let Some(addr) = self
- .cashier_wallet
- .get_withdraw_keys_by_token_public_key(&address, &network)
- .await?
- {
- cashier_public = addr.public;
- } else {
- let cashier_secret = SecretKey::random(&mut OsRng);
- cashier_public = PublicKey::from_secret(cashier_secret);
- self.cashier_wallet
- .put_withdraw_keys(
- &address,
- &cashier_public,
- &cashier_secret,
- &network,
- &token_id,
- mint_address.into(),
- )
- .await?;
- }
- let cashier_public_str = Address::from(cashier_public).to_string();
- Ok(cashier_public_str)
- }
- .await;
- match result {
- Ok(res) => JsonResult::Resp(jsonresp(json!(res), json!(id))),
- Err(err) => JsonResult::Err(jsonerr(InternalError, Some(err.to_string()), json!(id))),
- }
- }
- // RPCAPI:
- // Returns supported cashier features, like network, listening ports, etc.
- // --> {"jsonrpc": "2.0", "method": "features", "params": [], "id": 1}
- // <-- {"jsonrpc": "2.0", "result": {"network": ["btc", "sol"]}, "id": 1}
- async fn features(&self, id: Value, _params: Value) -> JsonResult {
- let tcp_port: Option<u16>;
- let tls_port: Option<u16>;
- let onionaddr: Option<String>;
- let dnsaddr: Option<String>;
- if self.config.serve_tls {
- tls_port = Some(self.config.rpc_listen_address.port());
- tcp_port = None;
- } else {
- tcp_port = Some(self.config.rpc_listen_address.port());
- tls_port = None;
- }
- if self.config.dns_addr.ends_with(".onion") {
- onionaddr = Some(self.config.dns_addr.clone());
- dnsaddr = None;
- } else {
- dnsaddr = Some(self.config.dns_addr.clone());
- onionaddr = None;
- }
- let mut resp: serde_json::Value = json!(
- {
- "server_version": env!("CARGO_PKG_VERSION"),
- "protocol_version": "1.0",
- "public_key": self.public_key.to_string(),
- "networks": [],
- "hosts": {
- "tcp_port": tcp_port,
- "tls_port": tls_port,
- "onion_addr": onionaddr,
- "dns_addr": dnsaddr,
- }
- }
- );
- for network in self.networks.iter() {
- resp.as_object_mut().unwrap()["networks"].as_array_mut().unwrap().push(json!(
- {
- network.name.to_string().to_lowercase():
- {"chain": network.blockchain.to_lowercase()}
- }
- ));
- }
- JsonResult::Resp(jsonresp(resp, id))
- }
- }
- async fn start(
- executor: Arc<Executor<'_>>,
- config: &CashierdConfig,
- get_address_flag: bool,
- ) -> Result<()> {
- let client_wallet_path =
- format!("sqlite://{}", expand_path(&config.client_wallet_path)?.to_str().unwrap());
- let client_wallet = WalletDb::new(&client_wallet_path, &config.client_wallet_password).await?;
- let rocks = Rocks::new(expand_path(&config.database_path.clone())?.as_path())?;
- info!("Building verifying key for the mint contract...");
- let mint_vk = VerifyingKey::build(11, &MintContract::default());
- info!("Building verifying key for the spend contract...");
- let spend_vk = VerifyingKey::build(11, &SpendContract::default());
- // new Client
- let gateway_urls =
- (config.gateway_protocol_url.parse()?, config.gateway_publisher_url.parse()?);
- let client = Client::new(rocks.clone(), gateway_urls, client_wallet.clone()).await?;
- let tree = client.get_tree().await?;
- let merkle_roots = RocksColumn::<columns::MerkleRoots>::new(rocks.clone());
- let nullifiers = RocksColumn::<columns::Nullifiers>::new(rocks);
- // get cashier public key
- let cashier_public = client.main_keypair.public;
- // new Cashier daemon
- let mut cashierd = Cashierd::new(config.clone(), Address::from(cashier_public)).await?;
- // this will print the cashier public key and exit
- if get_address_flag {
- info!("Public Key: {}", cashierd.public_key);
- return Ok(())
- };
- // new State
- let public_keys = vec![cashier_public];
- let state = Arc::new(Mutex::new(State {
- tree,
- merkle_roots,
- nullifiers,
- public_keys,
- mint_vk,
- spend_vk,
- }));
- // start cashier
- let (t1, t2) = cashierd.start(client, state, executor.clone()).await?;
- // config for rpc
- let cfg = RpcServerConfig {
- socket_addr: config.rpc_listen_address,
- use_tls: config.serve_tls,
- identity_path: expand_path(&config.clone().tls_identity_path)?,
- identity_pass: config.tls_identity_password.clone(),
- };
- // listen and serve RPC
- listen_and_serve(cfg, Arc::new(cashierd), executor).await?;
- t1.cancel().await;
- t2.cancel().await;
- Ok(())
- }
- #[async_std::main]
- async fn main() -> Result<()> {
- let args = CliCashierd::parse();
- let matches = CliCashierd::command().get_matches();
- let config_path = if args.config.is_some() {
- expand_path(&args.config.unwrap())?
- } else {
- join_config_path(&PathBuf::from("cashierd.toml"))?
- };
- // Spawn config file if it's not in place already.
- spawn_config(&config_path, CONFIG_FILE_CONTENTS)?;
- let verbosity_level = matches.occurrences_of("verbose");
- let (lvl, conf) = log_config(verbosity_level)?;
- TermLogger::init(lvl, conf, TerminalMode::Mixed, ColorChoice::Auto)?;
- let config: CashierdConfig = Config::<CashierdConfig>::load(config_path)?;
- if args.refresh {
- info!(target: "CASHIER DAEMON", "Refresh the wallet and the database");
- // refresh cashier's client wallet
- let client_wallet_path =
- format!("sqlite://{}", expand_path(&config.client_wallet_path)?.to_str().unwrap());
- let client_wallet =
- WalletDb::new(&client_wallet_path, &config.client_wallet_password).await?;
- client_wallet.remove_own_coins().await?;
- // refresh cashier wallet
- let wallet_path =
- format!("sqlite://{}", expand_path(&config.cashier_wallet_path)?.to_str().unwrap());
- let wallet = CashierDb::new(&wallet_path, &config.cashier_wallet_password).await?;
- wallet.remove_withdraw_and_deposit_keys().await?;
- // refresh rocks database
- if let Some(path) = expand_path(&config.database_path)?.to_str() {
- info!(target: "CASHIER DAEMON", "Remove database: {}", path);
- std::fs::remove_dir_all(path)?;
- }
- info!("Wallet updated successfully.");
- return Ok(())
- }
- let get_address_flag = args.address;
- let ex = Arc::new(Executor::new());
- let (signal, shutdown) = async_channel::unbounded::<()>();
- let ex2 = ex.clone();
- let nthreads = num_cpus::get();
- debug!(target: "CASHIER DAEMON", "Run {} executor threads", nthreads);
- let (_, result) = Parallel::new()
- .each(0..nthreads, |_| smol::future::block_on(ex.run(shutdown.recv())))
- // Run the main future on the current thread.
- .finish(|| {
- smol::future::block_on(async move {
- start(ex2, &config, get_address_flag).await?;
- drop(signal);
- Ok::<(), darkfi::Error>(())
- })
- });
- result
- }
|